亚洲免费乱码视频,日韩 欧美 国产 动漫 一区,97在线观看免费视频播国产,中文字幕亚洲图片

      1. <legend id="ppnor"></legend>

      2. 
        
        <sup id="ppnor"><input id="ppnor"></input></sup>
        <s id="ppnor"></s>

        在AngularJS框架中處理數(shù)據(jù)建模的方式解析

        字號:


            我們知道,AngularJS并沒有自帶立等可用的數(shù)據(jù)建模方案。而是以相當(dāng)抽象的方式,讓我們在controller中使用JSON數(shù)據(jù)作為模型。但是隨著時間的推移和項目的成長,我意識到這種建模的方式不再能滿足我們項目的需求。在這篇文章中我會介紹在我的AngularJS應(yīng)用中處理數(shù)據(jù)建模的方式。
            為Controller定義模型
            讓我們從一個簡單的例子開始。我想要顯示一個書本(book)的頁面。下面是控制器(Controller):
            BookController
            app.controller('BookController', ['$scope', function($scope) {
              $scope.book = {
                id: 1,
                name: 'Harry Potter',
                author: 'J. K. Rowling',
                stores: [
                  { id: 1, name: 'Barnes & Noble', quantity: 3},
                  { id: 2, name: 'Waterstones', quantity: 2},
                  { id: 3, name: 'Book Depository', quantity: 5}
                ]
              };
            }]);
            這個控制器創(chuàng)建了一個書本的模型,我們可以在后面的模板中(templage)中使用它。
            template for displaying a book
            <div ng-controller="BookController">
              Id: <span ng-bind="book.id"></span>
              Name:<input type="text" ng-model="book.name" />
              Author: <input type="text" ng-model="book.author" />
            </div>
            假如我們需要從后臺的api獲取書本的數(shù)據(jù),我們需要使用$http:
            BookController with $http
            app.controller('BookController', ['$scope', '$http', function($scope, $http) {
              var bookId = 1;
              $http.get('ourserver/books/' + bookId).success(function(bookData) {
                $scope.book = bookData;
              });
            }]);
            注意到這里的bookData仍然是一個JSON對象。接下來我們想要使用這些數(shù)據(jù)做一些事情。比如,更新書本信息,刪除書本,甚至其他的一些不涉及到后臺的操作,比如根據(jù)請求的圖片大小生成一個書本圖片的url,或者判斷書本是否有效。這些方法都可以被定義在控制器中。
            BookController with several book actions
            app.controller('BookController', ['$scope', '$http', function($scope, $http) {
              var bookId = 1;
              $http.get('ourserver/books/' + bookId).success(function(bookData) {
                $scope.book = bookData;
              });
              $scope.deleteBook = function() {
                $http.delete('ourserver/books/' + bookId);
              };
              $scope.updateBook = function() {
                $http.put('ourserver/books/' + bookId, $scope.book);
              };
              $scope.getBookImageUrl = function(width, height) {
                return 'our/image/service/' + bookId + '/width/height';
              };
              $scope.isAvailable = function() {
                if (!$scope.book.stores || $scope.book.stores.length === 0) {
                  return false;
                }
                return $scope.book.stores.some(function(store) {
                  return store.quantity > 0;
                });
              };
            }]);
            然后在我們的模板中:
            template for displaying a complete book
            <div ng-controller="BookController">
              <div ng-style="{ backgroundImage: 'url(' + getBookImageUrl(100, 100) + ')' }"></div>
              Id: <span ng-bind="book.id"></span>
              Name:<input type="text" ng-model="book.name" />
              Author: <input type="text" ng-model="book.author" />
              Is Available: <span ng-bind="isAvailable() ? 'Yes' : 'No' "></span>
              <button ng-click="deleteBook()">Delete</button>
              <button ng-click="updateBook()">Update</button>
            </div>
            在controllers之間共享Model
            如果書本的結(jié)構(gòu)和方法只和一個控制器有關(guān),那我們現(xiàn)在的工作已經(jīng)可以應(yīng)付。但是隨著應(yīng)用的增長,會有其他的控制器也需要和書本打交道。那些控制器很多時候也需要獲取書本,更新它,刪除它,或者獲得它的圖片url以及看它是否有效。因此,我們需要在控制器之間共享這些書本的行為。我們需要使用一個返回書本行為的factory來實現(xiàn)這個目的。在動手寫一個factory之前,我想在這里先提一下,我們創(chuàng)建一個factory來返回帶有這些book輔助方法的對象,但我更傾向于使用prototype來構(gòu)造一個Book類,我覺得這是更正確的選擇:
            Book model service
            app.factory('Book', ['$http', function($http) {
              function Book(bookData) {
                if (bookData) {
                  this.setData(bookData):
                }
            // Some other initializations related to book
              };
              Book.prototype = {
                setData: function(bookData) {
                  angular.extend(this, bookData);
                },
                load: function(id) {
                  var scope = this;
                  $http.get('ourserver/books/' + bookId).success(function(bookData) {
                    scope.setData(bookData);
                  });
                },
                delete: function() {
                  $http.delete('ourserver/books/' + bookId);
                },
                update: function() {
                  $http.put('ourserver/books/' + bookId, this);
                },
                getImageUrl: function(width, height) {
                  return 'our/image/service/' + this.book.id + '/width/height';
                },
                isAvailable: function() {
                  if (!this.book.stores || this.book.stores.length === 0) {
                    return false;
                  }
                  return this.book.stores.some(function(store) {
                    return store.quantity > 0;
                  });
                }
              };
              return Book;
            }]);
            這種方式下,書本相關(guān)的所有行為都被封裝在Book服務(wù)內(nèi)?,F(xiàn)在,我們在BookController中來使用這個亮眼的Book服務(wù)。
            BookController that uses Book model
            app.controller('BookController', ['$scope', 'Book', function($scope, Book) {
              $scope.book = new Book();
              $scope.book.load(1);
            }]);
            正如你看到的,控制器變得非常簡單。它創(chuàng)建一個Book實例,指派給scope,并從后臺加載。當(dāng)書本被加載成功時,它的屬性會被改變,模板也隨著被更新。記住其他的控制器想要使用書本功能,只要簡單地注入Book服務(wù)即可。此外,我們還要改變template使用book的方法。
            template that uses book instance
            <div ng-controller="BookController">
              <div ng-style="{ backgroundImage: 'url(' + book.getImageUrl(100, 100) + ')' }"></div>
              Id: <span ng-bind="book.id"></span>
              Name:<input type="text" ng-model="book.name" />
              Author: <input type="text" ng-model="book.author" />
              Is Available: <span ng-bind="book.isAvailable() ? 'Yes' : 'No' "></span>
              <button ng-click="book.delete()">Delete</button>
              <button ng-click="book.update()">Update</button>
            </div>
            到這里,我們知道了如何建模一個數(shù)據(jù),把他的方法封裝到一個類中,并且在多個控制器中共享它,而不需要寫重復(fù)代碼。
            在多個控制器中使用相同的書本模型
            我們定義了一個書本模型,并且在多個控制器中使用了它。在使用了這種建模架構(gòu)之后你會注意到有一個嚴重的問題。到目前為止,我們假設(shè)多個控制器對書本進行操作,但如果有兩個控制器同時處理同一本書會是什么情況呢?
            假設(shè)我們頁面的一塊區(qū)域我們所有書本的名稱,另一塊區(qū)域可以更新某一本書。對應(yīng)這兩塊區(qū)域,我們有兩個不同的控制器。第一個加載書本列表,第二個加載特定的一本書。我們的用戶在第二塊區(qū)域中修改了書本的名稱并且點擊“更新”按鈕。更新操作成功后,書本的名稱會被改變。但是在書本列表中,這個用戶始終看到的是修改之前的名稱!真實的情況是我們對同一本書創(chuàng)建了兩個不同的書本實例——一個在書本列表中使用,而另一個在修改書本時使用。當(dāng)用戶修改書本名稱的時候,它實際上只修改了后一個實例中的屬性。然而書本列表中的書本實例并未得到改變。
            解決這個問題的辦法是在所有的控制器中使用相同的書本實例。在這種方式下,書本列表和書本修改的頁面和控制器都持有相同的書本實例,一旦這個實例發(fā)生變化,就會被立刻反映到所有的視圖中。那么按這種方式行動起來,我們需要創(chuàng)建一個booksManager服務(wù)(我們沒有大寫開頭的b字母,是因為這是一個對象而不是一個類)來管理所有的書本實例池,并且富足返回這些書本實例。如果被請求的書本實例不在實例池中,這個服務(wù)會創(chuàng)建它。如果已經(jīng)在池中,那么就直接返回它。請牢記,所有的加載書本的方法最終都會被定義在booksManager服務(wù)中,因為它是唯一的提供書本實例的組件。
            booksManager service
            app.factory('booksManager', ['$http', '$q', 'Book', function($http, $q, Book) {
              var booksManager = {
                _pool: {},
                _retrieveInstance: function(bookId, bookData) {
                  var instance = this._pool[bookId];
                  if (instance) {
                    instance.setData(bookData);
                  } else {
                    instance = new Book(bookData);
                    this._pool[bookId] = instance;
                  }
                  return instance;
                },
                _search: function(bookId) {
                  return this._pool[bookId];
                },
                _load: function(bookId, deferred) {
                  var scope = this;
                  $http.get('ourserver/books/' + bookId)
                    .success(function(bookData) {
                      var book = scope._retrieveInstance(bookData.id, bookData);
                      deferred.resolve(book);
                    })
                    .error(function() {
                      deferred.reject();
                    });
                },
            /* Public Methods */
            /* Use this function in order to get a book instance by it's id */
                getBook: function(bookId) {
                  var deferred = $q.defer();
                  var book = this._search(bookId);
                  if (book) {
                    deferred.resolve(book);
                  } else {
                    this._load(bookId, deferred);
                  }
                  return deferred.promise;
                },
            /* Use this function in order to get instances of all the books */
                loadAllBooks: function() {
                  var deferred = $q.defer();
                  var scope = this;
                  $http.get('ourserver/books)
                    .success(function(booksArray) {
                      var books = [];
                      booksArray.forEach(function(bookData) {
                        var book = scope._retrieveInstance(bookData.id, bookData);
                        books.push(book);
                      });
                      deferred.resolve(books);
                    })
                    .error(function() {
                      deferred.reject();
                    });
                  return deferred.promise;
                },
            /* This function is useful when we got somehow the book data and we wish to store it or update the pool and get a book instance in return */
                setBook: function(bookData) {
                  var scope = this;
                  var book = this._search(bookData.id);
                  if (book) {
                    book.setData(bookData);
                  } else {
                    book = scope._retrieveInstance(bookData);
                  }
                  return book;
                },
              };
              return booksManager;
            }]);
            下面是我們的EditableBookController和BooksListController兩個控制器的代碼:
            EditableBookController and BooksListController that uses booksManager
            app.factory('Book', ['$http', function($http) {
              function Book(bookData) {
                if (bookData) {
                  this.setData(bookData):
                }
            // Some other initializations related to book
              };
              Book.prototype = {
                setData: function(bookData) {
                  angular.extend(this, bookData);
                },
                delete: function() {
                  $http.delete('ourserver/books/' + bookId);
                },
                update: function() {
                  $http.put('ourserver/books/' + bookId, this);
                },
                getImageUrl: function(width, height) {
                  return 'our/image/service/' + this.book.id + '/width/height';
                },
                isAvailable: function() {
                  if (!this.book.stores || this.book.stores.length === 0) {
                    return false;
                  }
                  return this.book.stores.some(function(store) {
                    return store.quantity > 0;
                  });
                }
              };
              return Book;
            }]);
            需要注意的是,模塊(template)中還是保持原來使用book實例的方式。現(xiàn)在應(yīng)用中只持有一個id為1的book實例,它發(fā)生的所有改變都會被反映到使用它的各個頁面上。
            AngularJS 中的一些坑
            UI的閃爍
            Angular的自動數(shù)據(jù)綁定功能是亮點,然而,他的另一面是:在Angular初始化之前,頁面中可能會給用戶呈現(xiàn)出沒有解析的表達式。當(dāng)DOM準(zhǔn)備就緒,Angular計算并替換相應(yīng)的值。這樣就會導(dǎo)致出現(xiàn)一個丑陋的閃爍效果。
            上述情形就是在Angular教程中渲染示例代碼的樣子:
            <body ng-controller="PhoneListCtrl">
             <ul>
              <li ng-repeat="phone in phones">
               {{ phone.name }}
               <p>{{ phone.snippet }}</p>
              </li>
             </ul>
            </body>
            如果你做的是SPA(Single Page Application),這個問題只會在第一次加載頁面的時候出現(xiàn),幸運的是,可以很容易杜絕這種情形發(fā)生: 放棄{{ }}表達式,改用ng-bind指令
            <body ng-controller="PhoneListCtrl">
             <ul>
              <li ng-repeat="phone in phones">
               <span ng-bind="phone.name"></span>
               <p ng-bind="phone.snippet">Optional: visually pleasing placeholder</p>
              </li>
             </ul>
            </body>
            你需要一個tag來包含這個指令,所以我添加了一個<span>給phone name.
            那么初始化的時候會發(fā)生什么呢,這個tag里的值會顯示(但是你可以選擇設(shè)置空值).然后,當(dāng)Angular初始化并用表達式結(jié)果替換tag內(nèi)部值,注意你不需要在ng-bind內(nèi)部添加大括號。更簡潔了!如果你需要符合表達式,那就用ng-bind-template吧,
            如果用這個指令,為了區(qū)分字符串字面量和表達式,你需要使用大括號
            另外一種方法就是完全隱藏元素,甚至可以隱藏整個應(yīng)用,直到Angular就緒。
            Angular為此還提供了ng-cloak指令,工作原理就是在初始化階段inject了css規(guī)則,或者你可以包含這個css 隱藏規(guī)則到你自己的stylesheet。Angular就緒后就會移除這個cloak樣式,讓我們的應(yīng)用(或者元素)立刻渲染。
            Angular并不依賴jQuery。事實上,Angular源碼里包含了一個內(nèi)嵌的輕量級的jquery:jqLite. 當(dāng)Angular檢測到你的頁面里有jQuery出現(xiàn),他就會用這個jQuery而不再用jqLite,直接證據(jù)就是Angular里的元素抽象層。比如,在directive中訪問你要應(yīng)用到的元素。
            angular.module('jqdependency', [])
             .directive('failswithoutjquery', function() {
              return {
               restrict : 'A',
               link : function(scope, element, attrs) {
                    element.hide(4000)
                   }
              }
            });
            但是這個元素jqLite還是jQuery元素呢?取決于,手冊上這么寫的:
            Angular中所有的元素引用都會被jQuery或者jqLite包裝;他們永遠不是純DOM引用
            所以Angular如果沒有檢測到j(luò)Query,那么就會使用jqLite元素,hide()方法值能用于jQuery元素,所以說這個示例代碼只能當(dāng)檢測到j(luò)Query時才可以使用。如果你(不小心)修改了AngularJS和jQuery的出現(xiàn)順序,這個代碼就會失效!雖說沒事挪腳本的順序的事情不經(jīng)常發(fā)生,但是在我開始模塊化代碼的時候確實給我造成了困擾。尤其是當(dāng)你開始使用模塊加載器(比如 RequireJS), 我的解決辦法是在配置里顯示的聲明Angular確實依賴jQuery
            另外一種方法就是你不要通過Angular元素的包裝來調(diào)用jQuery特定的方法,而是使用$(element).hide(4000)來表明自己的意圖。這樣依賴,即使修改了script加載順序也沒事。
            壓縮
            特別需要注意的是Angular應(yīng)用壓縮問題。否則錯誤信息比如 ‘Unknown provider:aProvider  <- a' 會讓你摸不到頭腦。跟其他很多東西一樣,這個錯誤在官方文檔里也是無從查起的。簡而言之,Angular依賴參數(shù)名來進行依賴注入。壓縮器壓根意識不到這個這跟Angular里普通的參數(shù)名有啥不同,盡可能的把腳本變短是他們職責(zé)。咋辦?用“友好壓縮法”來進行方法注入。看這里:
            module.service('myservice', function($http, $q) {
            // This breaks when minified
            });
            to this:
            module.service('myservice', [ '$http', '$q', function($http, $q) {
            // Using the array syntax to declare dependencies works with minification<b>!</b>
            }]);
            這個數(shù)組語法很好的解決了這個問題。我的建議是從現(xiàn)在開始照這個方法寫,如果你決定壓縮JavaScript,這個方法可以讓你少走很多彎路。好像是一個automatic rewriter機制,我也不太清楚這里面是怎么工作的。
            最終一點建議:如果你想用數(shù)組語法復(fù)寫你的functions,在所有Angular依賴注入的地方應(yīng)用之。包括directives,還有directive里的controllers。別忘了逗號(經(jīng)驗之談)
            // the directive itself needs array injection syntax:
            module.directive('directive-with-controller', ['myservice', function(myservice) {
              return {
               controller: ['$timeout', function($timeout) {
            // but this controller needs array injection syntax, too! 
               }],
               link : function(scope, element, attrs, ctrl) {
               }
              }
            }]);
            注意:link function不需要數(shù)組語法,因為他并沒有真正的注入。這是被Angular直接調(diào)用的函數(shù)。Directive級別的依賴注入在link function里也是使用的。
             Directive永遠不會‘完成'
            在directive中,一個令人掉頭發(fā)的事就是directive已經(jīng)‘完成'但你永遠不會知道。當(dāng)把jQuery插件整合到directive里時,這個通知尤為重要。假設(shè)你想用ng-repeat把動態(tài)數(shù)據(jù)以jQuery datatable的形式顯示出來。當(dāng)所有的數(shù)據(jù)在頁面中加載完成后,你只需要調(diào)用$(‘.mytable).dataTable()就可以了。 但是,臣妾做不到??!
            為啥呢?Angular的數(shù)據(jù)綁定是通過持續(xù)的digest循環(huán)實現(xiàn)的。基于此,Angular框架里根本沒有一個時間是‘休息'的。 一個解決方法就是將jQuery dataTable的調(diào)用放在當(dāng)前digest循環(huán)外,用timeout方法就可以做到。
            angular.module('table',[]).directive('mytable', ['$timeout', function($timeout) {
              return {
               restrict : 'E',
               template: '<table>' +
                      '<thead><tr><th>counting</th></tr></thead>' +
                      '<tr ng-repeat="data in datas"><td></td></tr>' +
                    '</table>',
               link : function(scope, element, attrs, ctrl) {
                 scope.datas = ["one", "two", "three"]
            // Doesn't work, shows an empty table:
            // $('.mytable', element).dataTable() 
            // But this does:
                 $timeout(function() {
                  $('.mytable', element).dataTable();
                 }, 0)
               }
              }
            }]);