AngularJS Better Enterprise Web Modules with Directives

Introduction

AngularJs has taken the industry by storm and has been high on our list of recommended technologies for web apps and component based workflows. In this series, we are going to introduce, directives in angularjs with a “learn more widget”, going over some techniques to jump start your next enterprise project. That we are going to cover.

  1. What are Directives and how to use them
  2. Adding interactivity to your widget
  3. Styling Directives
  4. Templating

The Power of Using Directives

While architecting an enterprise application, it’s important to have a reliable way build, test and extend features within your app. Whether you’re adding a social widget, video player or file uploader; modular development will allow you to focus on functionality and increase maintainability.

Directives give you the power to extend html in a more natural way. Adding classes, linking dom events, adding and replacing content with templates, with directives you can simplify the bootstrapping process and centralize modular code. When it all breaks down, angularjs directives have 2 main phases, compile and link. The compile phase happens once and is great for setting up directives that have options that won’t change. The kicker is most of the interaction happens in the link function which a compile function also returns.

To get started simply add the following code:

angular.module("app", [])
    .directive("learnMore", [ "$log", function ($log){
    return {
        link: function (scope, ele, attr) {
            //TODO: Add code here
             $log.info("Learn More Init");
        }
    }
}]);

To initialize the learn more widget simply create a div and give it an attribute of learn-more. Once your page is refreshed, you should get a log message in your console window confirming you have successfully setup and resolved your widget.

<div learn-more></div>

Scripting Interaction:

Our link function is made up of 3 arguments, scope, ele and attr. We will skip over scope and attr for now and talk about ele. Short for element, the 2nd parameter gives us access to the actual dom element our directive is attached to wrapped in jqLite. With access to the dom element we are free to make any modification or additions we would with jqLite or JQuery. One great benefit of angularjs is that it ships with access to jqlite, which gives those of you familiar with jquery, access to a library of helpers like, hasClass(), addClass(), on(), off(), bind(), find() and so much more. Even better if you have jquery 1.7.1+ angularjs will actually substitute jqLite with the jquery version you have enabled on your page.

To get started let add a mouse click event to our directive. For now we are going to focus on logging information.

angular.module("app", [])
.directive("learnMore", ["$log", function ($log) {
    return {
        link: function (scope, ele, attr) {
            //TODO: Add code here
            $log.info("Learn More Init");

            ele.on(“click", function () {
                $log.info("event: click");
            });
        }
    }
}]);

Now that we know the mouse event is working, let’s build up our directive a little more and add the ability to toggle an “on” class; to display extra information. Before that let’s make some updates to our html and add some css. For all of our projects we tend to use one of two pre-processor either sass or less; in this example we are using less.

HTML:

    
<div learn-more> <div class=”wrapper”> <p>Learn more text for user</p> </div> </div>

Less:

.learn-more{

 &.on{
   .wrapper{

   display: block;
    }
 }

 .wrapper{
  display:none;
 }
}

JS:

ele.on("click", function () {
     ele.toggleClass("on");
});

If you try this code it won’t work because were missing one thing. You might notice that our less code is using an object oriented approach to css or SMACSS , with the “learn-more” class encapsulating our wrapper class. In order to function properly, our learn more directive needs to have a “learn-more” class. Well this happens a lot more often than not, but it give us a great opportunity to take advantage of another great part of directives and that is bootstrapping the classes as well. As part of our directive we can require that the class exist or be added during the link phase.

Within your link function add the following lines of code:

if(!ele.hasClass("learn-more")){
     ele.addClass("learn-more");
}

Run the code now and when you click on the learn more text it will show and hide the wrapper content. So what just happened. Well using jqlite we check to see if our directive has a class of “learn-more” if not we add it. Earlier in the article I spoke about the importance of modular code, this is a great example of how angularjs allows us to easily enhance our html and bootstrap it the way we need it without requiring a heavy setup.

Scopes and Templates:

Scopes play a huge role in angularjs, use to expose variables and even functions scopes are the glue that keeps things running smooth in angularjs. One great thing about angularjs is we can actually use scope variables to bind content to our html. AngularJs has a number of templating techniques, we’re going to focus on just 2. Ng-bind and template notation. So what’s the big difference.

When the ng-app is initiated; it has to traverse through the dom to find directives and angularjs code. The downside is, there are times where the dom will display the { { } } template notation before angularjs gets a chance to resolve and evaluate. Which makes, when possible, ng-bind a better option for binding data that has a higher potential of being painted by the dom before angularjs has a chance to evaluate.

So lets start off by adding a key to our scope attribute and display that information using our notation. So there is no need to add scope.desc instead we can just add and that should replace our text.


HTML/js:

<div learn-more> Learn More Btn <div class=wrapper> <p></p> </div> </div>

JS:
 $scope.desc = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed pulvinar eu orci vel interdum. Integer rutrum viverra eros, a blandit risus convallis rhoncus. Morbi molestie eleifend diam, quis lacinia nisi imperdiet a";

Creating Unique Scopes

This widget needs to be able to display a unique desc for each instance of our directive. In angularjs terms, that means each directive needs an isolated scope. If we are to duplicate our div and directive, we would see they both share the same content. Since our scope.desc value is hard-coded to our directive then updating one will update both. Never Fear, angularjs directive already have the ability to isolate scopes built in. Lets get started with creating our first template and isolated scope.

When we talk about modular coding, the last thing we want to do is copy and paste code. As of right now our directive has html within it, which is good for small installations but makes it a nightmare to update if we have multiple instances. Luckily for us, angularjs ships with templates, transclude and url.

Lets start off by updating our directive and add the following keys,

  1. Transclude: allows for the dom element and scope to be synced even if the dom moves around;
  2. Replace: is a boolean value indicating if we want to replace our current html with the one providedin our template or templateUrl;
  3. Template: is a string containing our html template for our directive.

transclude: true, replace: true, template: '<div><p>Learn More</p> <div class="wrapper"> <p></p><p></div></div>',

//Now for our scope.

scope:{

desc: "@", title: "@" },

Within our scope key you will notice that we have two keys desc and title both with values of “@”. The keys added to our scope object, tells our new isolated scope what variables it should initiate with. Followed by how to bind them. In our case “@” simply means bind to the attribute with the same name. Your code should now look like.

Conclusion:

AngularJS directives are a powerful tool in creating enterprise based solutions. From this example you can quickly see how we can reuse, build and expand on our learn more directive. The link function allowed us to bootstrap our directive by adding the events we need as well as ensure the correct css classes are either present or added. By using isolated scopes, we were able to make sure each instance of or directive was unique and can contain its own set of values. While our usage of the templates feature, allowed us to keep the markup centralized and removed the need for complicated setups.