棘手的范围,angularjs指令结合 [英] Tricky scope binding in angularjs directives

查看:102
本文介绍了棘手的范围,angularjs指令结合的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想写指令在angularjs正在编辑。
我想该指令是可重复使用的,因此我有以下的指令要求:


  1. 它必须是能够deocorate任何元素的attirbute,这是有道理的(DIV,跨度,LI)

  2. 它必须支持编辑按钮,点击会改变设置OT displayd元素融入到输入个行业。通常为一个对象的属性,例如联系(编号,名称)

我disocver​​e在可在该小提琴的http:// jsfiddle.net/honzajde/ZgNbU/1/ 。


  1. 在该指令Comenting出来:模板和范围 - >显示contact.number和contact.name

  2. 在该指令Comenting出来:范围 - > contact.number只显示

  3. 不评论任何东西 - >未显示任何内容

=>当两者都注释掉只是增加模板的指令使它呈现contact.number即使不使用模板。

我问什么游戏规​​则?

 < D​​IV>
  < D​​IV NG控制器=ContactsCtrl>
    < H2>联系和LT; / H>
    < BR />
    < UL>
        <李NG重复=中的联系人联系>
            <跨度编辑就地=NG绑定=contact.number>< / SPAN> |
            &所述;跨度编辑就地=> {{contact.name}}&下; /跨度>
        < /李>
    < / UL>
    < BR />
    < P>此处,我们重复接触,以确保绑定工作:LT; / P>
    < BR />
    < UL>
        <李NG重复=中的联系人联系>
            {{contact.number}} | {{联系人姓名}}
        < /李>
    < / UL>  < / DIV>
