材质升级后AngularJS自定义指令双向绑定中断

时间:2017-03-10 20:37:00

标签: javascript angularjs angular-material angular-ngmodel angular-directive

我写了一个在Angular 1.6.1和材料1.1.1下工作的小指令。 这是一个简单的锁定/解锁按钮图标。

我必须将材料更新为1.1.3(日期选择器),但从那时起该指令不再起作用。

我不明白为什么材料更新会这样做.... 下面的plunker工作,但如果你将材料版本更改为1.1.2,它将停止工作。

http://plnkr.co/edit/ZamxN3WTXaOl5cTv4aWI?p=info

的index.html:

<html lang="en" >
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/angular-material/1.1.3/angular-material.css">
  <link rel="stylesheet" href="//fonts.googleapis.com/icon?family=Material+Icons">
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular-animate.js"></script>
  <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular-aria.js"></script>

  <script src="//cdnjs.cloudflare.com/ajax/libs/angular-material/1.1.1/angular-material.js"></script>
  <!-- <script src="//cdnjs.cloudflare.com/ajax/libs/angular-material/1.1.1/angular-material.js"></script> -->

  <script src="script.js"></script>

</head>
<body ng-app="app" ng-controller="controller as ctrl">
  <ju-lock ng-model="ctrl.lock"></ju-lock>|{{ctrl.lock}}|
</body>
</html>

的script.js:

  angular
    .module('app', ['ngMaterial'])
    .directive('juLock', function(){
        return {
            restrict: 'E',
            scope: {
                bindModel: '=ngModel'
            },
            template: 
                '<md-button class="md-icon-button">'+
                '<md-icon class="material-icons">lock_open</md-icon>'+
                '</md-button>|{{bindModel}}',
            link: function(scope, element, attributes){
                element.on('click', function (ev) {
                    scope.bindModel = !scope.bindModel;
                });
                scope.$watch('bindModel', function(){
                    angular.element(element[0].querySelector('.material-icons')).text(scope.bindModel ? 'lock' : 'lock_open');
                });
            }
        };
    })
    .controller('controller', function(){
        var vm = this;
        vm.lock=true;
    });

在询问堆栈社区之前,我已经尽可能多地进行了调查,是否有人对此有所了解?

2 个答案:

答案 0 :(得分:2)

避免在自定义指令中使用ng-model作为属性。如果您这样做,请不要使用隔离范围双向绑定。使用该属性实例化的ngModelController API

主要问题是jqLite click handler需要通过scope().$apply()通知AngularJS框架对范围的更改:

element.on('click', function (ev) {
    scope.bindModel = !scope.bindModel;
    //USE $apply
    scope.$apply();
});
  

Angular通过提供自己的事件处理循环来修改正常的JavaScript流。这将JavaScript拆分为经典和Angular执行上下文。只有在Angular执行上下文中应用的操作才会受益于Angular数据绑定,异常处理,属性监视等...您可以使用$apply()从JavaScript输入Angular执行上下文。

     

请记住,在大多数地方(控制器,服务)$apply已由处理事件的指令调用。只有在实施自定义事件回调或使用第三方库回调时才需要显式调用$apply

     

     

— AngularJS Developer Guide v1.1 - Concepts - Runtime

DEMO on PLNKR

而不是操纵DOM来改变锁定和解锁图标。它可以使用ng-showng-hide指令完成:

app.directive('juLock', function(){
    return {
        restrict: 'E',
        scope: {
            bindModel: '=myModel'
        },
        template: 
            '<md-button class="md-icon-button">'+
            '<md-icon ng-show="bindModel" class="material-icons">lock</md-icon>'+
            '<md-icon ng-hide="bindModel" class="material-icons">lock_open</md-icon>'+
            '</md-button>|{{bindModel}}',
        link: function(scope, element, attributes){
            element.on('click', function (ev) {
                scope.bindModel = !scope.bindModel;
                //USE $apply
                scope.$apply();
            });
            /*
            scope.$watch('bindModel', function(){
                angular.element(element[0].querySelector('.material-icons')).text(scope.bindModel ? 'lock' : 'lock_open');
            });
            */
        }
    };
})

答案 1 :(得分:1)

我不确定您在版本中的确切问题,但请查看此fiddle,此代码适用于这两个版本。

关键点是使用来自angularJs的ng-click,而不是依赖element.on(),如果您注入jQuery或不注意.directive('juLock', function(){ return { restrict: 'E', scope: { bindModel: '=ngModel' }, template: '<md-button ng-click="bindModel = !bindModel" class="md-icon-button">'+ '<md-icon class="material-icons">{{lock_text}}</md-icon>'+ '</md-button>|{{bindModel}}', link: function(scope, element, attributes){ scope.lock_text = ''; scope.$watch('bindModel', function(){ scope.lock_text = scope.bindModel ? 'lock' : 'lock_open'; }); } }; }) ,则可能会有所不同。加上这种方式更具说明性

我做的唯一改变是你的指令

<ExcludeFoldersFromDeployment>App_Plugins\UmbracoForms\Data</ExcludeFoldersFromDeployment>

我还在你的手表中删除了dom检查,我在Angular-Style

中做了更多