commonroutesApp/www/lib/angular-ui-router/src/viewDirective.js
www/lib/angular-ui-router/src/viewDirective.jsRaw
1/**
2 * @ngdoc directive
3 * @name ui.router.state.directive:ui-view
4 *
5 * @requires ui.router.state.$state
6 * @requires $compile
7 * @requires $controller
8 * @requires $injector
9 * @requires ui.router.state.$uiViewScroll
10 * @requires $document
11 *
12 * @restrict ECA
13 *
14 * @description
15 * The ui-view directive tells $state where to place your templates.
16 *
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.
19 *
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.
24 *
25 * *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll)
26 * functionality, call `$uiViewScrollProvider.useAnchorScroll()`.*
27 *
28 * @param {string=} onload Expression to evaluate whenever the view updates.
29 *
30 * @example
31 * A view can be unnamed or named.
32 * <pre>
33 * <!-- Unnamed -->
34 * <div ui-view></div>
35 *
36 * <!-- Named -->
37 * <div ui-view="viewName"></div>
38 * </pre>
39 *
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:
42 * <pre>
43 * <div ui-view></div>
44 * $stateProvider.state("home", {
45 * template: "<h1>HELLO!</h1>"
46 * })
47 * </pre>
48 *
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:
51 * <pre>
52 * $stateProvider.state("home", {
53 * views: {
54 * "": {
55 * template: "<h1>HELLO!</h1>"
56 * }
57 * }
58 * })
59 * </pre>
60 *
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:
64 * <pre>
65 * <div ui-view="main"></div>
66 * </pre>
67 * <pre>
68 * $stateProvider.state("home", {
69 * views: {
70 * "main": {
71 * template: "<h1>HELLO!</h1>"
72 * }
73 * }
74 * })
75 * </pre>
76 *
77 * Really though, you'll use views to set up multiple views:
78 * <pre>
79 * <div ui-view></div>
80 * <div ui-view="chart"></div>
81 * <div ui-view="data"></div>
82 * </pre>
83 *
84 * <pre>
85 * $stateProvider.state("home", {
86 * views: {
87 * "": {
88 * template: "<h1>HELLO!</h1>"
89 * },
90 * "chart": {
91 * template: "<chart_thing/>"
92 * },
93 * "data": {
94 * template: "<data_thing/>"
95 * }
96 * }
97 * })
98 * </pre>
99 *
100 * Examples for `autoscroll`:
101 *
102 * <pre>
103 * <!-- If autoscroll present with no expression,
104 * then scroll ui-view into view -->
105 * <ui-view autoscroll/>
106 *
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'/>
112 * </pre>
113 */
114$ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate'];
115function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate) {
116
117 function getService() {
118 return ($injector.has) ? function(service) {
119 return $injector.has(service) ? $injector.get(service) : null;
120 } : function(service) {
121 try {
122 return $injector.get(service);
123 } catch (e) {
124 return null;
125 }
126 };
127 }
128
129 var service = getService(),
130 $animator = service('$animator'),
131 $animate = service('$animate');
132
133 // Returns a set of DOM manipulation functions based on which Angular version
134 // it should use
135 function getRenderer(attrs, scope) {
136 var statics = function() {
137 return {
138 enter: function (element, target, cb) { target.after(element); cb(); },
139 leave: function (element, cb) { element.remove(); cb(); }
140 };
141 };
142
143 if ($animate) {
144 return {
145 enter: function(element, target, cb) {
146 var promise = $animate.enter(element, null, target, cb);
147 if (promise && promise.then) promise.then(cb);
148 },
149 leave: function(element, cb) {
150 var promise = $animate.leave(element, cb);
151 if (promise && promise.then) promise.then(cb);
152 }
153 };
154 }
155
156 if ($animator) {
157 var animate = $animator && $animator(scope, attrs);
158
159 return {
160 enter: function(element, target, cb) {animate.enter(element, null, target); cb(); },
161 leave: function(element, cb) { animate.leave(element); cb(); }
162 };
163 }
164
165 return statics();
166 }
167
168 var directive = {
169 restrict: 'ECA',
170 terminal: true,
171 priority: 400,
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);
179
180 scope.$on('$stateChangeSuccess', function() {
181 updateView(false);
182 });
183 scope.$on('$viewContentLoading', function() {
184 updateView(false);
185 });
186
187 updateView(true);
188
189 function cleanupLastView() {
190 if (previousEl) {
191 previousEl.remove();
192 previousEl = null;
193 }
194
195 if (currentScope) {
196 currentScope.$destroy();
197 currentScope = null;
198 }
199
200 if (currentEl) {
201 renderer.leave(currentEl, function() {
202 previousEl = null;
203 });
204
205 previousEl = currentEl;
206 currentEl = null;
207 }
208 }
209
210 function updateView(firstTime) {
211 var newScope,
212 name = getUiViewName(scope, attrs, $element, $interpolate),
213 previousLocals = name && $state.$current && $state.$current.locals[name];
214
215 if (!firstTime && previousLocals === latestLocals) return; // nothing to do
216 newScope = scope.$new();
217 latestLocals = $state.$current.locals[name];
218
219 var clone = $transclude(newScope, function(clone) {
220 renderer.enter(clone, $element, function onUiViewEnter() {
221 if(currentScope) {
222 currentScope.$emit('$viewContentAnimationEnded');
223 }
224
225 if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) {
226 $uiViewScroll(clone);
227 }
228 });
229 cleanupLastView();
230 });
231
232 currentEl = clone;
233 currentScope = newScope;
234 /**
235 * @ngdoc event
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
239 * @description *
240 * Fired once the view is **loaded**, *after* the DOM is rendered.
241 *
242 * @param {Object} event Event object.
243 */
244 currentScope.$emit('$viewContentLoaded');
245 currentScope.$eval(onloadExp);
246 }
247 };
248 }
249 };
250
251 return directive;
252}
253
254$ViewDirectiveFill.$inject = ['$compile', '$controller', '$state', '$interpolate'];
255function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate) {
256 return {
257 restrict: 'ECA',
258 priority: -400,
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];
265
266 if (! locals) {
267 return;
268 }
269
270 $element.data('$uiView', { name: name, state: locals.$$state });
271 $element.html(locals.$template ? locals.$template : initial);
272
273 var link = $compile($element.contents());
274
275 if (locals.$$controller) {
276 locals.$scope = scope;
277 var controller = $controller(locals.$$controller, locals);
278 if (locals.$$controllerAs) {
279 scope[locals.$$controllerAs] = controller;
280 }
281 $element.data('$ngControllerController', controller);
282 $element.children().data('$ngControllerController', controller);
283 }
284
285 link(scope);
286 };
287 }
288 };
289}
290
291/**
292 * Shared ui-view code for both directives:
293 * Given scope, element, and its attributes, return the view's name
294 */
295function 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 : ''));
299}
300
301angular.module('ui.router.state').directive('uiView', $ViewDirective);
302angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill);