最近都在閑,好不容易可以輕松一下,就放心的玩了一個月。現(xiàn)在到公司實習(xí)了,正好接著做之前沒有做完的事情。在使用Ionic框架的過程中還是遇到一些問題,就此記錄一下。
關(guān)于Ionic框架,之前也寫了一篇,記錄的是遇到的一些小問題,這次這篇,和AngularJS有關(guān)。我們知道Ionic框架是基于AngularJS的,由AngularJS進行路由控制,數(shù)據(jù)交互,而Ionic只相當(dāng)于一個UI框架。所以如果有AngularJS基礎(chǔ),學(xué)習(xí)這個框架其實不難。但是其中還是有一些不一樣的地方,這篇要說的,就是Ionic中$scope的作用域問題。
問題描述
在Ionic應(yīng)用中,HTML頁面中ng-model綁定的數(shù)據(jù),無法在controller中獲取到。
詳情如下:
<!--HTML代碼中-->
<div class="item-input textarea-input">
<textarea rows="4" ng-model="content" placeholder="說點什么吧..."></textarea>
</div>
//controller中
$scope.publish = function() {
console.log($scope.content);
}
在這里控制臺輸出是undefined。問題就在這里,如果是在AngularJS中,這樣寫是完全沒有問題的。
在此之前,先看一下AngularJS中controller的控制域,之前學(xué)習(xí)的時候只是簡單的知道controller之間可以是平級關(guān)系,也可以是子父級關(guān)系,這取決于controller控制的html區(qū)域的關(guān)系,是否嵌套,所以對應(yīng)的$scope的作用域也是可以嵌套的。
AngularJS的根作用域
每個AngularJS應(yīng)用都有一個默認(rèn)的根作用域$rootScope,如果沒有指定控制器,那么ng-model綁定的數(shù)據(jù)就會自動綁定到$rootScope中。如果想要在controller中使用$rootScope綁定的數(shù)據(jù),需要像$scope一樣,注入到控制器中。
AngularJS中作用域的繼承關(guān)系
作用域的繼承是剛接觸AngularJS的時候就了解到的內(nèi)容了,但是一直沒有深入了解,現(xiàn)在才回遇到這個問題。在html頁面中的嵌套關(guān)系中,綁定上對應(yīng)的controller,controller也就嵌套,這樣一來,對應(yīng)的作用域就有了繼承關(guān)系。
如下:
<div ng-app="myApp" ng-controller="parentCtrl">
<p>{{firstName}}</p>
<div ng-controller="childCtrl">
<p>{{firstName}}</p>
<button ng-click="update()">點擊修改</button>
</div>
</div>
//js
app.controller('parentCtrl', function($scope) {
$scope.firstName = "John";
});
app.controller('childCtrl', function($scope) {
$scope.update = function() {
}
});
由此,兩個控制器中的$scope有了子父級關(guān)系,那么,哪些內(nèi)容是可以繼承的,哪些內(nèi)容是共享的呢?
1.簡單變量可繼承
還是上面這個例子,寫上按鈕的點擊事件:
<div ng-app="myApp" ng-controller="parentCtrl">
<p>{{firstName}}</p>
<div ng-controller="childCtrl">
<p>{{firstName}}</p>
<button ng-click="update()">點擊修改</button>
</div>
</div>
//js
app.controller('parentCtrl', function($scope) {
$scope.firstName = "John";
});
app.controller('childCtrl', function($scope) {
$scope.update = function() {
$scope.firstName = $scope.firstName+"zzz";
}
});
一開始,我們沒有在childCtrl中聲明firstName變量,但是childCtrl管轄范圍內(nèi)的還是顯示出了這個變量值,就是從parentCtrl中繼承而來的值,這和java的基礎(chǔ)有點像,子類中找不到值,就去父類中找,發(fā)現(xiàn)父類中有這個值,就直接取來用,這里的隱式操作可以理解成這樣,childCtrl.firstName = parentCtrl.firstName;
但是有一個問題,當(dāng)我們點擊按鈕,修改firstName的值時,發(fā)現(xiàn),只有子控制器中的值改了,父控制器內(nèi)的沒有變,這就是變量值的繼承
2.對象在上下級作用域之間共享
<div ng-app="myApp" ng-controller="parentCtrl">
<p>{{data.firstName}}</p>
<div ng-controller="childCtrl">
<p>{{data.firstName}}</p>
<button ng-click="update()">點擊修改</button>
</div>
</div>
//js
app.controller('parentCtrl', function($scope) {
// $scope.firstName = "John";
$scope.data = {
firstName:"John"
}
});
app.controller('childCtrl', function($scope) {
$scope.update = function() {
//$scope.firstName = $scope.firstName+"zzz";
$scope.data.firstName = $scope.data.firstName+"zzz";
}
});
這次結(jié)果就不一樣了,再點擊修改,上下級的變量值都會改變。因為兩者的data對象是一個引用,對下級對象上值的修改可以反映到兩級對象上。
3.$parent指定父級作用域
在子父級之間有變量繼承時,很容易產(chǎn)生歧義,所以最好是指定不同的變量名,避免歧義,如果確實要使用“變量繼承”,可以使用$parent,它是$scope的一個屬性,可以用來指定父級作用域,使得子級作用域修改繼承的變量時,可以同步映射到父級。
//修改子級作用域中的變量名,添加$parent來指定修改的值
<div ng-app="myApp" ng-controller="parentCtrl">
<p>{{firstName}}</p>
<div ng-controller="childCtrl">
<p>{{firstName}}</p>
<button ng-click="update()">點擊修改</button>
</div>
</div>
//js
app.controller('parentCtrl', function($scope) {
$scope.firstName = "John";
});
app.controller('childCtrl', function($scope) {
$scope.update = function() {
$scope.$parent.firstName = firstName+"zzz";
}
});
一些命令也會創(chuàng)建作用域
在AngularJS中,不止ng-controller會創(chuàng)建作用域,一些命令也會,如果不清楚作用域的關(guān)系,很容易出錯。
ng-repeat
ng-repeat會創(chuàng)建自己的作用域其實很容易理解,ng-repeat范圍內(nèi)的每一項都是獨立存在的,如下:
<div ng-app="myApp" ng-controller="myCtrl">
{{sum}}
<ul>
<li ng-repeat="item in arr">
{{item}}
<button ng-click="item = item + 1">incerase</button>
</li>
</ul>
</div>
//js
app.controller('myCtrl', function($scope) {
$scope.sum = 2;
$scope.arr = [1,2,3];
});
上面這個例子,每個li里的item都是獨立的,運行之后我們可以發(fā)現(xiàn),雖然名字都一樣,但是它們之間互不影響,這是因為它們各自有自己的作用域,我們可以再驗證一下,如果在li標(biāo)簽內(nèi)部改變sum的值會出現(xiàn)什么情況。
<div ng-app="myApp" ng-controller="myCtrl">
{{sum}}
<ul>
<li ng-repeat="item in arr">
{{item}}
<button ng-click="sum = sum + 1">incerase</button>
{{sum}}
</li>
</ul>
</div>
我們可以發(fā)現(xiàn),li內(nèi)部的sum的值改變了,但是列表外顯示的sum值沒有變化,這就是我們上面提到的,變量在作用域之間可繼承,因為ng-repeat創(chuàng)建了自己的作用域,它使用的sum其實是繼承自mgCtrl的,所以下級控制器改變sum的值不會影響上級的值。
如果想要改變外部的值,可以用$parent來指定上級sum的變化,這樣一來,所有顯示sum的地方都會變了,因為下級的sum都繼承自上級。
<div ng-app="myApp" ng-controller="myCtrl">
{{sum}}
<ul>
<li ng-repeat="item in arr">
{{item}}
<button ng-click="$parent.sum = sum + 1">incerase</button>
{{sum}}
</li>
</ul>
</div>
之前的學(xué)習(xí)中一直覺得作用域和控制器時一樣的,從上面的例子可以發(fā)現(xiàn),兩者還是不同,上面的作用域可以說是兩級控制于,但是它們都處于一個控制器中。
ng-if
在剛接觸AngularJS的時候,會接觸到ng-show和ng-hide,這兩個很好理解,就是滿足條件的話就顯示/隱藏,同樣功能的還有ng-if,而二者的區(qū)別在于,ng-show和ng-hide的區(qū)域,原來就有,只是控制其是否顯示,而ng-if是一開始沒有,滿足條件的話,才在DOM樹中加入這塊內(nèi)容,我們可以在瀏覽器中用調(diào)試工具看到這個變化。
另外,它們還有一個區(qū)別,ng-if創(chuàng)建DOM的時候會為這塊區(qū)域創(chuàng)建作用域,這樣在使用的時候兩者就有差別了,如果要訪問外部的變量,就要像ng-repeat一樣使用$parent了。
Ionic的問題
現(xiàn)在回到我們最初的問題?在Ionic框架中,有時候在使用ng-model在視圖中綁定model之后,在controller中獲取不到值,但是,如果在controller中定義了這個model的值,視圖中是可以顯示出來的,這是不是和上面提到的變量在作用域之間可繼承很像呢?下級可以獲取上級的,而上級獲取不到下級的值。也就是說,ionic的某個命令創(chuàng)建了自己的作用域,在我們顯示定義的作用域的下級。
一般情況下,我們會為一個模板文件綁定一個控制器,控制器自然會創(chuàng)建一個作用域,而這個默認(rèn)文件的最上層是 ion-view命令,內(nèi)容寫在ion-content中,那是不是ion-content創(chuàng)建了作用域呢,我們可以這樣來證明:
<ion-view ng-controller="myCtrl">
<ion-header-bar align-title="center" class="bar-stable">
<div class="buttons">
<button class="button button-clear button-positive" ng-disabled="!$parent.ontent || $parent.content === ''" ng-click="publish()">
發(fā)表
</button>
</div>
</ion-header-bar>
<ion-content>
<div class="item-input textarea-input">
<textarea rows="4" ng-model="$parent.content" placeholder="說點什么吧..."></textarea>
</div>
</ion-content>
</ion-view>
app.controller('PublishRcdCtrl', ['$scope', function($scope) {
$scope.publish = function() {
console.log($scope.content);
}
}])
上面這個例子,當(dāng)我在textarea中綁定的是content的時候,點擊ion-header-bar里的按鈕是不會打印出輸入的內(nèi)容的,所以我在輸入框上綁定了$parent.content,然后按鈕上的ng-disabled命令綁定的也是$parent.content,這時,不管輸入框中有沒有內(nèi)容,按鈕都顯示被禁用,然后我把ng-disable命令綁定到content上,按鈕就可以正常使用了。兩個地方使用content變量的獲取方式不同,這也說明了是ion-content指令創(chuàng)建了作用域,這也說明了為什么我能在ion-footer-bar中正常進行數(shù)據(jù)雙向綁定了,因為它不會創(chuàng)建作用域。
解決方法,來自上面的說明,可以使用$parent,也可以上下級作用域共享對象,也有的方法說的是,顯示指定ng-controller,我試過這個方法,但是沒有生效,可能是使用環(huán)境不對,大家都可以試一試。
總結(jié)
果然知識沒學(xué)透,很多麻煩都會自己找來的,這里推薦一篇 大神的博客 ,我也是看了別人寫的文章之后才懂的,加上了自己的理解。好了,就到這里了,遇到問題再更。