6

In a simplified scenario, I have two UI-Router states and want different body background colors for them.

Ideally, I would thus like to do the following:

<body ng-class="background" ui-view>
</body>

and then in the controller for the states set the background classes (which might be dynamic, meaning: calculated in the controller):

stateHelperProvider
    .state({
        name: 'a',
        url: '/a',
        templateUrl: 'views/a.html',
        controller: function ($scope) {
            $scope.background = 'bg1';
        }
    })
    .state({
        name: 'b',
        url: '/b',
        templateUrl: 'views/b.html',
        controller: function ($scope) {
            $scope.background = 'bg2';
        }
    });

But putting the ui-view attribute on the body deletes it. So I have to put the ui-view attribute on a div inside the body.

<body>
    <div ui-view></div>
</body>

How can I now control the body background?

I am aware of various hacks (e.g. access the class DOM property of body in the onEnter function of UI-Router, ...), but is there a nice way to do that?

Or is it all about making the div[ui-view] full height (which is not trivial) and setting the background on this element to mimick how applying a background on the body takes up the full viewport?

Community
  • 1
  • 1
jack_kerouac
  • 1,482
  • 2
  • 15
  • 30

2 Answers2

2

So to sum up my learnings, there are two ways to do it. Both require the $state service to be in the $rootScope. The elegance of this can be disputed, but I will go for this solution.

  1. If the background classes are depending on the state only, add them to the state's custom data:

Example code:

.state({
    ...,
    data: {
        bodyClass: 'bg1'
    }
});

Then, on the body, put an ng-class and reference the current state's data:

<body ng-class="$state.current.data.bodyClass">
  1. If the background classes are (besides the state) depending on something else (like a state parameter or a service, ...), use the state's resolve mechanism:

Example code:

.state({
    ...,
    resolve: {
        bodyClass: function($stateParams) {
            // do some calculation
            return ...
        }
    }
});

Then, on the body, put an ng-class and reference the resolved class name through $state.$current.locals.globals:

<body ng-class="$state.$current.locals.globals.bodyClass">

In both cases, bodyClass can anything that is valid for the ng-class directive.

Community
  • 1
  • 1
jack_kerouac
  • 1,482
  • 2
  • 15
  • 30
  • Btw. [The Angular UI-Router title plugin](https://github.com/nonplus/angular-ui-router-title) for updating the page title does [something similar](https://github.com/nonplus/angular-ui-router-title/blob/master/src/angular-ui-router-title.js#L6) – jack_kerouac Jul 27 '15 at 08:51
  • 1
    This worked for me! -- Just want to point out that in order to get it to work you have to run `$rootScope.$state = $state;` from inside a `.run` when you declare your module. This is mentioned and bolded in the first sentence of the answer but there is no example given. You can find example code by following this link http://stackoverflow.com/questions/18572930/whats-the-purpose-of-setting-rootscope-state-state-with-angular-ui-ui-rou which is also provided in the answer – Benjamin Conant Dec 23 '15 at 17:06
0

Probably not accurate, but maybe could you just change your body background-color directly in the controller ?

stateHelperProvider
    .state({
        name: 'a',
        url: '/a',
        templateUrl: 'views/a.html',
        controller: function ($scope) {
            document.body.style.background = 'bg1';
        }
    })
    .state({
        name: 'b',
        url: '/b',
        templateUrl: 'views/b.html',
        controller: function ($scope) {
            document.body.style.background = 'bg2';
        }
    });

Or in this case just add/remove CSS classes to the body?

Good Luck'

Alexis B.
  • 1,105
  • 8
  • 13
  • That works and it might be ok with classes. Problem is, that if I have state `c` without a background, the background of the previous state will remain. I can of course then listen to `$destroy` on the scope, but that again is not exactly elegant. – jack_kerouac Jul 22 '15 at 06:58
  • What about a controller on the body with 'ngStyle' which could get page value and changing the background depending on a condition about the page name? Probably not very accurate again, but hope it can help. – Alexis B. Jul 22 '15 at 08:19
  • I could probably have the background class as a [custom data property of the state](https://github.com/angular-ui/ui-router/wiki#attach-custom-data-to-state-objects) and then do something like this: `` with `$state` on the `$rootScope`. Not exactly what you proposed but inspired by it. – jack_kerouac Jul 22 '15 at 13:29
  • I thought about something like that, but it's even better. : ) – Alexis B. Jul 22 '15 at 13:32
  • I just realized that I actually have the requirement of dynamically calculating the background CSS class per state, meaning it needs to be in a controller or a resolve. As [this answer](http://stackoverflow.com/a/28027023/1511832) details, there is a way to access the resolved properties from `$state`. So that might be the way to go. – jack_kerouac Jul 23 '15 at 07:52
  • I didn't even know it was possible. That's great, thanks for sharing this. – Alexis B. Jul 23 '15 at 08:12