杀手锏 - 在控制器中显式地声明数据模型

代码拓虹客
• 阅读 3196
在这篇文章中,我们将会研究一下,在控制器中显式地声明数据模型对我们的编程都有哪些好处,在这个过程中有哪些需要我们注意的地方;好了,废话不多说,进入主题吧!

需要知道的知识

ng-controller 是一个特殊的指令,它会以父级作用域(一般为$rootScope或者另外一个ng-controller的作用域)为原型生成一个子作用域;这种继承的机制可以创建一个隔离层,用来将需要协同工作的方法和数据模型对象放置在一起。

JavaScript 的对象,要么是值复制要么是引用复制。字符串,数字,布尔型变量都是值复制;数组,对象,函数都是引用复制。

在控制器中使用裸值

在控制器中,我们不推荐使用裸值形式为一个变量赋值,但是为了说明这个问题,我们尝试使用这种方法去编写我们的程序。

我们创建两个控制器,一个父控制器,一个子控制器;在父控制器和子控制器中分别使用父控制器中的数据,然后在父控制器和子控制器中分别修改我们使用的数据,看看会发生什么?

代码如下所示

HTML代码:

html<div ng-app="MyApp">
    <div ng-controller="ParentController">
        <h3>Parent Section: {{name}}</h3><br/>
        <button ng-click="parentChangeName()">parentChangeName</button>
        <div ng-controller="ChildController">
            <h3>Child Section: {{name}}</h3><br/>
            <button ng-click="childChangeName()">childChangeName</button>
        </div>
    </div>
</div>

JS代码:

javascriptangular.module("MyApp", [])
.controller("ParentController", function($scope){
    $scope.name = "dreamapple";
    $scope.parentChangeName = function(){
        $scope.name = "parent dreamapple";
    }
})
.controller("ChildController", function($scope){
    $scope.childChangeName = function(){
        $scope.name = "child dreamapple";
    }
});

Online Code Part1

关于上面的代码,我们还是要好好地分析一下:

首先它的表现结果是这样的:
(1)假如我们刚开始点击的是parentChangeName按钮,那么,父控制器和子控制器的h3标签中的{{name}}值都将是parent dreamapple;然后我们点击childChangeName按钮,会发现,只有子控制器的h3标签中的{{name}}值发生了变化,变成了child dreamapple;父控制器里面的{{name}}没有发生变化。
(2)假如我们刚开始的时候先点击的是childChangeName按钮,那么结果与刚才的不一样,只有子控制器的h3标签中的{{name}}值发生了变化,变成了child dreamapple;父控制器里面的{{name}}没有发生变化。然后当我们点击parentChangeName按钮时也只有父控制器里面的{{name}}的值发生了变化。子控制器也没有发生变化。

因为是这样的,由于原型继承的关系,初始化状态下,我们的子控制器虽然有自己的作用域,但是里面的属性值和方法,与父控制器的是一样的;所以,在子控制器有任何操作之前,子控制器里面的属性值与父控制器里面是一样的,所以当点击parentChangeName按钮时,父控制器里面的属性值发生变化,所以子控制器里面的属性值也发生了变化。但是,当我们再次点击childChangeName按钮时,这时候子控制器因为有自己的作用域,所以只能够修改自己作用域里的属性值,无法修改父控制器作用域里面的属性值。

第二种情况也很好解释,因为一开始,点击childChangeName按钮就修改了子作用域的属性值,虽然我们紧接着点击了parentChangeName按钮但是却没有改变子作用域里面的属性值,因为点击parentChangeName按钮只能在初始化的时候(并且子作用域中没有对那个属性进行初始化Online Code Part2),改变父控制器和子控制器的属性值,如果子控制器里面的属性已经被修改或者初始化了,那么父控制器也不可以改变子控制器里面的属性值。

在控制器中显式声明数据模型

我们将修改上面的代码,不再使用裸值,而是显式的声明一个对象。

代码部分如下

HTML代码:

html<div ng-app="MyApp">
    <div ng-controller="ParentController">
        <h3>Parent Section: {{person.name}}</h3><br/>
        <button ng-click="parentChangeName()">parentChangeName</button>
        <div ng-controller="ChildController">
            <h3>Child Section: {{person.name}}</h3><br/>
            <button ng-click="childChangeName()">childChangeName</button>
        </div>
    </div>
</div>

JS代码:

javascriptangular.module("MyApp", [])
.controller("ParentController", function($scope){
    // $scope.name = "dreamapple";
    $scope.person = {
        name: "dreamapple"
    };
    $scope.parentChangeName = function(){
        // $scope.name = "parent dreamapple";
        $scope.person.name = "parent dreamapple";
    }
})
.controller("ChildController", function($scope){
    $scope.childChangeName = function(){
        // $scope.name = "child dreamapple";
        $scope.person.name = "child dreamapple";
    }
});

Online Code Part3

我们来看看它的表现结果:
这时你会发现,不论是点击parentChangeName按钮还是点击childChangeName按钮,父控制器和子控制器里面的{{name}}都会发生变化,这是为什么呢?
因为,首先我们在父作用域上创建了一个对象也就是一个数据模型,子控制器上也会有对这个对象的引用,所以无论我们改变哪一个控制器里这个对象的属性,都会反应到这两个控制器里面。

