1

I am trying to set up custom themeing on my app, so what I am doing is letting the user choose certain themes and it will change the apps theme holistically. I have a service which sends a piece of json and listens for it changing inside the controller of each view. Now this works fine within the view itself - for reference here's some snippets of the working code.

my factory controlling the theme -

angular.module('demoApp')
.factory('templatingFactory', function () {

var meaningOfLife = 
      {
          'h1': '#ea6060',
          'bg': '#ffffff'
      };

return {
  setTheme: function(theme) {
    meaningOfLife = theme;

  },
  getTheme: function() {
    return meaningOfLife;
  }
};

});

One of my example controllers showing and changing the theme (and listening for changes)

$scope.themeStore = templatingFactory.getTheme();
console.log($scope.themeStore);

//send new themes
$scope.themeOne = function () {
  var newT1 = { 'h1': '#8A6516',
                'bg': '#000000'};
  templatingFactory.setTheme(newT1);

};

$scope.themeTwo = function () {
  var newT2 = { 'h1': '#ffffff',
                'bg': '#ea6060'};
  templatingFactory.setTheme(newT2);


};

$scope.themeThree = function () {
  var newT3 = { 'h1': '#ea6060',
                'bg': '#ffffff'};
  templatingFactory.setTheme(newT3);


};
//listen for new themes
$scope.watchThemes = templatingFactory.getTheme();
$scope.$watch(templatingFactory.getTheme, function (newTheme) { 

    $scope.themeStore = newTheme;

});

and then on the template/view itself i do something like this -

<h3 ng-style="{ 'color' : themeStore.h1 }">Title</h3>

So my issue is that this works fine inside the view. However the ng-view tag is inside the body and outside of it are the body containers, as well as the header and footer menus that I would like to be able to hook onto with this theme object. So my quesiton is, is there any way to use that scope outside of the ng-view? I don't think it's possible but I'm not sure how else I could access and put a ng-style on the header footer and body to change some css on it with this method I am using.

So for a simple reference it looks like this -

<body ng-app="myApp">
 <div class="container">

  <div class="header" ng-style="{ 'background-color' : themeStore.bg }">

    <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
        <i class="fa fa-bars"></i>
    </button>
    <div class="headerLogo"></div>
    <div class="navbar-collapse collapse">
                <ul class="nav navbar-nav navbar-right">

    </ul>
</div>
  </div>

  <div ng-view class="velocity-opposites-transition-slideUpIn" data-velocity-opts="{ duration: 500 }" ng-style="{ 'background-color' : themeStore.bg }"> </div>

  <div class="footer">
    <p></p>
  </div>
</div>

</body>

So as you can see - I'm trying to hook onto the header to change the background color, which does not work like this. What I noticed though, is if I put it on the ng-view div itself, it works alright.

I would much appreciate any input as I've been stuck on this for a while. Thank you for taking the time to read!

ajmajmajma
  • 13,712
  • 24
  • 79
  • 133
  • I'm new to angular, so I may be way off, but I think this question may help you: http://stackoverflow.com/questions/12008908/how-can-i-pass-variables-between-controllers – coopersita Dec 01 '14 at 18:22
  • @coopersita thank you very much for responding! the issue I am having is not actually passing between the controllers, I can save with the factory and it works very well across all controllers, its accessing outside the view to tag onto the header and such (which is not inside the view) – ajmajmajma Dec 01 '14 at 18:26
  • you might need a second directive on the header that watches the templatingFactory service and updates it's view based on what getTheme() returns. – Jase Dec 01 '14 at 19:00
  • @Jase very interesting! I've never done anything like that, would you be able to show an example of how something like that would work? Thanks!! – ajmajmajma Dec 01 '14 at 19:07
  • How about having an outter controller at the top level in the body tag. Have that controller be responsible for the theme. The inner controllers can then reference $rootScope.XXX to get to values in the upper controller. – Scott Dec 01 '14 at 19:40

3 Answers3

1

The DOM elements outside of your ng-view must have controllers of their own, with templatingFactory injected as a dependency.

First I would modify the html like so:

<div class="header" ng-controller="headerController" ng-style="{ 'background-color' : themeStore.bg }">

Then add headerController to your module:

angular.module('demoApp').controller('headerController', function($scope, templatingFactory){
  $scope.themeStore = templatingFactory.getTheme();

  $scope.$watch(templatingFactory.getTheme, function (newTheme) { 
      $scope.themeStore = newTheme;
  });

});

A more reusable solution would be to create a directive that adds this controller functionality to whatever DOM element it is applied to, but the above is a little more straight forward.

SteamDev
  • 4,294
  • 5
  • 20
  • 29
1

I think the best way to have angular functions and variables outside ui-view or ng-view is to use a global service. in this case you should do your theming logic inside 'templatingFactory'. Then inject this service not in your controllers, but in your module.

angular.module('demoApp').run(['$rootScope', 'templatingFactory', function($rootScope, templatingFactory){

   $rootScope.templatingService = templatingFactory;

}]);

So your service will be avaible in the $rootScope. now you can use it this way.

<body ng-app="myApp">
 <div class="container">

  <div class="header" ng-style="{ 'background-color' : templatingService.getTheme().bg }"> </div>
</div>
</div>

ps: I'm relative new in angular too, so I don't know nothing about good/wrong practices!

Sergio A.
  • 101
  • 3
1

For the directive approach, a simple example might look something like this:

demoApp.directive('themeHeader', function (templatingFactory) {
    return {
        restrict: 'A',
        link : function (scope, element, attrs) {

            scope.$watch(templatingFactory.getTheme, function () {
                element.css('background-color', newTheme.bg);
            });
        }
    }
});

and the html would look like this:

<div theme-header>
        <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"><i class="fa fa-bars"></i></button>
        <div class="headerLogo"></div>
        <div class="navbar-collapse collapse">
            <ul class="nav navbar-nav navbar-right"></ul>
</div>
Jase
  • 830
  • 6
  • 5