twitter bootstrap and angularjs directives

The past few days, I’ve been experimenting with angularjs. It’s an awesome javascript MVC framework and I recommend checking it out if you haven’t!
One of its features are directives which, among other things, allow you to replace html elements with a specific tag name or attribute name with a template and adjust their behaviour. This is nice if you have complex HTML structures in a website which repeat themselves.
Another awesome thing I’ve already posted about is twitter bootstrap, so I thought I would make some angularjs directives for bootstrap syntax that I have difficulty remembering.

Note: I recommend having some knowledge on twitter bootstrap and angularjs before reading this. Of course, the docs are always wonderful ;)

To begin, let’s make sure we have all the needed things. Your page’s head should look something like this:



    
        
        
    
    

We’re loading the stylesheets for bootstrap here, as well as having the ‘directives’ module handle our app and the ‘myController’ controller throughout the whole page.

And right before </body>, let’s load the scripts:


     


The scripts are Angularjs, our custom angularjs code (‘directives.js’) where we will code our directives, jquery, and bootstrap.

Let’s also define our module in directives.js:

function myController ($scope) {
}
var module = angular.module('directives', []);

Now with that out of the way, let’s get on with the directives.

Tabs

The first one is an example from the angularjs website: tabs.

This allows you to use the following syntax in your page:


        
         Content here
    
    
         Second pane content here
    

The directive is as follows:

module.directive('tabs', function() {
    return {
        restrict: 'E',
        transclude: true,
        scope: {},
        controller: function($scope, $element) {
            var panes = $scope.panes = [];

            $scope.select = function(pane) {
                angular.forEach(panes, function(pane) {
                    pane.selected = false;
                });

                pane.selected = true;
            }

            this.addPane = function(pane) {
                if (panes.length == 0)
                    $scope.select(pane);

                panes.push(pane);
            }
        },
        template:
        '
' + '' + '
' + '
', replace: true }; }); module.directive('pane', function() { return { require: '^tabs', restrict: 'E', transclude: true, scope: { title: '@' }, link: function(scope, element, attrs, tabsCtrl) { tabsCtrl.addPane(scope); }, template: '
' + '
', replace: true }; });

What’s special about these directives is that the pane children let the parent know when they are added (by calling addPane), and the tab controller has some code to give the ‘selected’ property to the selected pane every time.

Media

heading

Content

This is not too much trouble, but I like this more:


   Content

And this little directive….

module.directive ('media', function() {
    return {
        restrict: 'E',
        transclude: true,
        scope: { heading: '@', image: '@' },
        template:
        '
' + '' + '' + '' + '
' + '

{{ heading }}

' + '

' + '

' + '
' + '
', replace: true } });

..Will save time in the long run!

The special things I did here was use ng-src= for {{ image }} instead of just src= to prevent the browser from trying to download {{ src }}. Also, ng-transclude will copy the content of my <media> tag into the element that has that attribute, so the content can be anything you wish.


Content

Hello!


Works as expected!

Modals

Let’s look at what code for a modal might look like:


That’s long to type every time you need it. Thankfully, We can make it look more like this:


    Modal Content

You can change ok-fn and cancel-fn of course.

The directive is:

module.directive('modal', function() {
    return {
        restrict: 'E',
        transclude: true,
        scope: { title:'@', okFn: '&', cancelFn: '&' },
        template:
        '',
        replace: true
    }
});

The only ‘special’ things here are including &t;p ng-transclude> which will copy the innerHTML of our into that element, and ‘okFn: “&”, cancelFn: “&”‘ which means that these two functions will be called in the context of the parent controller.

DestroyDialog is defined in the parent:

function myController ($scope) {
    $scope.destroyDialog = function() {
        $("#modal").modal("hide");
    };
}

Navbars

Navbars aren’t very complex, but the syntax can become, depending on what you want. Luckily, you can create a special ‘navbar’ attribute that makes a

    a navbar:

    
    

    The directive:

    module.directive('navbar', function() {
        return {
            restrict: 'A',
            transclude: true,
            scope: { title: '@' },
            template:
            '',
            replace: true
        }
    });
    

    We’re using ‘A’ in ‘restrict’ for the first time, because we want it to search for an attribute, not element name. Because attributes are transcluded to the resulting element, you can give your navbar other classes, as normal:

    And of course, ng-transclude will copy our <lis> to the resulting element.

    Responsive navbars

    The syntax for responsive navbars is more complex. Let’s make a directive for these, as well.

    The directive to create this:

    Is as simple as this:

    module.directive ('navbarResponsive', function() {
        return {
            restrict: 'A',
            transclude: true,
            scope: { title: '@' },
            template:
            '',
            replace: true
        }
    });
    

    Carousel

    The original syntax for carousel is in the source of this page.

    Let’s improve it from that to this:

    
      
        
        
      
      
       
        
      
    
    

    Directive:

    module.directive ('carousel', function() {
        return {
            restrict: 'E',
            transclude: true,
            scope: { id: '@' },
            controller: function($scope, $element) {
                var items = $scope.items = [];
                $scope.selectedIndex = 0;
    
                $scope.select = function (index) {
                    if ( index >= $scope.items.length || index < 0 ) {
                        return;
                    }
    
                    angular.forEach (items, function(item) {
                        item.selected = false;
                    });
    
                    items[index].selected = true;
                    $scope.selectedIndex = index;
                }
    
                this.addItem = function(item) {
                    items.push(item);
    
                    if (items.length == 1)
                        $scope.select (0);
                }
            },
            template:
            '',
            replace: true
        };
    });
    
    module.directive('carouselItem', function() {
        return {
            require: '^carousel',
            restrict: 'E',
            transclude: true,
            scope: {  },
            link: function(scope, element, attrs, carouselCtrl) {
                carouselCtrl.addItem(scope);
            },
            template:
            '
    ' + '
    ', replace: true }; });

    The special magic here (in the carousel controller function) holds the selected carousel item. The indicators are generated for every carousel item, and also call the select function. That function is also called by the next and back buttons. The carousel items call addItem on the parent controller.

    And of course, don’t forget:

    $("#carousel").carousel();
    

    Is still needed for it to work

    Well, that’s all for now. Remember to check back later for more.. magic!

    A live demo of all this is here

One thought on “twitter bootstrap and angularjs directives

  1. Hi
    I am using your carousel directive, but sliding from one slide to another is not smooth. its jumping from one slide to another. Do you have updated code pls.

    thank you

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.