AngularJS - Access isolated scope in directive's link function

45,325

Solution 1

Isolate scope properties bound with @ are not immediately available in the linking function. You need to use $observe:

$attr.$observe('id', function(value) {
   console.log(value);
});

Your template works properly because Angular automatically updates isolate scope property id for you. And when it does update, your template automatically updates too.

If you are just passing strings, you can simply evaluate the values once instead of using @ binding:

link: function($scope, $elem, $attr) {
    var id    = $attr.id;
    var title = $attr.title
    console.log(id, title);
}

However, in your case, since you want to use the properties in templates, you should use @.

If you weren't using templates, then @ is useful when attribute values contain {{}}s – e.g., title="{{myTitle}}". Then the need to use $observe becomes more apparent: your linking function may want to do something each time the value of myTitle changes.

Solution 2

This is intentional and has to do with compilation order and the difference between '@' and '='.

Some excerpts from this Google Groups discussion with input from Misko:

@ and = do very different things. One copies the attribute value (which may be interpolated) and the other treats the attribute value as an expression.

@attrs are not $interpolated until later, so they are not available at link time. If you want to do something with them in the link function you either need to $interpolate yourself manually

Share:
45,325
pdegand59
Author by

pdegand59

Passionate Android Developper who likes perfectly aligned pixels delivered by most recents libraries and well crafted code !

Updated on December 31, 2020

Comments

  • pdegand59
    pdegand59 over 3 years

    I'm giving a first try at AngularJS custom directives.

    I'm having trouble using (or understanding ...) the isolated scope in the link function of the directive.

    Here is the code of this part of my app :

    view.html

    ...
    <raw-data id="request-data" title="XML of the request" data="request">See the request</raw-data>
    ...
    

    request is a variable published in the scope of the viewCtrl that contains the xml-string of a request.

    rawData.js

    directives.directive('rawData', function() {
    
        return {
            restrict : 'E',
            templateUrl : 'partials/directives/raw-data.html',
            replace : true,
            transclude : true,
            scope : {
                id : '@',
                title : '@',
                data : '='
            },
            link : function($scope, $elem, $attr) {
                console.log($scope.data); //the data is correclty printed
                console.log($scope.id); //undefined
            }
        };
    });
    

    raw-data.html

    <div>
        <!-- Button to trigger modal -->
        <a href="#{{id}}Modal" role="button" class="btn" data-toggle="modal" ng-transclude></a>
    
        <!-- Modal -->
        <div id="{{id}}Modal" class="modal hide fade" tabindex="-1" role="dialog" aria-labelledby="{{id}}Modal" aria-hidden="true">
            <div class="modal-header">
                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
                <h3 id="myModalLabel">{{ title }}</h3>
            </div>
            <div class="modal-body">
                <textarea class="input-block-level" rows="10">{{ data }}</textarea>
            </div>
            <div class="modal-footer">
                <!-- <button class="btn" ng-click="toggleTagText('')">{{'cacher'}} l'image</button> -->
                <button class="btn btn-primary" data-dismiss="modal" aria-hidden="true">Fermer</button>
            </div>
        </div>
    </div>
    

    I don't understand why the ID is correclty shown when the modal pops, but when I try to console.log() it, its value is undefined.

    Maybe i'm wrong with the isolated scope value (= and @).

    Thank you for reading. :)