Thursday, 4 April 2013

AngularJS directives: Integrate with $scope

Introduction

In the last blog item we saw a way to configure our panel widget so that we could create a collapsable and non collapsable version.
Until now, we didn’t integrate it yet with the model values, contained in the $scope object. This blog item shows you how you can do it.

Link function

Until now, we used the compile function of the directive to make our changes to he DOM structure. As stated in the documentation of the directive, this is also the best place to perform such actions.
But in that function, we can’t have access to the scope object. Within the link function we have, so lets try it that way.
demo.controller('Ctrl', function Ctrl($scope) {

    $scope.panelOptions = {
        collapsed : false
    };
});

As a starting point, suppose we have the above code as the controller. We defined an object panelOptions that indicates if the panel is collapsed or not. This is already in preparation for our next step that we like the panel contents to collapse as we change the scope value.

In the html code we like to refer to this scope information in the following way
<div my-dir5="panelOptions" title="title : {{panelTitle}}" >
   Contents of the div. Angular Expression result : {{contentField}}
</div>

To get access to the actual object defined in the scope, we need to execute the scope.$eval function as shown in the directive code.
directive('myDir5', function version5 () {
        return {
            restrict: 'A'
            , link: function (scope, element, attrs) {

                var options = scope.$eval(attrs.myDir5) || {};

                console.log('options value');
                console.dir(options);
              }
        };
    });

If we look at the console, we can see that the options object is the one that we have defined in the controller (collapsed is false).

attrs.$observe


In the AngularJS documentation, they mention the attrs.$observe as a way to retrieve the value of angular expressions defined in the attribute values. And in some use cases it can be useful.


  • You don’t need to have access to the scope object to get the value, but it seems that it only works from within a compile function.
  • You need to write it explicitly as an expression, so in our example it would be my-dir5=”{{panelOptions}}”. This is not the common notation as you are familiar with the brackets less version with ng-model and alike directives.
  • The function passed as parameter to the attrs.$observe method is executed asynchronously. That means that you have to wait until you receive the actual value before you can proceed.

For the above last 2 reasons, I prefer to use the scope.$eval method. And you might think that evaluating something at runtime is unsafe (as it is in standard JavaScript). Within angularJS it is a common practice, also used by the library itself, as you are always restricted to the scope content (a kind of sandbox if you like)

Dead end?


Now that we have access to the objects defined in the scope with the $eval() function, we can now call our create() method of the panel helper we have written in the previous blog item.

And to our surprise, the widget isn’t working properly anymore. The look is still ok, but the header of the panel still contains the Angular expression which isn’t resolved (title : {{panelTitle}} )

How can we explain that?

Our link function is called after the AngularJS code determines where there are expressions which need to be ‘watched’. Watched means that they get replaced by the actual value whenever the content is changed.

Because AngularJS has already identified the DOM elements to change, our newly created element isn’t on that list and thus not known by AngularJS.

The solution will be discussed in the next blog item.

Conclusion


If we like to access the scope object, we need to abandon the compile function and switch to the link function. But at first glance, it is a step back as the title isn’t set correctly anymore.  But as we will see towards the end of this series, this switch will pay off as we are able to collapse/expand the panel contents programmatically without any user intervention.

No comments:

Post a Comment