最近都在闲,好不容易可以轻松一下,就放心的玩了一个月。现在到公司实习了,正好接着做之前没有做完的事情。在使用Ionic框架的过程中还是遇到一些问题,就此记录一下。
关于Ionic框架,之前也写了一篇,记录的是遇到的一些小问题,这次这篇,和AngularJS有关。我们知道Ionic框架是基于AngularJS的,由AngularJS进行路由控制,数据交互,而Ionic只相当于一个UI框架。所以如果有AngularJS基础,学习这个框架其实不难。但是其中还是有一些不一样的地方,这篇要说的,就是Ionic中$scope的作用域问题。
问题描述
在Ionic应用中,HTML页面中ng-model绑定的数据,无法在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的控制域,之前学习的时候只是简单的知道controller之间可以是平级关系,也可以是子父级关系,这取决于controller控制的html区域的关系,是否嵌套,所以对应的$scope的作用域也是可以嵌套的。
AngularJS的根作用域
每个AngularJS应用都有一个默认的根作用域$rootScope,如果没有指定控制器,那么ng-model绑定的数据就会自动绑定到$rootScope中。如果想要在controller中使用$rootScope绑定的数据,需要像$scope一样,注入到控制器中。
AngularJS中作用域的继承关系
作用域的继承是刚接触AngularJS的时候就了解到的内容了,但是一直没有深入了解,现在才回遇到这个问题。在html页面中的嵌套关系中,绑定上对应的controller,controller也就嵌套,这样一来,对应的作用域就有了继承关系。
如下:
<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有了子父级关系,那么,哪些内容是可以继承的,哪些内容是共享的呢?
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管辖范围内的还是显示出了这个变量值,就是从parentCtrl中继承而来的值,这和java的基础有点像,子类中找不到值,就去父类中找,发现父类中有这个值,就直接取来用,这里的隐式操作可以理解成这样,childCtrl.firstName = parentCtrl.firstName;
但是有一个问题,当我们点击按钮,修改firstName的值时,发现,只有子控制器中的值改了,父控制器内的没有变,这就是变量值的继承
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";
}
});
这次结果就不一样了,再点击修改,上下级的变量值都会改变。因为两者的data对象是一个引用,对下级对象上值的修改可以反映到两级对象上。
3.$parent指定父级作用域
在子父级之间有变量继承时,很容易产生歧义,所以最好是指定不同的变量名,避免歧义,如果确实要使用“变量继承”,可以使用$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";
}
});
一些命令也会创建作用域
在AngularJS中,不止ng-controller会创建作用域,一些命令也会,如果不清楚作用域的关系,很容易出错。
ng-repeat
ng-repeat会创建自己的作用域其实很容易理解,ng-repeat范围内的每一项都是独立存在的,如下:
<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都是独立的,运行之后我们可以发现,虽然名字都一样,但是它们之间互不影响,这是因为它们各自有自己的作用域,我们可以再验证一下,如果在li标签内部改变sum的值会出现什么情况。
<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>
我们可以发现,li内部的sum的值改变了,但是列表外显示的sum值没有变化,这就是我们上面提到的,变量在作用域之间可继承,因为ng-repeat
创建了自己的作用域,它使用的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>
之前的学习中一直觉得作用域和控制器时一样的,从上面的例子可以发现,两者还是不同,上面的作用域可以说是两级控制于,但是它们都处于一个控制器中。
ng-if
在刚接触AngularJS的时候,会接触到ng-show
和ng-hide
,这两个很好理解,就是满足条件的话就显示/隐藏,同样功能的还有ng-if,而二者的区别在于,ng-show和ng-hide的区域,原来就有,只是控制其是否显示,而ng-if是一开始没有,满足条件的话,才在DOM树中加入这块内容,我们可以在浏览器中用调试工具看到这个变化。
另外,它们还有一个区别,ng-if创建DOM的时候会为这块区域创建作用域,这样在使用的时候两者就有差别了,如果要访问外部的变量,就要像ng-repeat一样使用$parent了。
Ionic的问题
现在回到我们最初的问题?在Ionic框架中,有时候在使用ng-model在视图中绑定model之后,在controller中获取不到值,但是,如果在controller中定义了这个model的值,视图中是可以显示出来的,这是不是和上面提到的变量在作用域之间可继承
很像呢?下级可以获取上级的,而上级获取不到下级的值。也就是说,ionic的某个命令创建了自己的作用域,在我们显示定义的作用域的下级。
一般情况下,我们会为一个模板文件绑定一个控制器,控制器自然会创建一个作用域,而这个默认文件的最上层是 ion-view
命令,内容写在ion-content
中,那是不是ion-content
创建了作用域呢,我们可以这样来证明:
<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()">
发表
</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);
}
}])
上面这个例子,当我在textarea
中绑定的是content
的时候,点击ion-header-bar
里的按钮是不会打印出输入的内容的,所以我在输入框上绑定了$parent.content
,然后按钮上的ng-disabled
命令绑定的也是$parent.content
,这时,不管输入框中有没有内容,按钮都显示被禁用,然后我把ng-disable
命令绑定到content上,按钮就可以正常使用了。两个地方使用content变量的获取方式不同,这也说明了是ion-content
指令创建了作用域,这也说明了为什么我能在ion-footer-bar
中正常进行数据双向绑定了,因为它不会创建作用域。
解决方法,来自上面的说明,可以使用$parent,也可以上下级作用域共享对象,也有的方法说的是,显示指定ng-controller,我试过这个方法,但是没有生效,可能是使用环境不对,大家都可以试一试。
总结
果然知识没学透,很多麻烦都会自己找来的,这里推荐一篇 大神的博客 ,我也是看了别人写的文章之后才懂的,加上了自己的理解。好了,就到这里了,遇到问题再更。