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