Nico Schlömer
Nico Schlömer

Reputation: 58821

Access controller data from route provider

I have an Angular app with a router and a bunch of controllers, organized like

articleApp.config(['$routeProvider',
  function($routeProvider) {
    $routeProvider.
      when('/articles', {
        templateUrl: 'templates/articles/index.html'
      }).
      when('/articles/:articleId', {
        templateUrl: 'templates/articles/article.html'
      }).
      otherwise({
        redirectTo: '/articles'
      });
  }]);

The controllers are given in the templates, e.g.,

<div ng-controller="ArticleCtrl">
  <!-- [...] -->
</div>

Now, I would like to set <head> data like page title, meta data etc. depending on the route and the contents of the page. The data is obviously in the controllers, so that's where I'm setting the <head> data now.

Unfortunately, this is becoming quite a mess: A controller here, a controller there, perhaps active in the same view, this one sets the page title, that one sets the page title... A debugging labyrinth.

Ideally, I would like to set meta data in one place only, and the $routeProvider above seems ideal to me. Unfortunately, though, we dont't have access to the data of the controllers there. Or do we?

Is it possible to access controller data from the $routeProvider?

Upvotes: 1

Views: 680

Answers (3)

Michel
Michel

Reputation: 28339

I think you are tackling the problem the wrong way. If this data is needed above the scope of the controller, it should be resolved before the instantiation of the controller. You would then inject this data in the controller the same way you inject any component.

See in particular the documentation for the resolve property on $routeProvider.

Here is an example:

$routeProvider
    .when('/phone/:phoneId', {
         controller: 'PhoneDetailController',
         templateUrl: 'phone.detail.html',
         resolve: {
             routeMetadata: function () {
                 return {
                     title: 'Phone product page',
                     keywords: ['phone', 'mobile', 'cellular']
                 }
             }
         }
    });   

In the controller you just have to inject routeMetadata:

app.controller('PhoneDetailController', function (..., routeMetadata, ...) {
    // ...
}

Besides the resolve property which is useful in particular if you need to fetch data from an api, you could also use a trick: define the data at the top level of the route definition:

$routeProvider
    .when('/phone/:phoneId', {
         controller: 'PhoneDetailController',
         templateUrl: 'phone.detail.html',
         routeMetadata: {
             title: 'Phone product page',
             keywords: ['phone', 'mobile', 'cellular']
         }
    });

This data is then accessible in $route.current. Although I write about this, I would rather go with the resolve property. The latter is more a workaround, as the difference between properties defined by the module ngRoute and custom properties is not obvious.

Upvotes: 2

Meeh
Meeh

Reputation: 2646

Define a variable in the title tag like

<title>{{ PageSettings.title() }}</title>

Then add a service for it

myModule.factory('PageSettings', function() {
   var title = 'default';
   return {
     title: function() { return title; },
     setTitle: function(newTitle) { title = newTitle }
   };
});

In your route config, add title

articleApp.config(['$routeProvider',
  function($routeProvider) {
    $routeProvider.
      when('/articles', {
        templateUrl: 'templates/articles/index.html',
        title: 'Articles'
      }).
      when('/articles/:articleId', {
        templateUrl: 'templates/articles/article.html',
        title: 'Article'
      }).
      otherwise({
        redirectTo: '/articles'
      });
  }]);

Then you can write your controller like this

function MainCtrl($scope,  $route, PageSettings) {
  PageSettings.setTitle($route.current.title);
}

Upvotes: 0

Ali Adravi
Ali Adravi

Reputation: 22763

If you have to use more information for page like

  • Title
  • Meta Data
  • Keywords
  • Description
  • and many more

Then I will suggest to use angularjs-viewhead

If only one or two info then you can set them in routing and at the time of changing the route you can set that.

See how I achieved it by using ui-router

.state('permission', {
    title: 'Permission',
    url: '/permission',
    templateUrl: 'app/views/permission.html',
    controller: 'permissionCtrl',
    data: {
        Roles: [Constants.Roles.admin, Constants.Roles.analyst]
    }
})
.state('products', {
    title: 'products Page',
    url: '/products',
    templateUrl: 'app/views/products.html'
});

An on changing the route

app.run(['$rootScope', '$state',  function ($rootScope, $state) {
$rootScope.$on('$stateChangeStart', function (event,  next) {

    // Page Title
    $rootScope.title = next.title;
});

}]);

In Html

<title ng-bind="title"></title>

Upvotes: 0

Related Questions