前言
近期一直在玩Angularjs,不得不說,相對於Knockout,Angularjs這一MVVM框架更強大,也更複雜,各種教程網上到處都是,不過真正用到項目的時候會遇到各種坑。
一、ng-repeat
ng-repeat 用來識別某個elem 需要重複輸出,同時重複輸出的內容需為唯一
<div ng-app="app" ng-controller="control"> <h3 ng-repeat="content in repeatContent">ng-repeat: {{ content }}</h3> </div>
let app = angular.module("app", []); app.controller("control", ($scope) => { // 输出李滨泓 $scope.repeatContent = ["李", "滨", "泓"]; // 下面存在两个“泓”,会报错 // $scope.repeatContent = ["李", "滨", "泓", "泓"]; })
的關係
factory
factory 很像service,不同之處在於,service 在Angular 中是一個單例對象,即當需要使用service 時,使用new 關鍵字來建立一個(也僅此一個)service。而 factory 則是一個普通的函數,當需要用時,他也只是一個普通函數的調用方式,它可以返回各種形式的數據,例如通過返回一個功能函數的集合對象來將供與使用。
定義:
let app = angular.module("app", []); // 这里可以注入 $http 等 Provider app.factory("Today", () => { let date = new Date(); return { year: date.getFullYear(), month: date.getMonth() + 1, day: date.getDate() }; });
使用注入:
app.controller("control", (Today) => { console.log(Today.year); console.log(Today.month); console.log(Today.day); });
service
service因為它使用new 關鍵字新建,同時它可以用在controller 之間的通訊與資料交互,因為controller 在無用時其作用域鏈會被銷毀(例如使用路由跳到另一個頁面,同時使用了另一個controller)定義:let app = angular.module("app", []); // 这里可以注入 $http 等 Provider // 注意这里不可以使用 arrow function // arrow function 不能作为 constructor app.service("Today", function() { let date = new Date(); this.year = date.getFullYear(); this.month = date.getMonth() + 1; this.day = date.getDate(); });使用注入:
app.controller("control", (Today) => { console.log(Today.year); console.log(Today.month); console.log(Today.day); });
provider 前對provider 進行一些參數的配置。
定義:
let app = angular.module("app", []); // 这里可以注入 $http 等 Provider // 注意这里不可以使用 arrow function // arrow function 不能作为 constructor app.provider("Today", function() { this.date = new Date(); let self = this; this.setDate = (year, month, day) => { this.date = new Date(year, month - 1, day); } this.$get = () => { return { year: this.date.getFullYear(), month: this.date.getMonth() + 1, day: this.date.getDate() }; }; });
使用注入:
// 这里重新配置了今天的日期是 2015年2月15日 // 注意这里注入的是 TodayProvider,使用驼峰命名来注入正确的需要配置的 provider app.config((TodayProvider) => { TodayProvider.setDate(2015, 2, 15); }); app.controller("control", (Today) => { console.log(Today.year); console.log(Today.month); console.log(Today.day); });
、handlebarbar ,而其中使用了handlebars 作為模板引擎,當node.js 對某URL 進行對應並render,由於其模板使用{ {} } 作為變數解析符號。同樣地,angular 也使用{ {} } 作為變數解析符號,所以當node.js 進行render 頁面後,如果{ {} } 內的變數不存在,則該個區域會被清空,而我的原意是這個作為angular 的解析所用,而不是handlebars 使用,同時我也想繼續使用handlebars,那麼此時就需要將angular 預設的{ {} } 解析符號重新定義。即使以依賴注入$interpolateProvider 進行定義,如下範例:
app.config($interpolateProvider => { $interpolateProvider.startSymbol('{[{'); $interpolateProvider.endSymbol('}]}'); });
ng-annotate-loader 應用於webpack +ular 在開發場景JS 壓縮後導致依賴注入失效並出現錯誤的解決方法
安裝
$ npm install ng-annotate-loader --save-dev
// webpack.config.js { test: /\.js?$/, exclude: /(node_modules|bower_components)/, loader: 'ng-annotate!babel?presets=es2015' },
,$scope 裡的資料改變並不會引起$digest 的dirty-checking 循環,這將導致當model 改變時,view 不會同步更新,這時我們需要自己主動觸發更新
HTML
<div>{{ foo }}</div> <button id="addBtn">go</button>
JavaScript
app.controller("control", ($scope) => { $scope.foo = 0; document.getElementById("addBtn").addEventListener("click", () => { $scope.foo++; }, false); })
很明顯,示例的意圖是當點擊button 時,foo 自增長並更新View,但是實際上,$scope.foo 是改變了,但是View 並不會刷新,這是因為foo 並沒有一個$watch 檢測變化後$apply,最終引起$digest,所以我們需要自己觸發$apply 或創建一個$watch 來觸發或檢測數據變化
JavaScript(使用$apply)
app.controller("control", ($scope) => { $scope.foo = 0; document.getElementById("addBtn").addEventListener("click", () => { $scope.$apply(function() { $scope.foo++; }); }, false); })
JavaScript(使用$watch & $digest)
app.controller("control", ($scope) => { $scope.foo = 0; $scope.flag = 0; $scope.$watch("flag", (newValue, oldValue) => { // 当 $digest 循环检测 flag 时,如果新旧值不一致将调用该函数 $scope.foo = $scope.flag; }); document.getElementById("addBtn").addEventListener("click", () => { $scope.flag++; // 主动触发 $digest 循环 $scope.$digest(); }, false); })六、$watch(watchExpression, listener, [objectEquality])註冊一個listener 回呼函數,每次發生回呼函數,在每次發生時都會改變的值在每次$digest 執行時被調用,並返回要被檢測的值(當多次輸入相同的值時,watchExpression 不應該改變其自身的值,否則可能會引起多次的$digest 循環,watchExpression 應該冪等等)
listener 將在當前watchExpression 回傳值和上次的watchExpression 回傳值不一致時被呼叫(使用!== 來嚴格地判斷不一致性,而不是使用== 來判斷,不過objectEquality == true 除外)
objectEquality 為boolean 值,當為true 時,將使用angular.equals 來判斷一致性,並使用angular.copy 來保存此次的Object 拷貝副本供給下一次的比較,這意味著複雜的對象檢測將會有效能和記憶體上的問題
七、$apply([exp])
function $apply(expr) { try { return $eval(expr); } catch (e) { $exceptionHandler(e); } finally { $root.$digest(); } }使用$eval(expr) 執行expr 表達式
如果在執行過程中跑出exception,那麼執行$exceptionHandler(e)
最後無論結果,都會執行一次 $digest 循環