Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I easily implement multiple layouts in Angular JS?

I need to be able to specify different layouts for different routes, and most preferably I would like to be able to define layouts and other parameters in an object in route config and have them propagate on route change.

like image 507
Kenneth Lynne Avatar asked Apr 03 '13 19:04

Kenneth Lynne


2 Answers

Here's the way I solved it in my current project.

Working demo to be found here


What if you could define objects in your $routeProvider.when(...) block like this:

Route definition:

  $routeProvider

    .when('/', {
      templateUrl: 'main.html',
      controller: 'MainCtrl',
      resolve: {
        interceptor: interceptWith('routeChangeInterceptor', {

          stateClass: {
            fullWidthLayout: true
          }

        })
      }
    });


And have them propagate and use it to add classes with an ng-class like interface using the stateClass objects like this?

HTML:

<body state-class="{'full-width-layout': fullWidthLayout}"> ... </body>

<div class="some-class" state-class="{'some-class': someValue}"> ... </div>



How to:

This is using an interceptWith(...) helper that simply injects a service and calls it with given parameters, but it could also be implemented using array notation like this

interceptor: ['serviceToInject', function(injectedService) { .. }];

Only this way It's DRYer. See demo fore more on this.


The service that is used to broadcast the object from the route definition:

//This interceptor is responsible for emiting an event on rootScope
  .factory('routeChangeInterceptor', ['$rootScope', function($rootScope) {

    var _prepare = function(state) {
      $rootScope.$broadcast('data:broadcast-state', state);  
    };

    return {
      prepare: _prepare
    };
  }]);


The directive used to add/remove classes based on the broadcasted state event object looks like this:

//This directive receives and $parses object/classname mappings, 
//and it will add or remove the defined class for every mapping that is defined.

angular.module('broadcastState')
  .directive('stateClass', ['$parse', function ($parse) {

    var _linkFn = function link(scope, element, attrs) {

        scope.$on('data:broadcast-state', function(e, state) {

          //Set defaults
          state = state || {};
          state.stateClass = state.stateClass || {};

          var classes = $parse(attrs.stateClass)(state.stateClass);

          angular.forEach(classes,function(value,className) {
            if(value && typeof value === 'boolean')
            {
              element.addClass(className);
            }
            else
            {
              element.removeClass(className);
            }

          });

        });
    }

    return {
      restrict: 'A',
      link: _linkFn
    };
  }]);


Check out the plnkr to read more.

like image 85
Kenneth Lynne Avatar answered Oct 17 '22 12:10

Kenneth Lynne


Looks like https://github.com/angular-ui/ui-router from the Angular team is the best approach.

like image 24
Kevin Suttle Avatar answered Oct 17 '22 12:10

Kevin Suttle