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 var promise = $animate.enter(element, null, target, cb);
147 if (promise && promise.then) promise.then(cb);
149 leave: function(element, cb) {
150 var promise = $animate.leave(element, cb);
151 if (promise && promise.then) promise.then(cb);
157 var animate = $animator && $animator(scope, attrs);
160 enter: function(element, target, cb) {animate.enter(element, null, target); cb(); },
161 leave: function(element, cb) { animate.leave(element); cb(); }
172 transclude: 'element',
173 compile: function (tElement, tAttrs, $transclude) {
174 return function (scope, $element, attrs) {
175 var previousEl, currentEl, currentScope, latestLocals,
176 onloadExp = attrs.onload || '',
177 autoScrollExp = attrs.autoscroll,
178 renderer = getRenderer(attrs, scope);
180 scope.$on('$stateChangeSuccess', function() {
183 scope.$on('$viewContentLoading', function() {
189 function cleanupLastView() {
196 currentScope.$destroy();
201 renderer.leave(currentEl, function() {
205 previousEl = currentEl;
210 function updateView(firstTime) {
212 name = getUiViewName(scope, attrs, $element, $interpolate),
213 previousLocals = name && $state.$current && $state.$current.locals[name];
215 if (!firstTime && previousLocals === latestLocals) return; // nothing to do
216 newScope = scope.$new();
217 latestLocals = $state.$current.locals[name];
219 var clone = $transclude(newScope, function(clone) {
220 renderer.enter(clone, $element, function onUiViewEnter() {
222 currentScope.$emit('$viewContentAnimationEnded');
225 if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) {
226 $uiViewScroll(clone);
233 currentScope = newScope;
236 * @name ui.router.state.directive:ui-view#$viewContentLoaded
237 * @eventOf ui.router.state.directive:ui-view
238 * @eventType emits on ui-view directive scope
240 * Fired once the view is **loaded**, *after* the DOM is rendered.
242 * @param {Object} event Event object.
244 currentScope.$emit('$viewContentLoaded');
245 currentScope.$eval(onloadExp);
254 $ViewDirectiveFill.$inject = ['$compile', '$controller', '$state', '$interpolate'];
255 function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate) {
259 compile: function (tElement) {
260 var initial = tElement.html();
261 return function (scope, $element, attrs) {
262 var current = $state.$current,
263 name = getUiViewName(scope, attrs, $element, $interpolate),
264 locals = current && current.locals[name];
270 $element.data('$uiView', { name: name, state: locals.$$state });
271 $element.html(locals.$template ? locals.$template : initial);
273 var link = $compile($element.contents());
275 if (locals.$$controller) {
276 locals.$scope = scope;
277 var controller = $controller(locals.$$controller, locals);
278 if (locals.$$controllerAs) {
279 scope[locals.$$controllerAs] = controller;
281 $element.data('$ngControllerController', controller);
282 $element.children().data('$ngControllerController', controller);
292 * Shared ui-view code for both directives:
293 * Given scope, element, and its attributes, return the view's name
295 function getUiViewName(scope, attrs, element, $interpolate) {
296 var name = $interpolate(attrs.uiView || attrs.name || '')(scope);
297 var inherited = element.inheritedData('$uiView');
298 return name.indexOf('@') >= 0 ? name : (name + '@' + (inherited ? inherited.state.name : ''));
301 angular.module('ui.router.state').directive('uiView', $ViewDirective);
302 angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill);