在angular1中自定义directive时数据显示不同步问题的分析和解决

在angular1项目中,有的时候我们需要写自定义directive来完成某些应用逻辑,但是如果你有过一些的angular1项目的开发经验,肯定会因为一些莫名其妙数据双向绑定,回调函数无法调用执行等问题,困扰过。下面是一个简单的案例。

问题引出

这里我们写一个简单的directive来说明问题。

directive.js


angular.module('app.directive')
.directive('ayTest',function(){
    return {
        restrict: 'EA',
        scope: {
            name:'='
        },
        template:`
            <div class="padding mystyle">
                {{vm.name}}
            </div>
        `,
        controller:function($scope){
            var vm = $scope.vm = {};
            vm.name = $scope.name;

        }
    }

});

A.controller.js

    var vm = $scope.vm = {};
    vm.name = 'Tom1';
    $timeout(function(){
        vm.name = 'Tom2';
    },1000);
    $timeout(function(){
        vm.name = 'Tom3';
    },2000);

A.html

   <ay-test name="vm.name"></ay-test>

这个directive可以实现以我们自己的格式展示‘name’的功能,我们可以通过在template中填写自己独有的style或者嵌套div来完善name展示模块。

在这我们希望name能够实现双向绑定,即当在A页面中name值发生变化的时候,directive也自动获得更改。

但是,在这里并不能实现这个功能,当name显示为Tom1之后,就不会再变化了。

为什么会这样呢?

我们都知道自定义directive时,有3个绑定符号,分别是

  • @ 单向绑定属性值
  • = 双向绑定属性
  • & 绑定用户函数

这里为了实现信息的双向绑定,已经使用了=,为什么还无法完成双向绑定呢?对于大神们,当然能一眼看出问题的所在,但小白可能会摸索比较长的时间~~~~

问题出在vm.name = $scope.name;这句代码上。因为name是一个基本字符串,赋值时,相当于对vm.name创建了一个新值,并赋值为$scope.name的值。这样,虽然controller中的$scope.name和页面上的{{name}}仍存在双向绑定,但是和我们的vm.name却无任何关系了。

我们应该还知道下面的这个问题:

//controller.js
$socpe.username = 'Mary';
//B.html
<input ng-model='username' />

如果你按照上面的方式来编码,十有八九会出现意外情况。

出现这样错误的根本原因,其实还是对JavaScript变量的值访问、引用访问,开发中的值传递、引用传递没有理解清楚。下面说一些这方面的问题。

Js基本类型、值传递

下面是笔者摘自《JavaScript高级程序设计》中的几段话,读者可以参照理解。这里并不是笔者自己懒,而书中的表达更准确,而且也很容易理解~~(如果你说不知道这本书,推荐快去读一下,如果你做Js相关开发,会让你受益匪浅,我能给你电子版,联系hao5743@163.com )

5种基本数据类型:Undefined、Null、Boolean、Number和String,5种基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值。

引用类型的值是保存在内存中的对象。于其他语言不同,JavaScript不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。引用类型的值是按引用访问的。

--参见《JavaScript高级程序设计》P69 4.1 基本类型和引用类型的值

如果从一个变量向另一个变量复制基本类型的值,会在变量对象上创建一个新值,然后把该值复制到为新变量分配的位置上。

--参见《JavaScript高级程序设计》P69 4.1.2 复制变量值

ECMAScript中所有函数的参数都是按值传递的。也就是说,把函数外部的值赋值给函数内部的参数,就和把值从一个变量复制到另一个变量一样。基本类型值的传递如同基本类型变量的复制一样,二引用类型值的传递,则如同引用类型变量的复制一样。有不少开发人员在这一点可能会感到困惑,因为访问变量有按值和按引用两种方式,而参数只能按值传递。

--参见《JavaScript高级程序设计》P70 4.1.3 传递参数

解决方案

理解了上面的话,那么你就能轻易用不同的方案解决这个问题了。

方案1 直接使用$scope.name展示数据

directive.js


angular.module('app.directive')
.directive('ayTest',function(){
    return {
        restrict: 'EA',
        scope: {
            name:'='
        },
        //这里直接使用name,不再使用vm.name
        template:`
            <div class="padding mystyle">
                {{name}}
            </div>
        `,
        controller:function($scope){
            var vm = $scope.vm = {};

        }
    }

});

不再使用vm的方式,而是直接绑定到页面,可以解决问题。

方案2 使用引用类型打包基本类型再传递

如果,你在angular1开发中,一直遵循了好的实践,那么有可能vm.name你可能已经使用习惯了。也许你会问,如果我还想使用vm.name的方式,我想遵循官方给出的最佳实践,怎么解决这个问题呢?

(什么是最佳实践?如果你是angular开发者,而还不知道的话,那么一定要去读读,原版在这里,英文差的看这里

当然有方法啦!看下面
controller.js

    var vm = $scope.vm = {};
    vm.name = {};
    //将数据封装到到内部属性data上
    vm.name.data = 'Tom1';
    $timeout(function(){
        vm.name.data = 'Tom2';
    },1000);
    $timeout(function(){
        vm.name.data = 'Tom3';
    },2000);

A.html

    <ay-test name="vm.name"></ay-test>

directive.js

.directive('ayTest',function(){
    return {
        restrict: 'EA',
        scope: {
            name:'='
        },
        template:`
            <div class="padding">
                {{vm.name.data}}
            </div>
        `,
        controller:function($scope){
            var vm = $scope.vm = {};
            //注意,虽然这里重新赋值了name,但是我们的vm.name.data还是同一份,所以仍能实现绑定
            vm.name = $scope.name;

        }
    }

});

方案3 使用$scope.$watch

如果你知道$scope.$watch,并且了解何时使用它,那么你应该知道这里也可能使用它来解决。看代码。

controller.js

    var vm = $scope.vm = {};
    //并没有进行数据封装
    vm.name = 'Tom1';
    $timeout(function(){
        vm.name = 'Tom2';
    },1000);
    $timeout(function(){
        vm.name = 'Tom3';
    },2000);

A.html

    <ay-test name="vm.name"></ay-test>

directive.js

angular.module('app.directive')
.directive('ayTest',function(){
    return {
        restrict: 'EA',
        scope: {
            name:'='
        },
        template:`
            <div class="padding">
                {{vm.name}}
            </div>
        `,
        controller:function($scope){
            var vm = $scope.vm = {};
            //添加数据监测
            $scope.$watch('name',function(newValue,oldValue){
                vm.name = $scope.name;
            });

        }
    }
});

这个方法其实相当于多加了一层的监测,vm.name和页面上的{{vm.name}}绑定,通过$scope.$watch,我们让vm.name$scope.name两个变量的值保持一致性。

哪个方法好呢

个人推荐第一种方法,因为他所做的工作最少,效率相对较高(虽然高不了很多)。

最后的话

本文首先提出了在编写自定义directive时有时候会出现的数据无法同步的问题。随后分析了问题,并给出了几种解决方案。

其实,问题的根本首先在于对JavaScript变量值访问引用访问值传递等概念的理解,其次在于对angular1scope层级双向绑定脏值检测等概念的理解。

有时,还能需要处理其他情况,比如默认值处理,分条件展示,错误值处理、数据回显、回调函数处理等具体问题,可以参考上面3种思路来具体实现。

很简单一个问题,个人只是做了一下分析,有错误的地方请大神及时指出,感激不尽~~

Author:shaochong

Email:hao5743@163.com

文章地址:https://hao5743.github.io/2016/12/05/Data-bind-problem-in-Angular-when-define-your-own-directives/

博客:http://hao5743.github.io/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容