Developing Large Web Applications- P14

Chia sẻ: Cong Thanh | Ngày: | Loại File: PDF | Số trang:10

0
40
lượt xem
4
download

Developing Large Web Applications- P14

Mô tả tài liệu
  Download Vui lòng tải xuống để xem tài liệu đầy đủ

Developing Large Web Applications- P14:This book presents a number of techniques for applying established practices of good software engineering to web development—that is, development primarily using the disparate technologies of HTML, CSS, JavaScript, and server-side scripting languages. Whereas there are many books on how to use languages, how to use libraries, and how to approach software engineering, this is the first book to codify many of the techniques it presents. These techniques will make the components of your own web applications more reusable, maintainable, and reliable....

Chủ đề:
Lưu

Nội dung Text: Developing Large Web Applications- P14

  1. this.disabled = false; } } MVC.MultiSelectView.prototype = new MVC.View(); MVC.MultiSelectView.prototype.attach = function(m, i) { // This method hooks up a view to its data source, which is a model. MVC.View.prototype.attach.call(this, m, i); // If the view has no predecessor view, it must be first in the chain. if (!this.prev) this.model.firstModel = true; this.container = document.getElementById(this.id); } MVC.MultiSelectView.prototype.update = function() { // Called when a change in the model takes place. Render new options. var select = this.getSelect(); // Remove any existing select element not created by the view. if (select && !YAHOO.util.Dom.hasClass(select, "mltsel")) { select.parentNode.removeChild(select); select = null; } // Insert a new select only the first time the view is being managed. if (!select) { select = document.createElement("select"); YAHOO.util.Dom.addClass(select, "mltsel"); select.setAttribute("name", this.name); YAHOO.util.Event.addListener ( select, "change", this.changeHandler, this, true ); // Insert the select element for the selection list into the DOM. if (this.container) this.container.appendChild(select); } if (this.disabled) select.disabled = true; else select.disabled = false; An Example: Chained Selection Lists | 111
  2. var o; var options; var count; // Start the options with the model's label for the selection list. select.options.length = 0; o = new Option(this.model.labelText, this.model.labelValue); select.options[select.options.length] = o; options = this.model.state.options; count = options.length; // Load the rest of the selection list remaining with the options. for (var i = 0; i < count; i++) { o = new Option(options[i].text, options[i].value); select.options[select.options.length] = o; } } MVC.MultiSelectView.prototype.changeHandler = function(e) { // Handle changes in one of the selection lists by adjusting others. var select = this.getSelect(); var option = select.options[select.selectedIndex].value; if (option == "") { // The selection list has been set back to its initial state; // selection lists beyond it in the chain must be reset as well. this.reset(this.next); } else { if (this.next) { // Use Ajax to get options for the next selection in the chain. if (this.next.model.proc.indexOf("?") == -1) option = "?value=" + option; else option = "&value=" + option; this.next.model.setState("GET", this.next.model.proc + option); this.next.enable(); // Move to the next selection list in the chain and reset all // views beyond it (when a choice has been made out of order). var iter = this.next; if (iter) this.reset(iter.next); } } } 112 | Chapter 5: Large-Scale JavaScript
  3. MVC.MultiSelectView.prototype.reset = function(view) { // Initialize all selection lists after the given one in the chain. var iter = view; while (iter) { iter.model.init(); iter.disable(); iter = iter.next; } } MVC.MultiSelectView.prototype.enable = function() { var select = this.getSelect(); this.disabled = false; if (select) select.disabled = this.disabled; } MVC.MultiSelectView.prototype.disable = function() { var select = this.getSelect(); this.disabled = true; if (select) select.disabled = this.disabled; } MVC.MultiSelectView.prototype.getSelect = function() { var elements; // Retrieve the current select element used by the selection list. if (this.container) elements = this.container.getElementsByTagName("select"); else return null; if (elements.length > 0) return elements[0]; else return null; } An Example: Chained Selection Lists | 113
  4. A logical extension of this implementation for chained selection lists is to use it to build highly reusable modules for different types of chained selection lists you might need to support around a large web application. For example, you could build a New Cars Selection module for anywhere you need a make-model-trim 3-tuple for new cars. This module would bundle the generic chaining behavior presented in the preceding exam- ples with the HTML and CSS to make it a fully reusable component. We’ll learn more about this encapsulation for modules in Chapter 7. 114 | Chapter 5: Large-Scale JavaScript
  5. CHAPTER 6 Data Management As you examine the design for a web page, it’s important to distinguish between data on the page that is dynamic and data that is static. Dynamic data, such as a list of search results, changes each time the page is loaded (based on the query); static data, such as a label for the query box, does not. The distinction between static and dynamic data is important because each requires its own management strategy. On the one hand, static data is easy—you simply specify it directly within the HTML of the page. With dynamic data, however, you must enlist the help of a server-side scripting language, such as PHP, so that you can interact with backend systems to store and retrieve the data. In this chapter, we look at techniques for managing dynamic data. One of the most important goals for managing dynamic data in a large web application is to establish a clearly defined data interface through which to interact with the back- end. A clearly defined data interface allows modules in the user interface (see Chap- ter 7) to remain loosely coupled with the backend, allows details of the backend (e.g., data dependencies) to be abstracted from modules, and gives modules the flexibility to work with any set of data that contains what the data interface requires. In teams where web developers and backend engineers are separate roles, these qualities let each role work independently, knowing that both are working toward a common point where the user interface and backend will meet. This goal for managing dynamic data is cap- tured in the following tenet from Chapter 1: Tenet 6: Dynamic data exchanged between the user interface and the backend is managed through a clearly defined data interface. Pages define a single point for loading data and a single point for saving it. We begin this chapter by looking at what we mean by a dynamic module. We then discuss the concept of a data manager, look at important techniques for using data managers to store and retrieve dynamic data, and examine methods for making data managers extensible using inheritance and aggregation. Next, we look at some exam- ples of data managers using SQL and XML, and explore some techniques for working with database connections, accessing time-consuming web services in parallel, and 115
  6. working with JSON, which is particularly useful for Ajax applications. Finally, we look at a few things to keep in mind when working with dynamic data in cookies and forms. Dynamic Modules Let’s reconsider the New Car Reviews module from Example 3-3, which contains a list of three new car reviews. That example illustrates well-constructed HTML for the module, but it doesn’t address how the HTML was generated on the server or which parts of that module are dynamic versus static. Exploring that module again, it’s rea- sonable to expect that the list of reviews should be generated dynamically so that we can insert whichever reviews are relevant wherever the module is used. An associative array is a good data structure for organizing dynamic data. The list of reviews might be structured as shown in the PHP code in Example 6-1. Example 6-1. An associative array for dynamically generated new car reviews array ( "0" => array ( "name" => "2009 Honda Accord", "price" => "21905", "link" => "http://.../reviews/00001/" ), "1" => array ( "name" => "2009 Toyota Prius", "price" => "22000", "link" => "http://.../reviews/00002/" ), "2" => array ( "name" => "2009 Nissan Altima", "price" => "19900", "link" => "http://.../reviews/00003/" ) ) Example 6-2 shows a method that uses the data structure of Example 6-1 to generate the HTML for the list items in the New Car Reviews module (Chapter 7 presents a complete class for implementing a module in PHP, which might employ a method like this). This method takes the array of new car reviews as an argument. Example 6-2. A method for generating list items for new car reviews dynamically protected function get_reviews($reviews) { $count = count($reviews); $items = ""; 116 | Chapter 6: Data Management
  7. for ($i = 0; $i < $count; $i++) { $pos = ($i == 0) ? "beg" : (($i == $count - 1) ? "end" : "mid"); $price = "&#36;".number_format($reviews[$i]["price"]); $items .=
  8. public function load_data() { // Set up load_args for each of the data managers called below. ... $dm = new NewCarListingsDataManager(); $dm->get_data ( $this->load_args["new_car_listings"], $this->load_data["new_car_listings"], $this->load_stat["new_car_listings"] ); $dm = new NewCarReviewsDataManager(); $dm->get_data ( $this->load_args["new_car_reviews"], $this->load_data["new_car_reviews"], $this->load_stat["new_car_reviews"] ); ... } ... } Notice the use of new_car_listings and new_car_reviews members (named after the data managers themselves) for each argument of the get_data calls. These ensure that the arguments, data, and status for each data manager are uniquely identifiable. All you need to know right now about get_data is that the $load_args argument is the input (allowing you to control the method’s operation), the $load_data argument is the main output, and the $load_stat argument is additional output that you can use in case something goes wrong. After get_data returns, the $load_data member of the page class contains the data retrieved by each data manager, with the data for each module placed within its own area of the data structure. Example 6-4 shows an example of this data structure. Example 6-4. The $load_data member of the page class after calling load_data array ( "new_car_listings" => array ( // Data retrieved by the New Car Listings data manager is here. ... ), "new_car_reviews" => array ( // Data retrieved by the New Car Reviews data manager is here. 118 | Chapter 6: Data Management
  9. "0" => array ( "name" => "2009 Honda Accord", "price" => "21905", "link" => "http://.../reviews/00001/" ), "1" => array ( "name" => "2009 Toyota Prius", "price" => "22000", "link" => "http://.../reviews/00002/" ), "2" => array ( "name" => "2009 Nissan Altima", "price" => "19900", "link" => "http://.../reviews/00003/" ) ) ) Anytime you need to set some data in the backend managed by a data manager, you simply instantiate the data manager and call its set_data method. Example 6-5 illus- trates the use of a data manager to set data in the backend within the kind of PHP class for pages that we’ll develop in Chapter 7. The save_data method defines a single point at which to save data for a page. As in Example 6-3, notice the use of the new_car_queries member for each argument of set_data to ensure the arguments, data, and status for this data manager are uniquely identifiable. Example 6-5. Saving data for a page using a data manager class NewCarSearchResultsPage extends SitePage { ... public function save_data() { // Set up save_args and save_data for each data manager called below. ... $dm = new NewCarQueriesDataManager(); $dm->set_data ( $this->save_args["new_car_queries"], $this->save_data["new_car_queries"], $this->save_stat["new_car_queries"] ); ... } Data Managers | 119
  10. ... } To allow a data manager to be configured before accessing the data that it manages, you can define parameters for its constructor or define various setter methods. For example, to tell the data manager whether you’d like abbreviated or full information for the listings that are retrieved, you can define a method such as set_full_listings, which can be called anytime before calling get_data. Creating Data Managers A good approach for creating data managers is to define them for fairly granular sets of data grouped logically from the backend perspective. Backend developers may be in the best position to do this since they have good visibility into details about backend systems. Ideally, these details should be abstracted from the user interface. Once data managers are defined, the user interface can instantiate whichever of them are needed to load and save data for the page. It’s important to realize that data managers don’t necessarily correspond one-to-one to modules on the page. In fact, this is a key design attribute that makes it easy for multiple modules to access the same data, which is common in large web applications. For example, imagine a postal code stored by the backend for the current visitor. You may need to use this within multiple modules on a page, but ideally there should be a single data manager that defines the interface for getting and setting it. Because all data managers fundamentally do the same thing (i.e., get and set data), it’s useful to define a DataManager base class (see Example 6-6). This base class defines a standard interface that all data managers implement. For each data manager that you derive from this base class, implement either or both of the methods in the interface as needed, and provide whatever supporting methods are helpful for these methods to manage the data efficiently. The default implementations do nothing. Example 6-6. The DataManager base class class DataManager { public function __construct() { } public function get_data($load_args, &$load_data, &$load_stat) { } public function set_data($save_args, &$save_data, &$save_stat) { } } 120 | Chapter 6: Data Management
Đồng bộ tài khoản