Ionic中的$scope的作用域問題(結(jié)合AngularJS的$scope作用域)

最近都在閑,好不容易可以輕松一下,就放心的玩了一個月。現(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-showng-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é)透,很多麻煩都會自己找來的,這里推薦一篇 大神的博客 ,我也是看了別人寫的文章之后才懂的,加上了自己的理解。好了,就到這里了,遇到問題再更。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • AngularJS 常見面試問題 1. ng-if 跟 ng-show/hide 的區(qū)別有哪些? ng-if在后面...
    darr250閱讀 2,763評論 0 11
  • 2013年8月2日, 嚴(yán)清 譯譯文:理解AngularJS的作用域Scope原文:Understanding Sc...
    竿牘閱讀 423評論 0 0
  • 個人博客搭建完成,歡迎大家來訪問哦黎默丶lymoo的博客 博主最近了解學(xué)習(xí)了一下angularJS,記錄一下心得給...
    黎默丶lymoo閱讀 1,099評論 0 21
  • AngularJS是什么?AngularJs(后面就簡稱ng了)是一個用于設(shè)計動態(tài)web應(yīng)用的結(jié)構(gòu)框架。首先,它是...
    200813閱讀 1,760評論 0 3
  • 翻譯自:Understanding Scopes 摘要 在AngularJS中,子作用域通常會原型繼承于其父作用域...
    paradisefj閱讀 1,123評論 0 5

友情鏈接更多精彩內(nèi)容