angularjs指令中的棘手范围绑定

hon*_*jde 1 angularjs

我想在angularjs中编写'edit in place'指令.我希望该指令是可重用的,因此我对该指令有以下要求:

  1. 它必须是一个可以解散任何元素的地方,这是有道理的(div,span,li)
  2. 它必须支持编辑按钮,单击它将把显示元素设置为输入文件.通常是一个对象的属性,例如联系人(号码,名称)

我在这个小提琴http://jsfiddle.net/honzajde/ZgNbU/1/中可以看到指令中范围可见性的欺骗行为.

  1. 在指令中注释:模板和范围 - > contact.number和contact.name
  2. 在指令中进行注释:仅显示scope - > contact.number
  3. 没有评论任何东西 - >没有显示任何内容

=>当两者都被注释掉时只需将模板添加到指令使得它渲染contact.number即使不使用模板.

我在问游戏的规则是什么?

<div>
  <div ng-controller="ContactsCtrl">
    <h2>Contacts</h2>
    <br />
    <ul>
        <li ng-repeat="contact in contacts">
            <span edit-in-place="" ng-bind="contact.number"></span> | 
            <span edit-in-place="" >{{contact.name}}</span>
        </li>
    </ul>
    <br />
    <p>Here we repeat the contacts to ensure bindings work:</p>
    <br />
    <ul>
        <li ng-repeat="contact in contacts">
            {{contact.number}} | {{contact.name}}
        </li>
    </ul>

  </div>
</div>


var app = angular.module( 'myApp', [] );

app.directive( 'editInPlace', function() {
  return {
    restrict: 'A',
    //scope: { contact:"=" },    
    template: '<span ng-click="edit()" ng-bind="value"></span><input ng-model="value"></input>',
    link: function ( $scope, element, attrs ) {
      // Let's get a reference to the input element, as we'll want to reference it.
      var inputElement = angular.element( element.children()[1] );

      // This directive should have a set class so we can style it.
      element.addClass( 'edit-in-place' );

      // Initially, we're not editing.
      $scope.editing = false;

      // ng-click handler to activate edit-in-place
      $scope.edit = function () {
        $scope.editing = true;

        // We control display through a class on the directive itself. See the CSS.
        element.addClass( 'active' );

        // And we must focus the element. 
        // `angular.element()` provides a chainable array, like jQuery so to access a native DOM function, 
        // we have to reference the first element in the array.
        inputElement[0].focus();
      };

      // When we leave the input, we're done editing.
      inputElement.prop( 'onblur', function() {
        $scope.editing = false;
        element.removeClass( 'active' );
      });
    }
  };
});

app.controller('ContactsCtrl', function ( $scope ) {
  $scope.contacts = [
    { number: '+25480989333', name: 'sharon'},
    { number: '+42079872232', name: 'steve'}
  ];
});
Run Code Online (Sandbox Code Playgroud)

Jos*_*ler 8

你正在遇到问题,因为你误用角度.

首先,指令应该是自包含的,但是你要从中提取功能,这使得它不那么普遍并且不太可重复使用.在您的代码中,您在DOM和控制器中具有属于指令的功能.为什么?

其次,你的标记也不清楚,javascript特别希望你想要在所有这些部分串在一起时完成.

第三,在大多数情况下,指令应该有自己的独立范围,这是通过声明一个带有应该绑定的属性的范围对象来完成的.你不应该{{contact.name}}在指令中传递一个表达式(即),因为它会破坏绑定,并且当就地编辑完成时你的联系人不会被更新.正确的方法是通过=范围内的属性建立双向绑定.ng-bind这不是你想要的:那是特定于范围的,所以我们在指令的范围使用它.正如Valentyn建议的那样,你可以做一些魔术来解决这个问题,但这不是一个好主意,以正确的方式设置它是非常简单的.通过属性执行此操作有什么问题?

这都是糟糕的Ju-ju.

正如我在你关于同一主题的另一个问题中指出的那样,你必须使你的指令自成一体并使用角度而不是反对它.这是我之前给你的小提琴的基于属性的版本,符合你的第一个要求.请让我知道这个实现有什么问题,我们可以谈谈修复它的角度方式.

最后,如果您根据"按钮"提供有关所需内容的更多背景信息,我也会将其纳入小提琴中.


[更新]

有可能使指令按照您的方式工作,但最终会遇到问题(或者现在看起来似乎).角度应用程序(或任何应用程序)中的所有组件应尽可能独立.这不是"规则"或限制; 这是一个"最佳实践".类似地,指令组件之间的通信可以通过控制器进行,但不应该.理想情况下,您根本不应该在控制器中引用DOM - 这就是指令的用途.

如果您的特定目的是可编辑的,那么是您的指令.可以使用较大指令使用的较低级别通用编辑就地指令,但仍然存在更高级别的指令.更高级别的指令封装了它们之间的逻辑.然后,这个更高级别的组件将需要联系对象.

最后,不,ng-bind="var"和之间不一定有很大的区别{{var}}.但那不是问题; 这个问题是在那里是发生结合.在您的示例中,传递给指令而不是双向绑定变量.我的观点是该指令需要访问变量,以便它可以更改它.

简介:您正在以一种非常jQuery风格的方式编写代码.这对于jQuery中的编码很有用,但是在Angular中进行编码时效果不佳.事实上,它会导致许多问题,例如您正在经历的问题.例如,在jQuery中,您可以手动插入DOM元素,声明和处理事件,并在一个代码块中手动绑定变量.在Angular中,关注点清晰分离,大多数绑定都是自动的.在大多数情况下,它导致javascript代码至少比jQuery替代方案小三分之二.这是其中一个案例.

也就是说,我创建了一个Plunker,其中包含一个更复杂的编辑版本以及一个新的更高级别指令,以包含其他功能:http://plnkr.co/edit/LVUIQD?p =预览.

我希望这有帮助.

[更新2]

这些是您新一轮问题的答案.它们可能对你的启发很有帮助,但我已经为你提供了解决问题的"棱角分明的方法".您还会发现我在原始答案和更新中已经解决了这些问题(更广泛的描述).希望这更加明显.

问题: "在指令中注释:模板和范围 - > contact.number和contact.name显示"

我的回复:当您没有指定范围时,该指令继承其父范围.您在父项的上下文中绑定并插入了名称和数字,因此它"有效".但是,因为该指令会改变该值,所以这不是解决它的好方法.它确实应该有自己的范围.

问题: "在指令中填写:范围 - >仅显示contact.number"

我的回复:您将父级的范围属性绑定到"contact.number"指令,因此它将在$ digest循环中放入 - 在处理指令之后.在"contact.name"上,您将其放在指令中,该指令仅在指令代码进行转换时才有效.

问题: "不评论任何内容 - >没有显示任何内容"

我的回复:对.如果指令有自己的范围(并且肯定应该这样),那么必须使用已定义的指令范围属性来传递值,正如我的几个代码示例所示.但是,当我们通过scope在其定义中使用属性明确禁止时,您的代码会尝试使用指令中的父作用域.

简介:虽然第二次更新可能是提供信息的(我希望它是),但它没有回答你的问题下面的问题:我如何正确使用角度组件,以便我使用的范围始终是我认为的是什么?我的第一篇文章和随后的更新,回答了这个问题.