1、Angular是一款優秀的前端js框架,用它可以輕鬆構建SPA(Single Page Application,單頁面應用程序) ,它的特徵是:具有mvc的架構模式,便於模塊化開發,自動化雙向數據綁定和特有的指令系統。它最大限度的解放了Dom操作,讓js代碼更專注於業務邏輯的實現,通過簡單的指令,結合頁面結構和邏輯數據,通過自定義指令,實現組件化編程,代碼結構更合理,維護成本更低。
2、Angular的應用完全沒有自己寫dom操作的地方。通過一些ng指令來完成事件綁定以及賦值,當我們寫網頁或者其他Angular應用時應注意的就是程序邏輯的地方,而不用去關注dom操作,減少了大量的代碼量。這更符合面向對象編程,讓框架去處理dom而我們只去處理邏輯。
3、如何使用Angular
在HTML代碼中引入Angular.js包
在HTML代碼中將剛剛定義的模塊通過ng-app="yourModuleName"指令的方式作用到一個特定的元素上
在JS代碼中通過angular.module('yourModuleName', []) 註冊一個模塊
根據當前頁面的情況(業務塊)劃分控制器
在HTML代碼中將剛剛定義的控制器通過ng-controller="ControllerName"作用到特定的元素上
建模(根據界面原型抽象一個數據模型)得到一個視圖模型(ViewModel)
在JS代碼中通過$scope暴露需要提供到頁面的數據成員
在HTML代碼中將剛剛暴露出來的數據通過類似ng-model/{{}}/ng-click之類的指令綁定到特定的元素上
在JS中完成業務邏輯
4、ng中是怎麼樣處理指令的
當瀏覽器渲染一個頁面時,本質上是讀html標識,然後建立dom節點,當dom樹創建完畢之後廣播一個事件給我們.
當你在頁面中使用script標籤加載ng應用程序代碼時,ng監聽上面的dom完成事件,查找帶有ng-app屬性的元素.
當找到這樣的元素之後,ng開始處理dom以這個元素的起點,所以假如ng-app被添加到html元素上,則ng就會從html元素開始處理dom.
從這個起點開始,ng開始遞歸查找所有子元素裏面,符合應用程序裏定義好的指令規則.
5、angular程序的控制器必須在ng-app的範圍之內,否則失效。
ng-model是用於表單的模型,用於代替表單的值,用作和程序之間交流的模型,可以進行雙向的數據綁定 。
{{}}表達式只能夠單向的綁定數據。實現從邏輯層到視圖層的單向綁定,呈現數據。
6、service
在angular中,service都是單例的,幾乎每一個學習的教程中都會提到這一點,
並加以強調。單例的意思是說,在整個的應用中,每一個service對象只會被實
例化一次,其他地方對這個對象的操作,會影響到全局。
首先,有個東西需要說一下。以往在Js中,我們寫了大量的代碼來存取變量值,在angular中我們找到了一個比較好的
方法。然後,剛開始寫angular的時候,我們容易把數據放的亂七八糟,有時候各種$scope, 各種域, 各種controller
的問題,這樣其實就失去了angular的優勢,angular的設計中 controller層是很薄的,一些業務邏輯的處理和數據持
久化的問題需要放到service中。出於內存的考慮,controller只有在需要的時候纔會被初始化,不需要的的時候就會
被拋棄。所以說,當你刷新頁面時,controller就會被清空。而service就可以用來保存應用數據,並且可以在不同的
域中被使用。
(1)用Factory就是創建一個對象,添加了一定的屬性之後,把它返回回來。你把service傳進controller之後,在
controller裏這個對象裏的屬性就可以通過factory使用了。
app.controller('myFactoryctrl', function($scope, myfactory){
$scope.artist = myFactory.getArtist();
});
app.factory('myfactory', fuction(){
var _artist = '';
var service = {};
service.getArtist = fuction(){
return _artist;
}
return service;
});
(2) service 是用‘new’ 關鍵字實例化的。因此,你應該給‘this’添加屬性,然後 service返回‘this’,把service傳進cont
roller之後,在controller裏的‘this’ 上的屬性就可以通過service來使用了。
app.controller('myServicectrl',function($scope, myService){
$scope.artist =myService.getArtist();
});
app.service('myService', function(){
var _artist = 'Nelly';
this.getArtist = function(){
return _artist;
}
});
(3)provider 是唯一一種可以傳進config()函數的service。當你想要在service對象啓用之前,先進行模塊範圍的配
置,那就應該使用provider.
app.controller('myProviderCtrl', function($scope, myProvider){
$scope.artist = myProvider.getArtist();
$scope.data.thingFromConfig = myProvider.thingOnConfig;
});
app.provider('myProvider', function(){
this._artist = '';
this.thingFromConfig = '' ;
this,$get = function(){
var that = this;
return {
getArtist:function(){
return that._artist;
}
thingOnConfig: that.thingFromConfig
}
}
});
app.config(function(myProviderProvider){
myProviderProvider.thingFromConfig = 'this was set in config()';
});
最後,強調一點,當我們需要在不同的域中共享數據的時候,我們最好用 service來實現.
8、關於directive裏的link和controller區別?
(1)、執行順序:先controller後link
(2)、何時使用controller:一般場景下都不想要使用controller,只需要把邏輯寫在link中就可以了;用controller的場景就是該指令(假設爲a)會被其他指令(假設爲b)require的時候,這樣就會在b指令的link函數中傳入這個controller(如果require多個的話,傳入的是一個數組,數組中存放的是每一個require的指令對應的controller),目的很顯然是爲了指令間進行交流的。
9、關於非directive的scope能否用link?
在指令中才存在調用link的時候,也就是說link是該指令在compile之後和scope進行綁定的時候調用的。
那只有在指令定義的那個地方用到link了,其他地方也可以用,例如做彈出框的時候就需要,拿到模板tpl,然後調用var linkFn = $compile(angular.element(tpl));
此時返回的就是一個link的函數,然後linkFn(scope)
,這裏的scope是你需要指定的scope,可以是新創建的,也可以是已經存在的。
10、爲什麼編譯的過程要分成compile和link?
簡單的說就是爲了解決性能問題,特別是那種model變化會影響dom結構變化的,而變化的結構還會有新的scope綁定及事件綁定,比如ng-repeat
compile
和link
的形式
compile
function compile(element, attrs, transclude) { ... }
- 在compile階段要執行的函數,返回的function就是link時要執行的function
- 常用參數爲
element
和attrs
,分別是dom元素和元素上的屬性們,其它的以後細說 - 較少使用,因爲大部分directive是處理dom元素的行爲綁定,而不是改變它們
link
function link(scope, element, attrs, controller) { ... }
- 在link階段要執行的函數,這個屬性只有當compile屬性沒有設置時才生效
- 常用參數爲
scope
,element
和attrs
,分別是當前元素所在的scope,dom元素和元素上的屬性們,其它的以後細說 - directive基本上都會有此函數,可以註冊事件,並與scope相綁
compile
和link
的使用時機
compile
- 想在dom渲染前對它進行變形,並且不需要scope參數
- 想在所有相同directive裏共享某些方法,這時應該定義在compile裏,性能會比較好
- 返回值就是link的function,這時就是共同使用的時候
link
- 對特定的元素註冊事件
- 需要用到scope參數來實現dom元素的一些行爲
11、作用域和控制器
控制器是通過增強作用域來提供業務邏輯的代碼。這是我們在作用域這個角度爲它下的一個定義。註冊的控制器在
ng-controller指令連接到AngularJS模板時被實例化的。每個控制器都會有一個自己的單獨的子作用域,至於在自定
義指令中的$scope,是與控制器作用域同級的,和控制器作用域並沒有任何的隸屬關係。
12、作用域和模板
模板是用來爲Angular提供視圖的。在模板中定義AngularJS指令時,可以使用作用域屬性和函數。在模板的表達式中
也可以引用作用域中的屬性。
13、作用域生命週期
創建->監視器註冊->模型變化->變化觀察->作用域銷燬
創建階段發生在作用域初始化時。啓動應用時將創建一個根作用域,當遇到ng-controller或者ng-repeat指令時,控制
器或者指令鏈接到模板時,將會創建子作用域。在創建階段,將會創建一個digest循環,用於與瀏覽器時間循環交
互。該digest循環負責使用模型的變化更新DOM元素,並執行所有已經註冊的監視器函數。如果需要手動的執行dig
est循環,可以通過執行$digest()方法實現. 例如,下面的代碼將執行所有的異步改動,然後執行作用域中的監視函
數。
$scope.$digest()
我們可以用$watch()方法在作用域上註冊自己的監視函數。該方法接受兩個參數,一個是作用域屬性的名字,一個
是回調函數。
$scope.$watch('name', function(newValue, oldValue) {
});
3.作用域層次問題
這裏有一個來自《Angular Js開發祕籍》的Demo,簡單明瞭。
scopeDemo.js
angular.module('myApp',[])
.controller('LevelA', function($scope){
$scope.title = "LevelA";
$scope.valueA = 1;
$scope.inc = function(){
$scope.valueA++;
};
})
.controller('LevelB', function($scope){
$scope.title = "LevelB";
$scope.valueB = 1;
$scope.inc = function(){
$scope.valueB++;
};
})
.controller('LevelC', function($scope){
$scope.title = "LevelC";
$scope.valueC = 1;
$scope.inc = function(){
$scope.valueC++;
};
});
HTML:
<!doctype html>
<html ng-app="myApp">
<head>
<title>AngularJS Scope</title>
</head>
<body>
<div ng-controller="LevelA">
<h3>{{h3}}</h3>
ValueA = {{valueA}}<input type="button" ng-click="inc()" value="+" />
<div ng-controller="LevelB"><hr>
<h3>{{title}}</h3>
ValueA = {{valueA}}<br>
ValueB = {{valueB}}<br>
<input type="button" ng-click="inc()" value="+" />
<div ng-controller="LevelC"><hr>
<h3>{{title}}</h3>
ValueA = {{valueA}}<br>
ValueB = {{valueB}}<br>
ValueC = {{valueC}}
<input type="button" ng-click="inc()" value="+" />
</div>
</div>
</div>
<script src="js/angular.min.js"></script>
<script src="scopeDemo.js"></script>
</body>
</html>
14、在debugger中查看scope:
(1). 在瀏覽器中,對着感興趣的元素點擊右鍵,選擇“查看元素”。我們可以看到瀏覽器debugger高亮了我們選中的元素。
(2). debugger允許我們在console中通過$0變量去訪問當前選擇的元素。
(3). 想查看關聯的scope,我們可以在console中輸入:angular.element($0).scope()
- $emit只能向parent controller傳遞event與data
- $broadcast只能向child controller傳遞event與data
- $on用於接收event與data
在一個controller裏面通過事件觸發一個方法,在方法裏面通過$broadcast或$emit來定義一個變量,在父,子controller裏面通過$on來獲取。
parent用$broadcast賦的值,只能子級得到值;$emit賦的值,只能父級得到;而平級的什麼都不能得到。
例子:
<!DOCTYPE HTML>
<html lang="zh-cn" ng-app = "myApp">
<head>
<meta charset="UTF-8">
<title>scope-event-propagation</title>
<style type="text/css">
.ng-cloak {
display: none;
}
.ng-scope {
border: 1px dashed red;
}
</style>
</head>
<body class="ng-cloak">
<div ng-controller="MyController">
root scope count:{{count}}
<ul>
<li ng-repeat="i in [1]" ng-controller="MyController">
<button ng-click="$emit('MyEvent')">$emit("MyEvent")</button>
<button ng-click="$broadcast('MyEvent')">$broadcast("MyEvent")</button>
<br/>
middle scope count:{{count}} //平級
<ul>
<li ng-repeat="item in [1,2]" ng-controller="MyController">
Leaf scope count:{{count}}
</li>
</ul>
</li>
</ul>
</div>
</body>
<script src = "js/angular.min.js"></script>
<script src="demo.js">
</script>
</html>
demo.js
angular.module('myApp', [])
.controller("MyController", function ($scope) {
$scope.count = 0;
$scope.$on("MyEvent", function() {
$scope.count++;
});
})
16、我們通過使用angular.element(aDomElement).scope()查看任意DOM元素的scope。
17、模塊定義
angular.module("app", []);
angular.module("app").controller("testCtrl", ["$scope", function ($scope) {
$scope.currentMenu = "menu1";
$scope.selectMenu = function (menu) {
$scope.currentMenu = menu;
}
}]);
- 我給ng-app指定了一個名稱叫”app“,同時js代碼使用
angular.module("app", []);
定義了一個名稱爲”app“的module,同時用這個app module 的controller方法定義了一個testCtrl;定義module函數是angular對象上的靜態方法,第一個參數傳名稱,第二個參數是一個數組,這個數組表示這個module所引用的其他module,在這個例子中我們沒有使用任何其他的module,所以傳入一個空的數組,如果第二個參數不傳,表示獲取名稱爲”app“的module;