3 * @name ui.router.state.directive:ui-view
5 * @requires ui.router.state.$state
7 * @requires $controller
9 * @requires ui.router.state.$uiViewScroll
15 * The ui-view directive tells $state where to place your templates.
17 * @param {string=} name A view name. The name should be unique amongst the other views in the
18 * same state. You can have views of the same name that live in different states.
20 * @param {string=} autoscroll It allows you to set the scroll behavior of the browser window
21 * when a view is populated. By default, $anchorScroll is overridden by ui-router's custom scroll
22 * service, {@link ui.router.state.$uiViewScroll}. This custom service let's you
23 * scroll ui-view elements into view when they are populated during a state activation.
25 * *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll)
26 * functionality, call `$uiViewScrollProvider.useAnchorScroll()`.*
28 * @param {string=} onload Expression to evaluate whenever the view updates.
31 * A view can be unnamed or named.
37 * <div ui-view="viewName"></div>
40 * You can only have one unnamed view within any template (or root html). If you are only using a
41 * single view and it is unnamed then you can populate it like so:
44 * $stateProvider.state("home", {
45 * template: "<h1>HELLO!</h1>"
49 * The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#views `views`}
50 * config property, by name, in this case an empty name:
52 * $stateProvider.state("home", {
55 * template: "<h1>HELLO!</h1>"
61 * But typically you'll only use the views property if you name your view or have more than one view
62 * in the same template. There's not really a compelling reason to name a view if its the only one,
63 * but you could if you wanted, like so:
65 * <div ui-view="main"></div>
68 * $stateProvider.state("home", {
71 * template: "<h1>HELLO!</h1>"
77 * Really though, you'll use views to set up multiple views:
80 * <div ui-view="chart"></div>
81 * <div ui-view="data"></div>
85 * $stateProvider.state("home", {
88 * template: "<h1>HELLO!</h1>"
91 * template: "<chart_thing/>"
94 * template: "<data_thing/>"
100 * Examples for `autoscroll`:
103 * <!-- If autoscroll present with no expression,
104 * then scroll ui-view into view -->
105 * <ui-view autoscroll/>
107 * <!-- If autoscroll present with valid expression,
108 * then scroll ui-view into view if expression evaluates to true -->
109 * <ui-view autoscroll='true'/>
110 * <ui-view autoscroll='false'/>
111 * <ui-view autoscroll='scopeVariable'/>
114 $ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate'];
115 function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate) {
117 function getService() {
118 return ($injector.has) ? function(service) {
119 return $injector.has(service) ? $injector.get(service) : null;
120 } : function(service) {
122 return $injector.get(service);
129 var service = getService(),
130 $animator = service('$animator'),
131 $animate = service('$animate');
133 // Returns a set of DOM manipulation functions based on which Angular version
135 function getRenderer(attrs, scope) {
136 var statics = function() {
138 enter: function (element, target, cb) { target.after(element); cb(); },
139 leave: function (element, cb) { element.remove(); cb(); }
145 enter: function(element, target, cb) {
146 if (angular.version.minor > 2) {
147 $animate.enter(element, null, target).then(cb);
149 $animate.enter(element, null, target, cb);
152 leave: function(element, cb) {
153 if (angular.version.minor > 2) {
154 $animate.leave(element).then(cb);
156 $animate.leave(element, cb);
163 var animate = $animator && $animator(scope, attrs);
166 enter: function(element, target, cb) {animate.enter(element, null, target); cb(); },
167 leave: function(element, cb) { animate.leave(element); cb(); }
178 transclude: 'element',
179 compile: function (tElement, tAttrs, $transclude) {
180 return function (scope, $element, attrs) {
181 var previousEl, currentEl, currentScope, latestLocals,
182 onloadExp = attrs.onload || '',
183 autoScrollExp = attrs.autoscroll,
184 renderer = getRenderer(attrs, scope);
186 scope.$on('$stateChangeSuccess', function() {
192 function cleanupLastView() {
193 var _previousEl = previousEl;
194 var _currentScope = currentScope;
197 _currentScope._willBeDestroyed = true;
200 function cleanOld() {
202 _previousEl.remove();
206 _currentScope.$destroy();
211 renderer.leave(currentEl, function() {
216 previousEl = currentEl;
226 function updateView(firstTime) {
228 name = getUiViewName(scope, attrs, $element, $interpolate),
229 previousLocals = name && $state.$current && $state.$current.locals[name];
231 if (!firstTime && previousLocals === latestLocals || scope._willBeDestroyed) return; // nothing to do
232 newScope = scope.$new();
233 latestLocals = $state.$current.locals[name];
237 * @name ui.router.state.directive:ui-view#$viewContentLoading
238 * @eventOf ui.router.state.directive:ui-view
239 * @eventType emits on ui-view directive scope
242 * Fired once the view **begins loading**, *before* the DOM is rendered.
244 * @param {Object} event Event object.
245 * @param {string} viewName Name of the view.
247 newScope.$emit('$viewContentLoading', name);
249 var clone = $transclude(newScope, function(clone) {
250 renderer.enter(clone, $element, function onUiViewEnter() {
252 currentScope.$emit('$viewContentAnimationEnded');
255 if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) {
256 $uiViewScroll(clone);
263 currentScope = newScope;
266 * @name ui.router.state.directive:ui-view#$viewContentLoaded
267 * @eventOf ui.router.state.directive:ui-view
268 * @eventType emits on ui-view directive scope
270 * Fired once the view is **loaded**, *after* the DOM is rendered.
272 * @param {Object} event Event object.
273 * @param {string} viewName Name of the view.
275 currentScope.$emit('$viewContentLoaded', name);
276 currentScope.$eval(onloadExp);
285 $ViewDirectiveFill.$inject = ['$compile', '$controller', '$state', '$interpolate'];
286 function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate) {
290 compile: function (tElement) {
291 var initial = tElement.html();
292 return function (scope, $element, attrs) {
293 var current = $state.$current,
294 name = getUiViewName(scope, attrs, $element, $interpolate),
295 locals = current && current.locals[name];
301 $element.data('$uiView', { name: name, state: locals.$$state });
302 $element.html(locals.$template ? locals.$template : initial);
304 var link = $compile($element.contents());
306 if (locals.$$controller) {
307 locals.$scope = scope;
308 locals.$element = $element;
309 var controller = $controller(locals.$$controller, locals);
310 if (locals.$$controllerAs) {
311 scope[locals.$$controllerAs] = controller;
313 $element.data('$ngControllerController', controller);
314 $element.children().data('$ngControllerController', controller);
324 * Shared ui-view code for both directives:
325 * Given scope, element, and its attributes, return the view's name
327 function getUiViewName(scope, attrs, element, $interpolate) {
328 var name = $interpolate(attrs.uiView || attrs.name || '')(scope);
329 var inherited = element.inheritedData('$uiView');
330 return name.indexOf('@') >= 0 ? name : (name + '@' + (inherited ? inherited.state.name : ''));
333 angular.module('ui.router.state').directive('uiView', $ViewDirective);
334 angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill);