2

I have created a directive to understand transclusion and isolated scope. Here is the HTML:

<div ng-app="myApp">
    <div ng-controller="MyCtrl">
        <div>
            <input type="text" ng-model="name" />
            <input type="number" ng-model="friendCount" />
        </div>
        <my-friends name="{{name}}">
            <p>Hi, I'm {{name}}, and I have {{friendCount}} friends</p>
        </my-friends>
    </div>
</div>

Directive:

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

app.controller('MyCtrl', function ($scope) {
    $scope.name = "John Doe";
    $scope.friendCount = 3;
});

app.directive('myFriends', function () {
    return {
        restrict: 'E',
        replace: true,
        template: '<div>' +
            '    <h3> Isolated Scope: {{name}}</h3>' +
            '    <div ng-transclude></div>' +
            '</div>',
        transclude: true,
        scope: {
            name: '@'
        },
        link: function (scope, element, attrs) {
            scope.name = "Anderson";
        }
    }
});

Open this fiddle: JsFiddle

I have two questions:

  1. Why does the {{name}} in directive template says "John Doe" instead of "Anderson"? I expected it to be Anderson, since name property is prototypically inherited and as soon as write to it in the link function, it should lose that ancestral connection.

  2. It seems to be transcluding correctly but why does it throw the error Error: [ngTransclude:orphan] in the dev tools console? Could it be the angular version I am using?

Any help is greatly appreciated.

Here is the fiddle: JsFiddle

UPDATE:

The transclusion error was due to loading angular twice by mistake.

When using the @ binding you can overwrite the name inside directive after the initial digest cycle has completed, for example in an click handler or something. However, it will be overwritten as soon as you change the parent scope's name property.

The issue about @ binding is my misunderstanding of isolated scopes. It is working the way it is supposed to in the example. Isolated scope does not prototypically inherit from parent. And the meaning of @ binding, also referred to as read-only access or one-way binding, is that it will not let you update/write to parent scope.

UPDATED FIDDLE

Harish
  • 270
  • 3
  • 11

2 Answers2

1

The AngularJS documentation is in such a state of flux that I cannot find the page relevant to the different types of isolated scope bindings so I apologize for any inaccuracies.

How to make it work

scope: {
    name: '=' //formerly @
},

IIRC the @ binding takes the value passed in as a string literal. I am not positive why this means the link function assignment doesn't overwrite it, if I could find the section in the docs then I'd link to it.

The = binding binds to the value of a scope property and updates both the isolate scope and the assigning scope when updated.

<my-friends name="name"> //formerly name="{{name}}"

Using the = binding means that a scope property needs to be passed instead of a string value.

Again, sorry for any incorrect or vague information. I'll update the answer if I can find the dang documentation.

bmceldowney
  • 2,307
  • 13
  • 19
  • This question might be relevant to the "at" versus "equals": http://stackoverflow.com/questions/14050195/what-is-the-difference-between-and-in-directive-scope – drew_w Mar 02 '14 at 01:29
  • Thanks for reply. I am specifically trying to verify the one way binding and so I used @. But the {{name}} seems to be bound to controller instead of the directive, which is the cause of confusion for me. – Harish Mar 02 '14 at 02:19
  • drew_w's link explains the difference much better than I can here. Basically the one-way binding prevents you from assigning to the property in the linking function. – bmceldowney Mar 02 '14 at 03:09
1

1) With @ binding, at the digest phase, angular will reevaluate the expression and set the value back to the current value of your controller which will overwrite your value set in the link function.

2) You have 2 versions of angular loaded in the fiddle. The angular loaded the second time walks the DOM again and tries to compile again the already compiled DOM. For more information, take a look at my answer to another question Illegal use of ngTransclude directive in the template

DEMO

Community
  • 1
  • 1
Khanh TO
  • 48,509
  • 13
  • 99
  • 115
  • Thanks Khanh. Nice catch. The transclusion error is gone after I removed the second reference to angular. However, with the @ binding, even if the name property is bound to the controller initially, I should be able to overwrite it in the directive link function with out modifying the parent/controller scope, right? – Harish Mar 02 '14 at 02:29
  • @Harish: actually, you could overwrite the value in the link function. But and the end of the cycle, when angular updates your bindings, angular will evaluate your expressions ( `{{name}}` ) and overwrite your scope's property again. To fix this, try `=` binding – Khanh TO Mar 02 '14 at 02:32