AngularJS 自定义指令两种方式绑定

Posted

技术标签:

【中文标题】AngularJS 自定义指令两种方式绑定【英文标题】:AngularJS Custom Directive Two Way Binding 【发布时间】:2013-09-23 19:33:11 【问题描述】:

如果我有一个没有模板的 AngularJS 指令,并且我希望它在当前范围内设置一个属性,那么最好的方法是什么?

例如,计算按钮点击次数的指令:

<button twoway="counter">Click Me</button>
<p>Click Count:  counter </p>

使用指令将点击计数分配给双向属性中的表达式:

.directive('twoway', [
'$parse',
  function($parse) 
    return 
      scope: false,
      link: function(scope, elem, attrs) 
        elem.on('click', function() 
          var current = scope.$eval(attrs.twoway) || 0;
          $parse(attrs.twoway).assign(scope, ++current);
          scope.$apply();
        );
      
    ;
  
])

有没有更好的方法来做到这一点?从我读过的内容来看,一个孤立的范围会过大,但我需要一个子范围吗?除了使用$parse之外,还有没有更简洁的方法可以写回指令属性中定义的范围变量。我只是觉得这太难了。

完整的 Plunker here.

【问题讨论】:

这个问题似乎跑题了,因为它是关于代码审查的。 或者代码架构。我认为这是一个很好的问题。 【参考方案1】:

将模板更改为:

<button twoway bind="counter">Click Me</button>
<p>Click Count:  counter.val </p>

和指令:

.directive('twoway',
    function() 
        return 
            scope: 
                localValue: '=?bind'
            ,
            link: function(scope, elem, attrs) 
                scope.localValue = 
                    val: 0
                ;
                elem.on('click', function() 
                    scope.localValue.val = scope.localValue.val + 1;
                    scope.$apply();
                );
            
        ;
    
);

【讨论】:

你能解释一下这个问题的答案吗? 虽然此代码可能会回答问题,但提供有关它如何和/或为什么解决问题的额外上下文将提高​​答案的长期价值。【参考方案2】:

应用双向绑定的一个好方法是使用指令组件。这是我的解决方案。它允许使用 ng-repeat 和可扩展的数据绑定。

View Plunker

HTML

<body ng-controller='MainCtrl'>  
    Data: data
    <hr>
    <mydirective name='data[0]'></mydirective>
    <hr>
    <mydirective name='data[1]'></mydirective>
</body>

控制器

app.controller('MainCtrl', function($scope) 
  $scope.data = [];
  $scope.data[0] = 'Marco';
  $scope.data[1] = 'Billy';
);

指令

app.directive("mydirective", function()
    return 
        restrict: "EA",
        scope: name: '=',
        template: "<div>Your name is : name</div>"+
        "Change your name : <input type='text' ng-model='name' />"
    ;
);

在计数器的情况下,可以使用相同的方法。

【讨论】:

如何在我的指令控制器中访问 data[1] 以进行 HTTP get 调用 您可以将controller : myDirectivecontroller 作为返回对象的一部分添加到您的指令中,并让它在指令控制器中处理您的数据。我建议将所有 RESTful 请求保存在一个服务中,并在控制器中引用该服务,如下所示:function myDirectivecontroller(ServiceName) var ctrl = this; ... @n.bharath 我用组件写了这个:plnkr.co/edit/4wOdA3OYkxS9vXAWfSxZ?p=preview 访问控制器中的 name name 值然后执行 http get 调用并将响应绑定到 template ** "你的名字是 : $ctrl.response "**【参考方案3】:

我很惊讶没有人提到ng-model,这是执行两个数据绑定的默认指令。可能不是那么广为人知,但是链接函数还有第四个参数:

angular.module('directive-binding', [])
  .directive('twoway', 
      function() 
        return 
          require: '?ngModel',
          link: function(scope, elem, attrs, ngModel) 
            elem.on('click', function() 
              var counter = ngModel.$viewValue ? ngModel.$viewValue : 0
              ngModel.$setViewValue(++counter);
              scope.$apply();
            );
          
        ;
      
    );

在你看来

<button twoway ng-model="counter">Click Me</button>
<p>Click Count:  counter </p>

第四个参数是ngModelController 的 API,它在处理(例如解析和格式化)以及在指令和作用域之间共享数据方面有很多用途。

这是更新后的Plunker。

【讨论】:

我希望在 ngshow/nghide 之类的东西之后对指令语法进行建模,它直接在其属性值中获取模型属性。不过,我很欣赏使用 ngModel 的实现——我还没有深入探索过。 然后去隔离范围,见道格的回答。【参考方案4】:

不使用$parse,你绝对可以像这样简化它

angular.module('directive-binding', []).directive('twoway', [function () 
    return 
        scope: false,
        link: function (scope, elem, attrs) 
            elem.on('click', function () 
                scope[attrs.twoway] = scope[attrs.twoway] == null ? 1 : scope[attrs.twoway] + 1;
                scope.$apply();
            );
        
    ;
]);

【讨论】:

如果您在模型中使用“嵌套”对象,这似乎不起作用。例如,如果我使用“counter.val”而不是“counter”,这似乎不起作用。 Here's a Plunker -- 我的 JS 技能不是最好的,所以我可能错过了一些东西。 @DavidFaivre 您需要在控制器中初始化它,例如$scope.counter=; $scope.counter.val = 0 如果您有兴趣,请参阅this 通过字符串访问“嵌套”对象。这是这个解决方案的根本问题——你必须编写某种解析器。 $parser 服务或嵌套范围映射在 Angular 世界中为我们处理。 @DavidFaivre 您没有提到要在原始问题中使用嵌套对象。当您提出问题时,请确保您提出的问题清楚。【参考方案5】:

为什么隔离作用域过大了?它对这种事情非常有用:

  scope: 
     "twoway": "=" // two way binding
  ,

这是这个问题的一个非常惯用的角度解决方案,所以这是我坚持的。

【讨论】:

Doug T。我本来想离开this SO post,但我可能只理解了四分之一。 这个答案的关键补充是你传递给这个指令的变量,例如counter必须是一个对象,例如obj.counter 否则您将无法获得正确的引用来更新父范围。

以上是关于AngularJS 自定义指令两种方式绑定的主要内容,如果未能解决你的问题,请参考以下文章

在自定义指令的范围绑定中使用符号“@”、“&”、“=”和“>”:AngularJS

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

将 keydown 事件定位到 angularJS 自定义指令

AngularJs学习笔记3——自定义指令

angularjs中的CKEditor自定义插件

为啥我不能在angularjs中以两种方式绑定指令组件具有相同的名称?