< / DIV>
VAR应用= angular.module('对myApp',[]);app.directive('editInPlace',函数(){
  返回{
    限制:'A',
    //范围:{接触:=},
    模板:'<跨度NG点击=编辑()NG绑定=值>< / SPAN><输入NG模型=值>< /输入>,
    链接:功能($范围,元素,ATTRS){
      //让我们对输入元素的引用,因为我们将要引用它。
      变种inputElement = angular.element(element.children()[1]);      //这个指令应该有一组类,所以我们可以样式。
      element.addClass(编辑就地');      //起初,我们并没有进行编辑。
      $ scope.editing = FALSE;      // NG单击处理程序来激活编辑就地
      $ scope.edit =功能(){
        $ scope.editing = TRUE;        //我们通过这个指令本身的一类控制显示。见CSS。
        element.addClass(激活);        //我们必须关注的元素。
        //`angular.element()`提供了一个可链接的阵列,像jQuery这样访问本地DOM功能,
        //我们必须引用的第一个元素的数组中为止。
        inputElement [0]。重点();
      };      //当我们离开的投入,我们就大功告成了编辑。
      inputElement.prop('的onblur',函数(){
        $ scope.editing = FALSE;
        element.removeClass(激活);
      });
    }
  };
});app.controller('ContactsCtrl',函数($范围){
  $ scope.contacts = [
    {号码:25480989333,名称:'沙龙'},
    {号码:42079872232,名称:'史蒂夫'}
  ];
});


解决方案

您正在运行到问题,因为你是滥用棱角分明。

首先,指令应该是独立的,但你拔的功能出来,这使得它的通用和重用。在您的code,你在DOM和在该指令所属的控制器具有的功能。为什么?

其次,它也是从您的标记不清和JavaScript特别想你想完成当所有这些件串在一起。

第三,在大多数情况下,指令应该有自己的分离的范围,这是通过声明一个范围对象与它应绑定属性进行。你不应该传递一个前pression(即 {{} contact.name} )指令内因为它会破解的绑定和编辑就地完成时,您的联系人将不会被更新。正确的方法是建立双向通过的范围的 = 属性绑定。 NG-绑定是不是你想要的位置:这是范围特异的,所以我们用它的指令的范围。作为Valentyn建议,你可以做一些魔法来解决这个问题,但它不是一个好主意,它是超级简单的设置它的正确途径。什么是由一个属性这样的问题?

这是所有坏的菊菊。

我在你的<一指出, href=\"http://stackoverflow.com/questions/14166637/agularjs-how-to-say-to-a-directive-to-clone-scope/14169878\">other在这同一主题的问题,你必须使你的指令,自包含的,工作的的角度,而不是反对。下面是我给你previously,会议第一您的要求小提琴的,基于属性的版本。请让我知道这有什么具体实施的错误,我们可以谈论它固定的角速度方式。

最后,如果你提供进一步的情况下你在一个按钮方面需要什么,我会将此主题融入小提琴了。


[更新]

有可能使指令您的工作方式,但你最终会遇到问题(或现在,它似乎)。 所有在角应用程序(或任何与此有关的应用程序)组件应尽可能自成体系是可行的。这不是一个规则,或限制;这是一个最佳实践。同样,指令组件之间的通信的可以的通过一个控制器发生,但它不应该。理想情况下,你不应该引用控制器在所有的DOM - 这就是指令是

如果您的具体目的是一个这是编辑,然后点击是你的指令。这没关系有较大的指令使用较低级别的通用编辑就地指令,但仍有更高级别的指令了。较高层次的指令封装它们之间的逻辑。那么这个更高级别的组件将需要一个接触对象。

最后,没有,没有一定的 NG-绑定=VAR {{VAR}} 。但是,这不是问题;问题是的其中,这个绑定发生。在你的榜样,一个的的传递给指令,而不是双向绑定的变量的。我的观点是,该指令需要访问的变量,因此它可以改变它。

摘要:您的编码在一个非常jQuery的风格的方式。这是伟大的jQuery的编码,但在角编码时它不工作这么好。事实上,它会导致出现很多问题,比如你遇到的人。在jQuery中,你会,比如动态插入DOM元素,声明和处理事件,并手动绑定所有在单个code块变量,所有手动。在角,存在的顾虑一个干净的分离和大部分的结合的是自动的。在大多数情况下,它会导致的javascript code的至少的三分之二比jQuery的替代小。这是那些案件之一。

这是说,我已经创建了一个包含了更复杂的版本无论是编辑就地,以及一个新的更高层次的指令把附加功能的Plunker:的 http://plnkr.co/edit/LVUIQD?p=$p$pview

我希望这有助于。

[更新2]

这些都是别人给你的新一轮质疑。他们可能是对你的熏陶,但我已经给你了角的方式来解决您的问题。你还会发现,我已经在我原来的答复,以及在我的更新中解决这些问题(更广泛的中风)更早。我们希望,这使得它更加明显。

问:的的指令Comenting出来:模板和范围 - > contact.number和contact.name显示

我的回复:的当你不指定范围,该指令将继承其父范围。您约束和父的范畴内插的姓名和号码的,所以工程。由于该指令将修改的价值,但是,这是的的一个很好的方式方法来解决它。这真的应该有自己的范围。

问:的的指令Comenting出来:范围 - > contact.number只显示

我的回复:的束缚,您母公司的作用域属性的contact.number指令,所以$消化循环中它会被放在里面 - 该指令已被受理。在contact.name,你把它的 的内部指令,它只能工作,如果该指令codeS为transclusion。

问:的不评论任何东西 - >不会显示任何

我的回复:的权利。如果该指令有它自己的范围(而这一次一定要),则必须使用规定的指令,scope属性进行沟通价值,因为我的几个code示例演示。您code,然而,尝试使用父范围的指令,当我们明确禁止通过使用范围属性的定义。

摘要:虽然本次更新也有可能说明(我希望它是),它不回答这个问题的的你的问题:我怎么用角分量正确的让我使用的范围始终是什么,我认为它是什么?我的第一篇和后续更新,回答这个问题。

I want to write 'edit in place' directive in angularjs. I want that directive is reusable, therefore I have following requirements on the directive:

  1. it must be an attirbute that can deocorate any element, that makes sense (div,span,li)
  2. it must support edit button, clicking on that will change set ot displayd elements into input fileds. Typically properties of one object, e.g. contact (number, name)

I disocvere trickery behaviour of scope visibility in the directive that can be seen in this fiddle http://jsfiddle.net/honzajde/ZgNbU/1/.

  1. Comenting out in the directive: template and scope -> contact.number and contact.name are displayed
  2. Comenting out in the directive: scope -> contact.number only is displayed
  3. Not commenting out anything -> nothing is displayed

=> when both are commented out just adding template to the directive makes it render contact.number even though template is not used.

I am asking what are the rules of the game?

<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'}
  ];
});

