1 var ngMajorVer = angular.version.major;
2 var ngMinorVer = angular.version.minor;
5 * @name ui.router.state.directive:ui-view
7 * @requires ui.router.state.$state
9 * @requires $controller
11 * @requires ui.router.state.$uiViewScroll
17 * The ui-view directive tells $state where to place your templates.
19 * @param {string=} name A view name. The name should be unique amongst the other views in the
20 * same state. You can have views of the same name that live in different states.
22 * @param {string=} autoscroll It allows you to set the scroll behavior of the browser window
23 * when a view is populated. By default, $anchorScroll is overridden by ui-router's custom scroll
24 * service, {@link ui.router.state.$uiViewScroll}. This custom service let's you
25 * scroll ui-view elements into view when they are populated during a state activation.
27 * @param {string=} noanimation If truthy, the non-animated renderer will be selected (no animations
28 * will be applied to the ui-view)
30 * *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll)
31 * functionality, call `$uiViewScrollProvider.useAnchorScroll()`.*
33 * @param {string=} onload Expression to evaluate whenever the view updates.
36 * A view can be unnamed or named.
42 * <div ui-view="viewName"></div>
45 * You can only have one unnamed view within any template (or root html). If you are only using a
46 * single view and it is unnamed then you can populate it like so:
49 * $stateProvider.state("home", {
50 * template: "<h1>HELLO!</h1>"
54 * The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#views `views`}
55 * config property, by name, in this case an empty name:
57 * $stateProvider.state("home", {
60 * template: "<h1>HELLO!</h1>"
66 * But typically you'll only use the views property if you name your view or have more than one view
67 * in the same template. There's not really a compelling reason to name a view if its the only one,
68 * but you could if you wanted, like so:
70 * <div ui-view="main"></div>
73 * $stateProvider.state("home", {
76 * template: "<h1>HELLO!</h1>"
82 * Really though, you'll use views to set up multiple views:
85 * <div ui-view="chart"></div>
86 * <div ui-view="data"></div>
90 * $stateProvider.state("home", {
93 * template: "<h1>HELLO!</h1>"
96 * template: "<chart_thing/>"
99 * template: "<data_thing/>"
105 * Examples for `autoscroll`:
108 * <!-- If autoscroll present with no expression,
109 * then scroll ui-view into view -->
110 * <ui-view autoscroll/>
112 * <!-- If autoscroll present with valid expression,
113 * then scroll ui-view into view if expression evaluates to true -->
114 * <ui-view autoscroll='true'/>
115 * <ui-view autoscroll='false'/>
116 * <ui-view autoscroll='scopeVariable'/>
119 $ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate'];
120 function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate) {
122 function getService() {
123 return ($injector.has) ? function(service) {
124 return $injector.has(service) ? $injector.get(service) : null;
125 } : function(service) {
127 return $injector.get(service);
134 var service = getService(),
135 $animator = service('$animator'),
136 $animate = service('$animate');
138 // Returns a set of DOM manipulation functions based on which Angular version
140 function getRenderer(attrs, scope) {
142 enter: function (element, target, cb) { target.after(element); cb(); },
143 leave: function (element, cb) { element.remove(); cb(); }
146 if (!!attrs.noanimation) return statics;
148 function animEnabled(element) {
149 if (ngMajorVer === 1 && ngMinorVer >= 4) return !!$animate.enabled(element);
150 if (ngMajorVer === 1 && ngMinorVer >= 2) return !!$animate.enabled();
151 return (!!$animator);
157 enter: function(element, target, cb) {
158 if (!animEnabled(element)) {
159 statics.enter(element, target, cb);
160 } else if (angular.version.minor > 2) {
161 $animate.enter(element, null, target).then(cb);
163 $animate.enter(element, null, target, cb);
166 leave: function(element, cb) {
167 if (!animEnabled(element)) {
168 statics.leave(element, cb);
169 } else if (angular.version.minor > 2) {
170 $animate.leave(element).then(cb);
172 $animate.leave(element, cb);
180 var animate = $animator && $animator(scope, attrs);
183 enter: function(element, target, cb) {animate.enter(element, null, target); cb(); },
184 leave: function(element, cb) { animate.leave(element); cb(); }
195 transclude: 'element',
196 compile: function (tElement, tAttrs, $transclude) {
197 return function (scope, $element, attrs) {
198 var previousEl, currentEl, currentScope, latestLocals,
199 onloadExp = attrs.onload || '',
200 autoScrollExp = attrs.autoscroll,
201 renderer = getRenderer(attrs, scope);
203 scope.$on('$stateChangeSuccess', function() {
209 function cleanupLastView() {
210 var _previousEl = previousEl;
211 var _currentScope = currentScope;
214 _currentScope._willBeDestroyed = true;
217 function cleanOld() {
219 _previousEl.remove();
223 _currentScope.$destroy();
228 renderer.leave(currentEl, function() {
233 previousEl = currentEl;
243 function updateView(firstTime) {
245 name = getUiViewName(scope, attrs, $element, $interpolate),
246 previousLocals = name && $state.$current && $state.$current.locals[name];
248 if (!firstTime && previousLocals === latestLocals || scope._willBeDestroyed) return; // nothing to do
249 newScope = scope.$new();
250 latestLocals = $state.$current.locals[name];
254 * @name ui.router.state.directive:ui-view#$viewContentLoading
255 * @eventOf ui.router.state.directive:ui-view
256 * @eventType emits on ui-view directive scope
259 * Fired once the view **begins loading**, *before* the DOM is rendered.
261 * @param {Object} event Event object.
262 * @param {string} viewName Name of the view.
264 newScope.$emit('$viewContentLoading', name);
266 var clone = $transclude(newScope, function(clone) {
267 renderer.enter(clone, $element, function onUiViewEnter() {
269 currentScope.$emit('$viewContentAnimationEnded');
272 if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) {
273 $uiViewScroll(clone);
280 currentScope = newScope;
283 * @name ui.router.state.directive:ui-view#$viewContentLoaded
284 * @eventOf ui.router.state.directive:ui-view
285 * @eventType emits on ui-view directive scope
287 * Fired once the view is **loaded**, *after* the DOM is rendered.
289 * @param {Object} event Event object.
290 * @param {string} viewName Name of the view.
292 currentScope.$emit('$viewContentLoaded', name);
293 currentScope.$eval(onloadExp);
302 $ViewDirectiveFill.$inject = ['$compile', '$controller', '$state', '$interpolate'];
303 function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate) {
307 compile: function (tElement) {
308 var initial = tElement.html();
309 return function (scope, $element, attrs) {
310 var current = $state.$current,
311 name = getUiViewName(scope, attrs, $element, $interpolate),
312 locals = current && current.locals[name];
318 $element.data('$uiView', { name: name, state: locals.$$state });
319 $element.html(locals.$template ? locals.$template : initial);
321 var link = $compile($element.contents());
323 if (locals.$$controller) {
324 locals.$scope = scope;
325 locals.$element = $element;
326 var controller = $controller(locals.$$controller, locals);
327 if (locals.$$controllerAs) {
328 scope[locals.$$controllerAs] = controller;
330 $element.data('$ngControllerController', controller);
331 $element.children().data('$ngControllerController', controller);
341 * Shared ui-view code for both directives:
342 * Given scope, element, and its attributes, return the view's name
344 function getUiViewName(scope, attrs, element, $interpolate) {
345 var name = $interpolate(attrs.uiView || attrs.name || '')(scope);
346 var inherited = element.inheritedData('$uiView');
347 return name.indexOf('@') >= 0 ? name : (name + '@' + (inherited ? inherited.state.name : ''));
350 angular.module('ui.router.state').directive('uiView', $ViewDirective);
351 angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill);