Skip to content

State provider views

nderelli edited this page Aug 1, 2014 · 14 revisions

We have already covered that each state can define multiple views and how the controller can be attached.

So lets look at the different ways you can define a template on a view.

Defining Templates

There is a number of ways to configure a template for a view, common for all of them is that you do it by the template property on a view.

Template as a String

The most common case might be to have a template defined as a simple string containing html or an URL to a file stored on the server.

To determine which is used, the regular expression ^(((http|https|ftp)://([\w-\d]+\.)+[\w-\d]+){0,1}(/?[\w~,;\-\./?%&+#=]*))$ with the case insensitive option is used, if it matches, the string is treated as an URL, otherwise it is returned as is. This may match more than what you wants it to, or maybe you actually wanted to use "http://github.com" as a value for the template.

If for any case, the string you provided isn't working as it should, try using the Template Object instead.

angular.module('phonecat', ['dotjem.routing']).
  config(['$stateProvider', function($stateProvider) {
  $stateProvider
      .state('phones', { 
          views: { 'main': { template: 'phones.html' } }
      })
}]);

Template as an Function

The most common case might be to have a template defined as an URL to a file stored on the server. If the template needs to be a bit more dynamic, or you wan't to select it based on parameters or something else, you can use a function (or an array with a function as the last element, and the preceeding elements to define dependencies, like we are use to in Angular)

The function can either return a string containing the template as html, or a promise that resolves to a string.

angular.module('phonecat', ['dotjem.routing']).
  config(['$stateProvider', function($stateProvider) {
  $stateProvider
      .state('phones', { 
          views: { 'main': { 
              template: ['$http', function($http) { return 'Hello Phones'; } ] } 
          }
      })
}]);

Template as an Object

The final way, which is also useful if you wish to be explicit in what type of template you provide, is a "template definition object". which defines one of 3 properties: html, url or fn.

angular.module('phonecat', ['dotjem.routing']).
  config(['$stateProvider', function($stateProvider) {
  $stateProvider
      .state('phones', { 
          views: { 
              'fnTpl': { template: { 
                  fn: ['$http', function($http) { return 'Hello Phones'; } ] } 
              },
              'urlTpl': { template: { url: 'phones.html'; } },
              'htmlTpl': { template: { html: 'Hello Phones' } },
          }
      })
}]);

This also helps you force the template provider to treat /some/string/that/looks/like/an/url as a pure html template. Where the automatic detection of what the template property was, would fail.

Views and the View Service

Defining views on a state is equivalent to calling the $view service.

Whenever a state opdate is triggered, the state machine checks all states from the root and down to the targeted state, for all non-changed states, down to the target, the $view.setIfAbsent function on the $view service is called, this ensures that if they view was removed it is reset.

For all states that have changed down to the target, the $view.setOrUpdate function is called. This will overwrite the view if it is currently defined.

All this is done in a transaction, so no changes take effect until all view changes has been determined, this ensure that if a parent state sets the view main and a child state overwrites it, we don't begin to render and then re-render when the child state overwrites.

Since it is done this way, it allows you to do additional view changes from transition handlers, like so:

angular.module('phonecat', ['dotjem.routing']).
  config(['$stateProvider', function($stateProvider) {
  $stateProvider
      .state('phones', { 
          views: { 
              'fnTpl': { template: { 
                  fn: ['$http', function($http) { return 'Hello Phones'; } ] } 
              },
              'urlTpl': { template: { url: 'phones.html'; } },
              'htmlTpl': { template: { html: 'Hello Phones' } },
          }
      })
      .transition('*', 'phones', function ($view) {
          $view.clear('htmlTpl');
          $view.setIfAbsent('htmlTpl', 'Hello More Phones');
      });
}]);

Which would overwrite the standard behavior in this case, and if we come from a state that already had the htmlTpl defined, we leave it alone. It is a bit cryptic here that we call ".clear". This will be covered in more detail in the View Provider section, where transactions are covered, and how they behave compared to direct updates.

Sticky Views

When defining views in states, it is possible to provide a sticky flag which provides better control over whether a view is updated or not.

angular.module('phonecat', ['dotjem.routing']).
  config(['$stateProvider', function($stateProvider) {
  $stateProvider
      .state('phones', { 
          views: { 
              'booleanSticky': { 
                  sticky: true,
                  template: 'sticky stuff'
              },
              'stringSticky': { 
                  sticky: 'imSticky',
                  template: 'sticky stuff'
              },
              'fnSticky': { 
                  sticky: ['$to', function($to) { return 'imSticky'; }],
                  template: 'sticky stuff'
              },
          }
      })
}]);

As shown in the example, within states, a sticky flag can be defined in three ways:

  • boolean: if sticky is defined as true, sticky will take the fullname from the state the view is defined on, in the example this would be root.phones.
  • string: if sticky is defined as a string, it will be used as is.
  • function: if sticky is defined as a function, the function will be called using the $injector service, the $to and $from states will be provided as locals for the $injector, the function must return a string.

In all cases the sticky flag will be provided to the view which will determine what to do. The state provider will then call the view service with: $view.setOrUpdate(viewname, template, controller, sticky).

If a view is made sticky, then its sticky flag is compared to the previous update for the view, if the sticky flags match, the view is not reloaded, instead it will call the refresh function if defined on the view scope, otherwise it will raise an $refresh event.

Clone this wiki locally