解决方案

You are running into problems because you are misusing angular.

First, a directive should be self-contained, but you are pulling functionality out of it, which makes it less universal and less reusable. In your code, you have functionality in the DOM and in the controller that belongs in the directive. Why?

Second, it's also unclear from your markup and javascript specifically want you want to accomplish when all these pieces are strung together.

Third, in most cases, directives should have their own isolated scope, which is done by declaring a scope object with attributes it should bind. You shouldn't be passing an expression (i.e. {{contact.name}}) inside the directive as it will break the binding and your contact will not be updated when the edit-in-place finishes. The proper way is to establish bi-directional binding through an = property on the scope. ng-bind isn't what you want here: that's scope-specific, so we use it inside the directive's scope. As Valentyn suggested, you could do some magic to get around this, but it's not a good idea and it's super-simple to set it up the right way. What's the issue with doing this by an attribute?

This is all bad Ju-ju.

As I pointed out in your other question on this same topic, you must make your directive self-contained and work with angular, rather than against it. Here's an attribute-based version of the fiddle I gave you previously, meeting the first of your requirements. Please let me know what is wrong specifically with this implementation and we can talk about the angular way of fixing it.

Lastly, if you provide further context on what you need in terms of a "button", I'll incorporate that into the fiddle too.


[update]

It is possible to make the directives work your way, but you will run into problems eventually (or right now, it would seem). All components in an angular app (or any app for that matter) should be as self-contained as is feasible. It's not a "rule" or limitation; it's a "best practice". Similarly, communication between directive components can occur through a controller, but it shouldn't. Ideally, you shouldn't reference the DOM in a controller at all - that's what directives are for.

If your specific purpose is a row that is editable, then that is your directive. It's okay to have a lower-level generic edit-in-place directive that the larger directive uses, but there is still the higher-level directive too. The higher-level directive encapsulates the logic between them. This higher-level component would then require a contact object.

Lastly, no, there isn't necessarily a big difference between ng-bind="var" and {{var}}. But that's not the issue; the issue was where that binding takes place. In your example, a value was passed to the directive instead of a bi-directionally-bound variable. My point was that the directive needs access to the variable so it can change it.

Summary: You are coding in a very jQuery-style way. That's great for coding in jQuery, but it doesn't work so well when coding in Angular. In fact, it causes a lot of problems, like the ones you're experiencing. In jQuery, you would, for example, dynamically insert DOM elements, declare and handle events, and manually bind variables all within a single code block, all manually. In Angular, there is a clean separation of concerns and most of the binding is automatic. In most cases, it leads to javascript code at least two-thirds smaller than the jQuery alternative. This is one of those cases.

That said, I have created a Plunker that contains a more sophisticated version of both the edit-in-place as well as a new higher-level directive to incorporate additional features: http://plnkr.co/edit/LVUIQD?p=preview.

I hope this helps.

[update 2]

These are the answers to your new round of questions. They may be good for your edification, but I already gave you the "angular way" to fix your problem. You will also find that I already addressed these questions (in broader strokes) earlier in my original answer as well as in my update. Hopefully, this makes it more apparent.

Question: "Comenting out in the directive: template and scope -> contact.number and contact.name are displayed"

My Reply: When you do not specify a scope, the directive inherits its parent scope. You bound and interpolated the name and number within the context of the parent, so it "works". Because the directive will alter the value, however, this is not a good way way to solve it. It really should have its own scope.

Question: "Comenting out in the directive: scope -> contact.number only is displayed"

My Reply: You bound a scope property of the parent to the "contact.number" directive, so it will get placed inside during the $digest loop - after the directive has been processed. On the "contact.name", you put it inside the directive, which can only work if the directive codes for transclusion.

Question: "Not commenting out anything -> nothing is displayed"

My Reply: Right. If the directive has its own scope (and this one definitely should), then you must use a defined directive scope property to communicate values, as my several code samples demonstrate. Your code, however, tries to use the parent scope in the directive when we explicitly forbid that by using the scope property in its definition.

Summary: While this second update may be informative (and I hope that it is), it doesn't answer the question beneath your questions: how do I use angular components correctly so that the scope I'm using is always what I think it is? My first post and the subsequent update, answer that question.

这篇关于棘手的范围,angularjs指令结合的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