Bilateral binding not working in the cross-border directive

I have a text box in the controller that is bound to the name model. There is a directive inside the controller and there is another text field inside the directive that is bound to the same name model:

 <div class="border" ng-controller="editCtrl"> Controller: editCtrl <br/> <input type="text" ng-model="name" /> <br/> <tabs> Directive: tabs <br/> <input type="text" ng-model="name"/> </tabs> </div> 

 mod.directive('tabs', function() { return { restrict: 'E', transclude: true, template: '<div class="border" ng-transclude></div>', }; }); 

When you enter something in an external text field, it is displayed in the inner text field, but if you enter something in the internal text field, it stops working, that is, both text fields no longer reflect the same value.

See an example: http://jsfiddle.net/uzairfarooq/MNBLd/

I also tried using two-way attr binding ( scope: {name: '='} ), but it gives a syntax error. And using scope: {name: '@'} has the same effect.

Any help would be greatly appreciated.

In addition to the accepted answer, this article really helped me understand prototypical inheritance in child blocks. I strongly recommend that anyone with area problems read it in its entirety.

+58
javascript angularjs
Jan 23 '13 at 14:04
source share
3 answers

The directive with transclude: true causes the directive to create a new (transclosed) child region. This new prototype region is inherited from the parent region. In your case, the parent area is the area associated with the editCtrl controller.

Using two-way data binding in a child region (i.e. ng-model) to bind to a property of the parent region containing a primitive value (e.g. name ) always causes problems - well, I have to say that this does not work properly. When a region property changes in a child element (for example, you type in a second text box), the child element creates a new scope property that hides / shadow the property of the parent region with the same name. If the parent property contains a primitive value, that value is (essentially) copied to the child property when the child property is created. Future changes to the content area (for example, in the second text box) will only affect the child property.

Before entering into the second text field (i.e. before changing the property in the child), the child / transcluded area finds the name property in the parent area through prototype inheritance (the dashed line in the figure below). This is why the two text fields initially remain in sync. Below, if you type “Mark” in the first text box, it looks like this:

transcluded scope follows inheritance chain

I created a fiddle where you can explore two areas. Click the "show area" link next to the second text box before entering text into the second text box. This allows you to see the transclosed coverage of the child. You will notice that at the moment it does not have a name property. Clean the console, enter in the second text box and click the link again. You will notice that the child region now has the name property, and the initial value is the value that the parent property ("Mark") has. If you entered “like Angular” in the second text box, it looks like this:

transcluded primitive hides parent property

There are two solutions:

  • do what @ pgreen2 offers (this is a "best practice" solution) - use an object instead of a primitive. When an object is used, the child / transcluded scope does not receive a new property. Only prototype inheritance is used here. In the figure below, suppose that the editCtrl $ area has this object:
    $scope.myObject = { name: "Mark", anotherProp: ... } :
    object in parent
  • use the $ parent child in the content area (this is a fragile solution and is not recommended because it makes assumptions about the HTML structure): use ng-model="$parent.name" inside the <input>, which is inside the <tabs> element. The first image above shows how this works.

A syntax error occurs when using scope: {name: '='} , because when using two-way data binding (that is, when using '=') interpolation is not allowed, i.e. {{}} cannot be used. Instead of <tabs name="{{name}}"> use <tabs name="name"> .

Using '@' works the same as for the transclude case, since ng-transclude uses the transcluded scope, not the selection area that is created using scope: { ... } .

For (lots) more information about areas (including pictures), see What are the nuances of the volume of the prototype / prototype inheritance in AngularJS?

+128
Jan 23 '13 at 16:46
source share

I believe that the problem is with the definition of the area. Initially, the inner text field does not have a set of name , so it inherits from the outer region. This is why text input in the outer box is reflected in the inner box. However, after entering the inner field of the inner region now contains name , which means that it is no longer tied to the outer name , so the outer text field is not synchronized.

The appropriate way to fix it is only to store the models in the area, not your values. I fixed it at http://jsfiddle.net/pdgreen/5RVza/ . The trick is to create a model object ( data ) and a link to it.

Wrong code changes the scope of the directive; correct code changes the scope of the directive. This subtle difference allows the correct inheritance of the area.

I believe that Mishko Harey formulated this, the scope should be write-only in the controller and read-only in directives.

update: link: https://www.youtube.com/watch?v=ZhfUv0spHCY#t=29m19s

+10
Jan 23 '13 at 14:16
source share

A syntax error means that you described something incorrectly. This is not related to a specific structure / library. You probably forgot to add a "," or close the parenthesis. Check it out again

0
Jan 23 '13 at 14:11
source share



All Articles