到这里,我想要和大家分享的差不多快结束了,不过还有一点需要注意的,就是使用任何会创建子作用域指令时,如果将指令定义中的scope设置为true,都会有上面的特性,我们可以根据自己的需要,采取不同的方法;好了,差不多该睡午觉了,还有,如果大家觉得我哪里说得不对,还望指出,我们一起进步。

点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
STM32 中断详解
中断,在单片机中占有非常重要的地位。代码默认地从上向下执行,遇到条件或者其他语句,会按照指定的地方跳转。而在单片机执行代码的过程中,难免会有一些突发的情况需要处理,这样就会打断当前的代码,待处理完突发情况之后,程序会回到被打断的地方继续执行。1EXTI控制器外部中断/事件控制器(EXTI)管理了控制器的23个中断/事件线。每
疯震震 疯震震
4年前
IntentFilter匹配规则
1、Activity启动方式我们知道,启动Activity分为两种,显式调用和隐式调用。显式调用需要明确指定被启动对象的组件信息,包括包名和类名。隐式调用不需要指定组件信息,但需要匹配目标组件的IntentFilter中所设置的过滤信息。2、IntentFilterIntentFilter中的过滤信息包括:action、category、data。为了匹配
Souleigh ✨ Souleigh ✨
4年前
【C 陷阱与缺陷】(二)语法陷阱
0.理解函数声明请思考下面语句的含义:((void()())0)()前面我们说过C语言的声明包含两个部分:类型和类似表达式的声明符。最简单的声明符就是单个变量:floatf,g;由于声明符和表达式的相似,我们可以在声明符中任意使用括号:float((f));这个声明的含义是:当对f求值时,(
Wesley13 Wesley13
3年前
Java中System.loadLibrary() 的执行过程
_System.loadLibrary()_是我们在使用Java的JNI机制时,会用到的一个非常重要的函数,它的作用即是把实现了我们在Javacode中声明的native方法的那个libraryload进来,或者load其他什么动态连接库。算是处于好奇吧,我们可以看一下这个方法它的实现,即执行流程。(下面分析的那些code,来自于android4.2
Wesley13 Wesley13
3年前
Unity优化之
当我们来创建一个对象、字符串或数组时,我们需要从称为堆的中央池中为其分配内存来存储它。当它不再被使用时,我们又需要来释放这块内存便于重复使用。在以前这个过程通常需要我们通过适当的函数调用显式地分配和释放块内存来实现。但现在,运行时系统如Unity的mono引擎将自动地为我们管理内存。自动内存管理比显式分配/释放需要更少的编码工作,大大减少了内存泄漏的可能性(
Stella981 Stella981
3年前
Kafka生产者哪些重要的参数是我们需要注意的?
在KafkaProducer中大部分的参数都有合理的默认值,一般不需要修改它们。不过了解这些参数可以让我们更合理地使用生产者客户端,其中还有一些重要的参数涉及程序的可用性和性能,如果能够熟练掌握它们,也可以让我们在编写相关的程序时能够更好地进行性能调优与故障排查。下面挑选一些重要的参数进行讲解。1\.acks这个参数用来指定分区中必须要
Stella981 Stella981
3年前
Android内存分配的注意事项
在Android(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fwww.51code.com%2F)开发中,要时刻注意内存的分配和垃圾回收,因为系统为每一个dalvik虚拟机分配的内存是有限的。这样就需要我们在开发的过程中时刻注意,不要因为自己的代码问题造成OOM。Android应用
Easter79 Easter79
3年前
Spring之导入和混合配置
  在典型的Spring应用中,我们可能会同时使用自动化和显式配置。即便你更喜欢通过JavaConfig实现显式配置,但有的时候XML却是最佳的方案。幸好在Spring中,这些配置方案都不是互斥的。你尽可以将JavaConfig的组件扫描和自动装配和/或XML配置混合在一起。实际上,就像在2.2.1小节中所看到的,我们至少需要有一点显式配置来启用组件扫描和自
Stella981 Stella981
3年前
PowerDesigner列名、注释内容互换
在用PowerDesigner时,常常在NAME或Comment中写中文在Code中写英文,Name只会显示给我们看,Code会使用在代码中,但Comment中的文字会保存到数据库TABLE的Description中,有时候我们写好了Name再写一次Comment很麻烦,以下两段代码就可以解决这个问题。在PowerDesigner中PowerDesig
Easter79 Easter79
3年前
SwiftUI 跨组件数据传递
作者:Cyandev,iOS和MacOS开发者,目前就职于字节跳动0x00前言众所周知,SwiftUI的开发模式与React、Flutter非常相似,即都是声明式UI,由数据驱动(产生)视图,视图也会与数据自动保持同步,框架层会帮你处理“绑定”的问题。在声明式UI中不存在命令式地让一个视图变成xxx
Stella981 Stella981
3年前
JavaScript 声明全局变量的三种方式
JS中声明全局变量主要分为显式声明或者隐式声明下面分别介绍。声明方式一:使用var(关键字)变量名(标识符)的方式在function外部声明,即为全局变量,否则在function声明的是局部变量。声明方式二:没有使用var,直接给标识符e赋值,这样会隐式的声明了全局变量e。即使该语句是在一个function内,当该funct