在Angular中創建一個對象時,需要依賴另一個對象,這是代碼層的一種依賴關系,當這種依賴被聲明后,Angular通過injector
注入器將所依賴的對象進行注入操作
依賴注入介紹
依賴注入的原理
看下面的示例代碼:
<div ng-controller="MyController">
<div class="{{cls}}">{{show}}</div>
<button ng-click="onClick()">點我</button>
</div>
var myapp = angular.module('MyApp', []);
myapp.config(function($controllerProvider) {
$controllerProvider.register('MyController', ['$scope', function($scope) {
$scope.cls = '';
$scope.onClick = function () {
$scope.cls = 'show';
$scope.show = '點擊后顯示的內容';
}
}])
})
看上面的代碼,與我們平時定義控制器的方式不一樣
myapp.controller('MyController', ['$scope', function($scope){
// 控制器代碼
}])
在Angular中,通過模塊的config函數來聲明需要注入的依賴對象,而聲明的方式是通過調用provider服務。
但在Angular內部,控制器并不是由provider
服務來創建的,而是由controllerProvider
服務創建,在創建過程中,實際上是在config函數中調用controllerProvider
服務的register
方法,再調用injector
注入器完成各個依賴對象的注入。
簡單依賴注入的示例
在Angular中,config函數的功能是為定義的模板對象注入依賴的各種服務。除了用于注冊控制器的controllerProvider服務外,還有一個$provider
服務,其中它包含了如provider
方法、factory
方法、service
方法和value
方法,這些方法都可以通過服務創建一個自定義的依賴注入對象。
示例:
html:
<div ng-controller="myCtrl">
<div class="{{cls}}">{{text}}</div>
<button ng-click="onClick(1)">早上</button>
<button ng-click="onClick(2)">上午</button>
<button ng-click="onClick(3)">下午</button>
<button ng-click="onClick(4)">晚上</button>
</div>
var app = angular.module('myApp', []);
app.config(function ($provide) {
$provide.provider('myprovider', function () {
this.$get = function () {
return {
val: function (name) {
return name;
}
}
}
})
});
app.config(function ($provide) {
$provide.factory('myfactory', function () {
return {
val: function (name) {
return name;
}
}
})
});
app.config(function ($provide) {
$provide.value('myvalue', function (name) {
return name;
})
});
app.config(function ($provide) {
$provide.service('myservice', function () {
return {
val: function (name) {
return name;
}
}
})
});
app.controller('myCtrl', ['$scope', 'myprovider', 'myfactory', 'myvalue', 'myservice',
function ($scope, myprovider, myfactory, myvalue, myservice) {
$scope.cls = '';
$scope.onClick = function (t) {
$scope.cls = 'show';
switch (t) {
case 1: $scope.text = myprovider.val('早上好'); break;
case 2: $scope.text = myfactory.val('上午好'); break;
case 3: $scope.text = myvalue('下午好'); break;
case 4: $scope.text = myservice.val('晚上好'); break;
}
}
}]);
依賴注入標記
每個Angular應用都是通過由注入器(injector
)負責查找和創建依賴注入的服務,當注入器執行時,它需要一些標記,來判斷需要注入什么樣的依賴服務,而這些標記,就是依賴注入標記。
依賴注入標記根據聲明的方式,分為:
- 推斷式注入
- 標記式注入
- 行內式注入
推斷式注入
推斷式注入是一種猜測式的注入,在沒有明確聲明的情況下,Angular認為參數名稱就是依賴注入的函數名,并在內部調用函數對象的toString
方法,獲取對應的參數列表,然后通過注入器(injector
)將這些參數注入到應用實例中,從而實現依賴注入。
示例:
html:
<div ng-controller="myCtrl">
<input type="button" value="彈出對話框" ng-click="onClick('我是一個彈出對話框')">
</div>
javascript:
var app = angular.module('myApp', []);
app.factory('myfactory', function ($window) {
return {
show: function (text) {
$window.alert(text);
}
}
});
var myCtrl = function ($scope, myfactory) {
$scope.onClick = function (msg) {
myfactory.show(msg);
}
}
app.controller('myCtrl', myCtrl);
標記式注入
標記式注入明確了一個函數在執行過程中需要依賴的各項服務,它可以直接調用$injector
屬性來完成,它的屬性值是一個數組,數組元素是需要注入的各項服務的名稱,所以這種方式的注入順序很重要
示例:
html:
<div ng-controller="myCtrl">
<div class="show">{{text}}</div>
<input type="button" value="彈出" ng-click="onShow('我是一個彈出對話框')">
<input type="button" value="顯示" ng-click="onWrite('今天天氣有點冷啊')">
</div>
javascript:
var app = angular.module('myApp', []);
app.factory('$show', ['$window', function ($window) {
return {
show: function (text) {
$window.alert(text);
}
}
}]);
app.factory('$write', function () {
return {
write: function (text) {
return text;
}
}
});
var myCtrl = function ($scope, $show, $write) {
$scope.onShow = function (msg) {
$show.show(msg);
}
$scope.onWrite = function (msg) {
$scope.text = $write.write(msg);
}
}
app.controller('myCtrl', myCtrl);
app.$inject = ['$scope', '$show', '$write'];
這種注入方式由于服務名和函數參數名在名稱和順序的一一對應關系,使得服務名和函數體綁定在一起,因此這種方式可以在壓縮或混淆后的代碼中執行。
行內式注入
所謂行內式注入,就是在構建一個Angular對象時,允許將一個字符型數組作為對象的參數,而不僅僅是一個函數。
在這個數組中,除最后一個必須是函數體外,其余都代表注入對象中的服務名,而他們的名稱和順序與最后一個函數的參數是一一對應的。
示例:
html:
<div ng-controller="myCtrl">
<div class="show">{{text}}</div>
<input type="button" value="求和" ng-click="onClick(5,10)">
</div>
var app = angular.module('myApp', []);
app.factory('$sum', function () {
return {
add: function (m, n) {
return m + n;
}
}
});
app.controller('myCtrl', ['$scope', '$sum', function ($scope, $sum) {
$scope.onClick = function (m, n) {
$scope.text = m + " + " + n + ' = ' + $sum.add(m, n);
}
}])
這種方法更簡潔,同樣也能在壓縮或混淆后的代碼中執行
$injector常用API
Angular中依賴注入離不開一個重要的對象--注入器($injector
),整個Angular應用中的注入對象都由它負責定位和創建,它也有很多方法,如get
、has
、invoke
等
has和get方法
has
方法的功能是根據傳入的名稱,從注冊的列表中查找對應的服務,如果找到返回true,否則返回false
injector.has(name)
-
injector
為獲取的$injector
對象 -
name
為需要查找的服務名稱
最后返回一個布爾值
get
方法返回指定名稱的服務實例,獲取到服務的實例對象后,就可以直接調用服務中的屬性和方法
injector.get(name)
-
injector
為獲取的$injector
對象 -
name
為需要返回實例的服務名稱
執行代碼后,將返回一個服務實例
var app = angular.module('myApp', []);
app.factory('$custom', function () {
return {
print: function (msg) {
console.log(msg);
}
}
});
// 獲取injector對象
var injector = angular.injector(['app', 'ng']);
// 通過has方法判斷是否有$custom服務
var has = injector.has('$custom');
console.log(has);
// 判斷如果存在$custom服務,則調用其方法在控制臺輸出任意字符
if (has) {
var custom = injector.get('$custom');
custom.print('控制臺輸入任意的內容!');
}
app.controller('myCtrl', ['$scope', '$custom', function ($scope, $custom) {
// ....
}]);
invoke方法
invoke
方法可以執行一個自定義的函數,除此之外,在執行函數時,還能傳遞變量給函數自身
injector.invoke(fn, [self], [locals])
-
injector
為獲取的$injector
對象 -
fn
為需要執行的函數名稱 -
self
是可選參數,它是一個對象,表示用于函數中的this
變量 -
locals
可選參數,也是一個對象,它能為函數中的變量名的傳遞提供方法支持
var myapp = angular.module('myApp', []);
myapp.factory('$custom', function () {
return {
print: function (msg) {
console.log(msg);
}
}
});
var injector = angular.injector(['myapp', 'ng']);
var fun = function ($custom) {
$custom.print('函數執行成功!');
}
injector.invoke(fun);
myapp.controller('myCtrl', ['$scope', '$custom', function ($scope, $custom) {
//...
}])