How can I easily implement multiple layouts in Angular JS?
Asked Answered
S

3

9

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.

Sashenka answered 3/4, 2013 at 19:0 Comment(0)
S
4

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

Soy answered 1/4, 2014 at 19:41 Comment(1)
This is my preferred way of doing it. UI router supports having multiple views, and you can define layout in a parent state that all child states populate views in.Sashenka
S
9

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.

Sashenka answered 3/4, 2013 at 19:0 Comment(3)
This is an overcomplicated and bad approach. I will provide a better way later on.Sashenka
so will the ui- router not help in this case ?Looks lot of workaround.Adverse
This should be the answer. Good job!Lientery
S
4

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

Soy answered 1/4, 2014 at 19:41 Comment(1)
This is my preferred way of doing it. UI router supports having multiple views, and you can define layout in a parent state that all child states populate views in.Sashenka
Q
0

Try this http://angular-route-segment.com/ (A lightweight extension for AngularJS $route service which supports tree-like nested views and routes, and advanced flow handling)

Quag answered 11/6, 2016 at 9:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.