Tuesday, 2 April 2013

AngularJS directives: Specifying options

Introduction

In the last blog item we created our panel widget by writing a few DOM manipulations with the JQuery light version included in AngularJS. Now that we have our directive working with code, we can think about the configuration of the collapse functionality.

Attributes

AngularJS gives you full access to all attributes that are specified on the element. In the previous version of our directive, you maybe saw already the attrs parameter of the compile function which we didn’t use. This is the list of all attributes on the element.
So for testing our directive, we create 2 panels, one where we like a ‘button’ to collapse the contents, and one where we don’t need it.
If we don’t need it, the html is the same as we already used in the previous blog items.

<div my-dir4 title="title : {{panelTitle}}" >
   Contents of the div. Angular Expression result : {{contentField}}
</div>

And when we need the collapsible contents, we like to write it like this:
<div my-dir4="collapsable" title="title : {{panelTitle}}" >
   Contents of the div. Angular Expression result : {{contentField}}
</div>

Reading config


By using the attrs parameter of the compile function, we can determine if the developer specified the ‘collapsable’ text within the directive. This allows us to create an options object that we can pass to the create function we have created the last time. And thus taking into account that we need to ‘render’ a button in the panel header.

The directive code looks like this
directive('myDir4', function version4() {
        return {
            restrict: 'A'
            , compile: function (element, attrs) {
                var options = {
                    collapse: 'collapsable' === attrs.myDir4
                };
                myPanel.create(element, options);
            }
        };

Now we have the collapse boolean property that tell us if we need to render the collapse button.
We are not limited to reading only the attribute of the directive itself.  We could for instance also retrieve the value of the title attribute, which is not a directive or a standard attribute like class.

Rendering the collapse button


The create function can be updated to ‘render’ an optional collapse button.
this.header = angular.element(Helper.findChild(element, 'pui-panel-titlebar'));

if(options.collapse) {
   this.toggler = angular.element('<a href="#"> X</a>').bind('click', function (e) {
      Helper.findChild(element, 'pui-panel-content').style.display = "none";
      e.preventDefault();
   });

   this.titleSpan = angular.element(Helper.findChild(this.header, 'ui-panel-title'));
   this.titleSpan.after(this.toggler);
}

When we need the ‘toggler’ (options.collapse is true), we create a DOM element (angular.element) and attach a handler for the click event.  When the users clicks the X marker, we find the element that has the pui-panel-content CSS class and set the display options to none.

The Helper.findChild is a helper method to find a child element with a certain CSS class name. With the JQuery light version, we can specify element.children(), but we can’t supply a selector with it. So element.children(‘pui-panel-titlebar’) returns all children. The helper function loops over all children and returns the one, if any, that has the class defined.

A more complete version of our panel widget can be found in the demo code on github. There you can find a more complete version of the collapse functionality where we render a plus or minus sign that allows us not only to collapse the panel contents but also expand it later on again.

Conclusion


Now we can react based on the information we find in the directive or other attributes on the element. This gives us the tools to create more dynamic solutions and have a configuration possibility.

In the next item, the last of the series, we see how we can access the scope within the directives and how the code looks like to have a programmatic collapse and expand of the content.

1 comment:

  1. how do you access a scope array passed as argument from within the compiler function? I'm trying to modify the dom and I need the passed array to loop it.

    I have a stack question here: http://stackoverflow.com/questions/14357945/how-to-make-angularjs-compile-the-code-generated-by-directive

    ReplyDelete