angular-gem 1.2.0.1 → 1.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,878 @@
1
+ /**
2
+ * @license AngularJS v1.2.1
3
+ * (c) 2010-2012 Google, Inc. http://angularjs.org
4
+ * License: MIT
5
+ */
6
+ (function(window, angular, undefined) {'use strict';
7
+
8
+ /**
9
+ * @ngdoc overview
10
+ * @name ngRoute
11
+ * @description
12
+ *
13
+ * # ngRoute
14
+ *
15
+ * The `ngRoute` module provides routing and deeplinking services and directives for angular apps.
16
+ *
17
+ * {@installModule route}
18
+ *
19
+ * <div doc-module-components="ngRoute"></div>
20
+ */
21
+ /* global -ngRouteModule */
22
+ var ngRouteModule = angular.module('ngRoute', ['ng']).
23
+ provider('$route', $RouteProvider);
24
+
25
+ /**
26
+ * @ngdoc object
27
+ * @name ngRoute.$routeProvider
28
+ * @function
29
+ *
30
+ * @description
31
+ *
32
+ * Used for configuring routes. See {@link ngRoute.$route $route} for an example.
33
+ *
34
+ * Requires the {@link ngRoute `ngRoute`} module to be installed.
35
+ */
36
+ function $RouteProvider(){
37
+ function inherit(parent, extra) {
38
+ return angular.extend(new (angular.extend(function() {}, {prototype:parent}))(), extra);
39
+ }
40
+
41
+ var routes = {};
42
+
43
+ /**
44
+ * @ngdoc method
45
+ * @name ngRoute.$routeProvider#when
46
+ * @methodOf ngRoute.$routeProvider
47
+ *
48
+ * @param {string} path Route path (matched against `$location.path`). If `$location.path`
49
+ * contains redundant trailing slash or is missing one, the route will still match and the
50
+ * `$location.path` will be updated to add or drop the trailing slash to exactly match the
51
+ * route definition.
52
+ *
53
+ * * `path` can contain named groups starting with a colon (`:name`). All characters up
54
+ * to the next slash are matched and stored in `$routeParams` under the given `name`
55
+ * when the route matches.
56
+ * * `path` can contain named groups starting with a colon and ending with a star (`:name*`).
57
+ * All characters are eagerly stored in `$routeParams` under the given `name`
58
+ * when the route matches.
59
+ * * `path` can contain optional named groups with a question mark (`:name?`).
60
+ *
61
+ * For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
62
+ * `/color/brown/largecode/code/with/slashs/edit` and extract:
63
+ *
64
+ * * `color: brown`
65
+ * * `largecode: code/with/slashs`.
66
+ *
67
+ *
68
+ * @param {Object} route Mapping information to be assigned to `$route.current` on route
69
+ * match.
70
+ *
71
+ * Object properties:
72
+ *
73
+ * - `controller` – `{(string|function()=}` – Controller fn that should be associated with
74
+ * newly created scope or the name of a {@link angular.Module#controller registered
75
+ * controller} if passed as a string.
76
+ * - `controllerAs` – `{string=}` – A controller alias name. If present the controller will be
77
+ * published to scope under the `controllerAs` name.
78
+ * - `template` – `{string=|function()=}` – html template as a string or a function that
79
+ * returns an html template as a string which should be used by {@link
80
+ * ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
81
+ * This property takes precedence over `templateUrl`.
82
+ *
83
+ * If `template` is a function, it will be called with the following parameters:
84
+ *
85
+ * - `{Array.<Object>}` - route parameters extracted from the current
86
+ * `$location.path()` by applying the current route
87
+ *
88
+ * - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
89
+ * template that should be used by {@link ngRoute.directive:ngView ngView}.
90
+ *
91
+ * If `templateUrl` is a function, it will be called with the following parameters:
92
+ *
93
+ * - `{Array.<Object>}` - route parameters extracted from the current
94
+ * `$location.path()` by applying the current route
95
+ *
96
+ * - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
97
+ * be injected into the controller. If any of these dependencies are promises, the router
98
+ * will wait for them all to be resolved or one to be rejected before the controller is
99
+ * instantiated.
100
+ * If all the promises are resolved successfully, the values of the resolved promises are
101
+ * injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is
102
+ * fired. If any of the promises are rejected the
103
+ * {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired. The map object
104
+ * is:
105
+ *
106
+ * - `key` – `{string}`: a name of a dependency to be injected into the controller.
107
+ * - `factory` - `{string|function}`: If `string` then it is an alias for a service.
108
+ * Otherwise if function, then it is {@link api/AUTO.$injector#invoke injected}
109
+ * and the return value is treated as the dependency. If the result is a promise, it is
110
+ * resolved before its value is injected into the controller. Be aware that
111
+ * `ngRoute.$routeParams` will still refer to the previous route within these resolve
112
+ * functions. Use `$route.current.params` to access the new route parameters, instead.
113
+ *
114
+ * - `redirectTo` – {(string|function())=} – value to update
115
+ * {@link ng.$location $location} path with and trigger route redirection.
116
+ *
117
+ * If `redirectTo` is a function, it will be called with the following parameters:
118
+ *
119
+ * - `{Object.<string>}` - route parameters extracted from the current
120
+ * `$location.path()` by applying the current route templateUrl.
121
+ * - `{string}` - current `$location.path()`
122
+ * - `{Object}` - current `$location.search()`
123
+ *
124
+ * The custom `redirectTo` function is expected to return a string which will be used
125
+ * to update `$location.path()` and `$location.search()`.
126
+ *
127
+ * - `[reloadOnSearch=true]` - {boolean=} - reload route when only `$location.search()`
128
+ * or `$location.hash()` changes.
129
+ *
130
+ * If the option is set to `false` and url in the browser changes, then
131
+ * `$routeUpdate` event is broadcasted on the root scope.
132
+ *
133
+ * - `[caseInsensitiveMatch=false]` - {boolean=} - match routes without being case sensitive
134
+ *
135
+ * If the option is set to `true`, then the particular route can be matched without being
136
+ * case sensitive
137
+ *
138
+ * @returns {Object} self
139
+ *
140
+ * @description
141
+ * Adds a new route definition to the `$route` service.
142
+ */
143
+ this.when = function(path, route) {
144
+ routes[path] = angular.extend(
145
+ {reloadOnSearch: true},
146
+ route,
147
+ path && pathRegExp(path, route)
148
+ );
149
+
150
+ // create redirection for trailing slashes
151
+ if (path) {
152
+ var redirectPath = (path[path.length-1] == '/')
153
+ ? path.substr(0, path.length-1)
154
+ : path +'/';
155
+
156
+ routes[redirectPath] = angular.extend(
157
+ {redirectTo: path},
158
+ pathRegExp(redirectPath, route)
159
+ );
160
+ }
161
+
162
+ return this;
163
+ };
164
+
165
+ /**
166
+ * @param path {string} path
167
+ * @param opts {Object} options
168
+ * @return {?Object}
169
+ *
170
+ * @description
171
+ * Normalizes the given path, returning a regular expression
172
+ * and the original path.
173
+ *
174
+ * Inspired by pathRexp in visionmedia/express/lib/utils.js.
175
+ */
176
+ function pathRegExp(path, opts) {
177
+ var insensitive = opts.caseInsensitiveMatch,
178
+ ret = {
179
+ originalPath: path,
180
+ regexp: path
181
+ },
182
+ keys = ret.keys = [];
183
+
184
+ path = path
185
+ .replace(/([().])/g, '\\$1')
186
+ .replace(/(\/)?:(\w+)([\?|\*])?/g, function(_, slash, key, option){
187
+ var optional = option === '?' ? option : null;
188
+ var star = option === '*' ? option : null;
189
+ keys.push({ name: key, optional: !!optional });
190
+ slash = slash || '';
191
+ return ''
192
+ + (optional ? '' : slash)
193
+ + '(?:'
194
+ + (optional ? slash : '')
195
+ + (star && '(.+?)' || '([^/]+)')
196
+ + (optional || '')
197
+ + ')'
198
+ + (optional || '');
199
+ })
200
+ .replace(/([\/$\*])/g, '\\$1');
201
+
202
+ ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
203
+ return ret;
204
+ }
205
+
206
+ /**
207
+ * @ngdoc method
208
+ * @name ngRoute.$routeProvider#otherwise
209
+ * @methodOf ngRoute.$routeProvider
210
+ *
211
+ * @description
212
+ * Sets route definition that will be used on route change when no other route definition
213
+ * is matched.
214
+ *
215
+ * @param {Object} params Mapping information to be assigned to `$route.current`.
216
+ * @returns {Object} self
217
+ */
218
+ this.otherwise = function(params) {
219
+ this.when(null, params);
220
+ return this;
221
+ };
222
+
223
+
224
+ this.$get = ['$rootScope',
225
+ '$location',
226
+ '$routeParams',
227
+ '$q',
228
+ '$injector',
229
+ '$http',
230
+ '$templateCache',
231
+ '$sce',
232
+ function($rootScope, $location, $routeParams, $q, $injector, $http, $templateCache, $sce) {
233
+
234
+ /**
235
+ * @ngdoc object
236
+ * @name ngRoute.$route
237
+ * @requires $location
238
+ * @requires $routeParams
239
+ *
240
+ * @property {Object} current Reference to the current route definition.
241
+ * The route definition contains:
242
+ *
243
+ * - `controller`: The controller constructor as define in route definition.
244
+ * - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
245
+ * controller instantiation. The `locals` contain
246
+ * the resolved values of the `resolve` map. Additionally the `locals` also contain:
247
+ *
248
+ * - `$scope` - The current route scope.
249
+ * - `$template` - The current route template HTML.
250
+ *
251
+ * @property {Array.<Object>} routes Array of all configured routes.
252
+ *
253
+ * @description
254
+ * `$route` is used for deep-linking URLs to controllers and views (HTML partials).
255
+ * It watches `$location.url()` and tries to map the path to an existing route definition.
256
+ *
257
+ * Requires the {@link ngRoute `ngRoute`} module to be installed.
258
+ *
259
+ * You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API.
260
+ *
261
+ * The `$route` service is typically used in conjunction with the
262
+ * {@link ngRoute.directive:ngView `ngView`} directive and the
263
+ * {@link ngRoute.$routeParams `$routeParams`} service.
264
+ *
265
+ * @example
266
+ This example shows how changing the URL hash causes the `$route` to match a route against the
267
+ URL, and the `ngView` pulls in the partial.
268
+
269
+ Note that this example is using {@link ng.directive:script inlined templates}
270
+ to get it working on jsfiddle as well.
271
+
272
+ <example module="ngViewExample" deps="angular-route.js">
273
+ <file name="index.html">
274
+ <div ng-controller="MainCntl">
275
+ Choose:
276
+ <a href="Book/Moby">Moby</a> |
277
+ <a href="Book/Moby/ch/1">Moby: Ch1</a> |
278
+ <a href="Book/Gatsby">Gatsby</a> |
279
+ <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
280
+ <a href="Book/Scarlet">Scarlet Letter</a><br/>
281
+
282
+ <div ng-view></div>
283
+ <hr />
284
+
285
+ <pre>$location.path() = {{$location.path()}}</pre>
286
+ <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
287
+ <pre>$route.current.params = {{$route.current.params}}</pre>
288
+ <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
289
+ <pre>$routeParams = {{$routeParams}}</pre>
290
+ </div>
291
+ </file>
292
+
293
+ <file name="book.html">
294
+ controller: {{name}}<br />
295
+ Book Id: {{params.bookId}}<br />
296
+ </file>
297
+
298
+ <file name="chapter.html">
299
+ controller: {{name}}<br />
300
+ Book Id: {{params.bookId}}<br />
301
+ Chapter Id: {{params.chapterId}}
302
+ </file>
303
+
304
+ <file name="script.js">
305
+ angular.module('ngViewExample', ['ngRoute'])
306
+
307
+ .config(function($routeProvider, $locationProvider) {
308
+ $routeProvider.when('/Book/:bookId', {
309
+ templateUrl: 'book.html',
310
+ controller: BookCntl,
311
+ resolve: {
312
+ // I will cause a 1 second delay
313
+ delay: function($q, $timeout) {
314
+ var delay = $q.defer();
315
+ $timeout(delay.resolve, 1000);
316
+ return delay.promise;
317
+ }
318
+ }
319
+ });
320
+ $routeProvider.when('/Book/:bookId/ch/:chapterId', {
321
+ templateUrl: 'chapter.html',
322
+ controller: ChapterCntl
323
+ });
324
+
325
+ // configure html5 to get links working on jsfiddle
326
+ $locationProvider.html5Mode(true);
327
+ });
328
+
329
+ function MainCntl($scope, $route, $routeParams, $location) {
330
+ $scope.$route = $route;
331
+ $scope.$location = $location;
332
+ $scope.$routeParams = $routeParams;
333
+ }
334
+
335
+ function BookCntl($scope, $routeParams) {
336
+ $scope.name = "BookCntl";
337
+ $scope.params = $routeParams;
338
+ }
339
+
340
+ function ChapterCntl($scope, $routeParams) {
341
+ $scope.name = "ChapterCntl";
342
+ $scope.params = $routeParams;
343
+ }
344
+ </file>
345
+
346
+ <file name="scenario.js">
347
+ it('should load and compile correct template', function() {
348
+ element('a:contains("Moby: Ch1")').click();
349
+ var content = element('.doc-example-live [ng-view]').text();
350
+ expect(content).toMatch(/controller\: ChapterCntl/);
351
+ expect(content).toMatch(/Book Id\: Moby/);
352
+ expect(content).toMatch(/Chapter Id\: 1/);
353
+
354
+ element('a:contains("Scarlet")').click();
355
+ sleep(2); // promises are not part of scenario waiting
356
+ content = element('.doc-example-live [ng-view]').text();
357
+ expect(content).toMatch(/controller\: BookCntl/);
358
+ expect(content).toMatch(/Book Id\: Scarlet/);
359
+ });
360
+ </file>
361
+ </example>
362
+ */
363
+
364
+ /**
365
+ * @ngdoc event
366
+ * @name ngRoute.$route#$routeChangeStart
367
+ * @eventOf ngRoute.$route
368
+ * @eventType broadcast on root scope
369
+ * @description
370
+ * Broadcasted before a route change. At this point the route services starts
371
+ * resolving all of the dependencies needed for the route change to occurs.
372
+ * Typically this involves fetching the view template as well as any dependencies
373
+ * defined in `resolve` route property. Once all of the dependencies are resolved
374
+ * `$routeChangeSuccess` is fired.
375
+ *
376
+ * @param {Object} angularEvent Synthetic event object.
377
+ * @param {Route} next Future route information.
378
+ * @param {Route} current Current route information.
379
+ */
380
+
381
+ /**
382
+ * @ngdoc event
383
+ * @name ngRoute.$route#$routeChangeSuccess
384
+ * @eventOf ngRoute.$route
385
+ * @eventType broadcast on root scope
386
+ * @description
387
+ * Broadcasted after a route dependencies are resolved.
388
+ * {@link ngRoute.directive:ngView ngView} listens for the directive
389
+ * to instantiate the controller and render the view.
390
+ *
391
+ * @param {Object} angularEvent Synthetic event object.
392
+ * @param {Route} current Current route information.
393
+ * @param {Route|Undefined} previous Previous route information, or undefined if current is
394
+ * first route entered.
395
+ */
396
+
397
+ /**
398
+ * @ngdoc event
399
+ * @name ngRoute.$route#$routeChangeError
400
+ * @eventOf ngRoute.$route
401
+ * @eventType broadcast on root scope
402
+ * @description
403
+ * Broadcasted if any of the resolve promises are rejected.
404
+ *
405
+ * @param {Object} angularEvent Synthetic event object
406
+ * @param {Route} current Current route information.
407
+ * @param {Route} previous Previous route information.
408
+ * @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
409
+ */
410
+
411
+ /**
412
+ * @ngdoc event
413
+ * @name ngRoute.$route#$routeUpdate
414
+ * @eventOf ngRoute.$route
415
+ * @eventType broadcast on root scope
416
+ * @description
417
+ *
418
+ * The `reloadOnSearch` property has been set to false, and we are reusing the same
419
+ * instance of the Controller.
420
+ */
421
+
422
+ var forceReload = false,
423
+ $route = {
424
+ routes: routes,
425
+
426
+ /**
427
+ * @ngdoc method
428
+ * @name ngRoute.$route#reload
429
+ * @methodOf ngRoute.$route
430
+ *
431
+ * @description
432
+ * Causes `$route` service to reload the current route even if
433
+ * {@link ng.$location $location} hasn't changed.
434
+ *
435
+ * As a result of that, {@link ngRoute.directive:ngView ngView}
436
+ * creates new scope, reinstantiates the controller.
437
+ */
438
+ reload: function() {
439
+ forceReload = true;
440
+ $rootScope.$evalAsync(updateRoute);
441
+ }
442
+ };
443
+
444
+ $rootScope.$on('$locationChangeSuccess', updateRoute);
445
+
446
+ return $route;
447
+
448
+ /////////////////////////////////////////////////////
449
+
450
+ /**
451
+ * @param on {string} current url
452
+ * @param route {Object} route regexp to match the url against
453
+ * @return {?Object}
454
+ *
455
+ * @description
456
+ * Check if the route matches the current url.
457
+ *
458
+ * Inspired by match in
459
+ * visionmedia/express/lib/router/router.js.
460
+ */
461
+ function switchRouteMatcher(on, route) {
462
+ var keys = route.keys,
463
+ params = {};
464
+
465
+ if (!route.regexp) return null;
466
+
467
+ var m = route.regexp.exec(on);
468
+ if (!m) return null;
469
+
470
+ for (var i = 1, len = m.length; i < len; ++i) {
471
+ var key = keys[i - 1];
472
+
473
+ var val = 'string' == typeof m[i]
474
+ ? decodeURIComponent(m[i])
475
+ : m[i];
476
+
477
+ if (key && val) {
478
+ params[key.name] = val;
479
+ }
480
+ }
481
+ return params;
482
+ }
483
+
484
+ function updateRoute() {
485
+ var next = parseRoute(),
486
+ last = $route.current;
487
+
488
+ if (next && last && next.$$route === last.$$route
489
+ && angular.equals(next.pathParams, last.pathParams)
490
+ && !next.reloadOnSearch && !forceReload) {
491
+ last.params = next.params;
492
+ angular.copy(last.params, $routeParams);
493
+ $rootScope.$broadcast('$routeUpdate', last);
494
+ } else if (next || last) {
495
+ forceReload = false;
496
+ $rootScope.$broadcast('$routeChangeStart', next, last);
497
+ $route.current = next;
498
+ if (next) {
499
+ if (next.redirectTo) {
500
+ if (angular.isString(next.redirectTo)) {
501
+ $location.path(interpolate(next.redirectTo, next.params)).search(next.params)
502
+ .replace();
503
+ } else {
504
+ $location.url(next.redirectTo(next.pathParams, $location.path(), $location.search()))
505
+ .replace();
506
+ }
507
+ }
508
+ }
509
+
510
+ $q.when(next).
511
+ then(function() {
512
+ if (next) {
513
+ var locals = angular.extend({}, next.resolve),
514
+ template, templateUrl;
515
+
516
+ angular.forEach(locals, function(value, key) {
517
+ locals[key] = angular.isString(value) ?
518
+ $injector.get(value) : $injector.invoke(value);
519
+ });
520
+
521
+ if (angular.isDefined(template = next.template)) {
522
+ if (angular.isFunction(template)) {
523
+ template = template(next.params);
524
+ }
525
+ } else if (angular.isDefined(templateUrl = next.templateUrl)) {
526
+ if (angular.isFunction(templateUrl)) {
527
+ templateUrl = templateUrl(next.params);
528
+ }
529
+ templateUrl = $sce.getTrustedResourceUrl(templateUrl);
530
+ if (angular.isDefined(templateUrl)) {
531
+ next.loadedTemplateUrl = templateUrl;
532
+ template = $http.get(templateUrl, {cache: $templateCache}).
533
+ then(function(response) { return response.data; });
534
+ }
535
+ }
536
+ if (angular.isDefined(template)) {
537
+ locals['$template'] = template;
538
+ }
539
+ return $q.all(locals);
540
+ }
541
+ }).
542
+ // after route change
543
+ then(function(locals) {
544
+ if (next == $route.current) {
545
+ if (next) {
546
+ next.locals = locals;
547
+ angular.copy(next.params, $routeParams);
548
+ }
549
+ $rootScope.$broadcast('$routeChangeSuccess', next, last);
550
+ }
551
+ }, function(error) {
552
+ if (next == $route.current) {
553
+ $rootScope.$broadcast('$routeChangeError', next, last, error);
554
+ }
555
+ });
556
+ }
557
+ }
558
+
559
+
560
+ /**
561
+ * @returns the current active route, by matching it against the URL
562
+ */
563
+ function parseRoute() {
564
+ // Match a route
565
+ var params, match;
566
+ angular.forEach(routes, function(route, path) {
567
+ if (!match && (params = switchRouteMatcher($location.path(), route))) {
568
+ match = inherit(route, {
569
+ params: angular.extend({}, $location.search(), params),
570
+ pathParams: params});
571
+ match.$$route = route;
572
+ }
573
+ });
574
+ // No route matched; fallback to "otherwise" route
575
+ return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
576
+ }
577
+
578
+ /**
579
+ * @returns interpolation of the redirect path with the parameters
580
+ */
581
+ function interpolate(string, params) {
582
+ var result = [];
583
+ angular.forEach((string||'').split(':'), function(segment, i) {
584
+ if (i === 0) {
585
+ result.push(segment);
586
+ } else {
587
+ var segmentMatch = segment.match(/(\w+)(.*)/);
588
+ var key = segmentMatch[1];
589
+ result.push(params[key]);
590
+ result.push(segmentMatch[2] || '');
591
+ delete params[key];
592
+ }
593
+ });
594
+ return result.join('');
595
+ }
596
+ }];
597
+ }
598
+
599
+ ngRouteModule.provider('$routeParams', $RouteParamsProvider);
600
+
601
+
602
+ /**
603
+ * @ngdoc object
604
+ * @name ngRoute.$routeParams
605
+ * @requires $route
606
+ *
607
+ * @description
608
+ * The `$routeParams` service allows you to retrieve the current set of route parameters.
609
+ *
610
+ * Requires the {@link ngRoute `ngRoute`} module to be installed.
611
+ *
612
+ * The route parameters are a combination of {@link ng.$location `$location`}'s
613
+ * {@link ng.$location#methods_search `search()`} and {@link ng.$location#methods_path `path()`}.
614
+ * The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched.
615
+ *
616
+ * In case of parameter name collision, `path` params take precedence over `search` params.
617
+ *
618
+ * The service guarantees that the identity of the `$routeParams` object will remain unchanged
619
+ * (but its properties will likely change) even when a route change occurs.
620
+ *
621
+ * Note that the `$routeParams` are only updated *after* a route change completes successfully.
622
+ * This means that you cannot rely on `$routeParams` being correct in route resolve functions.
623
+ * Instead you can use `$route.current.params` to access the new route's parameters.
624
+ *
625
+ * @example
626
+ * <pre>
627
+ * // Given:
628
+ * // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
629
+ * // Route: /Chapter/:chapterId/Section/:sectionId
630
+ * //
631
+ * // Then
632
+ * $routeParams ==> {chapterId:1, sectionId:2, search:'moby'}
633
+ * </pre>
634
+ */
635
+ function $RouteParamsProvider() {
636
+ this.$get = function() { return {}; };
637
+ }
638
+
639
+ ngRouteModule.directive('ngView', ngViewFactory);
640
+
641
+ /**
642
+ * @ngdoc directive
643
+ * @name ngRoute.directive:ngView
644
+ * @restrict ECA
645
+ *
646
+ * @description
647
+ * # Overview
648
+ * `ngView` is a directive that complements the {@link ngRoute.$route $route} service by
649
+ * including the rendered template of the current route into the main layout (`index.html`) file.
650
+ * Every time the current route changes, the included view changes with it according to the
651
+ * configuration of the `$route` service.
652
+ *
653
+ * Requires the {@link ngRoute `ngRoute`} module to be installed.
654
+ *
655
+ * @animations
656
+ * enter - animation is used to bring new content into the browser.
657
+ * leave - animation is used to animate existing content away.
658
+ *
659
+ * The enter and leave animation occur concurrently.
660
+ *
661
+ * @scope
662
+ * @priority 400
663
+ * @example
664
+ <example module="ngViewExample" deps="angular-route.js" animations="true">
665
+ <file name="index.html">
666
+ <div ng-controller="MainCntl as main">
667
+ Choose:
668
+ <a href="Book/Moby">Moby</a> |
669
+ <a href="Book/Moby/ch/1">Moby: Ch1</a> |
670
+ <a href="Book/Gatsby">Gatsby</a> |
671
+ <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
672
+ <a href="Book/Scarlet">Scarlet Letter</a><br/>
673
+
674
+ <div class="view-animate-container">
675
+ <div ng-view class="view-animate"></div>
676
+ </div>
677
+ <hr />
678
+
679
+ <pre>$location.path() = {{main.$location.path()}}</pre>
680
+ <pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
681
+ <pre>$route.current.params = {{main.$route.current.params}}</pre>
682
+ <pre>$route.current.scope.name = {{main.$route.current.scope.name}}</pre>
683
+ <pre>$routeParams = {{main.$routeParams}}</pre>
684
+ </div>
685
+ </file>
686
+
687
+ <file name="book.html">
688
+ <div>
689
+ controller: {{book.name}}<br />
690
+ Book Id: {{book.params.bookId}}<br />
691
+ </div>
692
+ </file>
693
+
694
+ <file name="chapter.html">
695
+ <div>
696
+ controller: {{chapter.name}}<br />
697
+ Book Id: {{chapter.params.bookId}}<br />
698
+ Chapter Id: {{chapter.params.chapterId}}
699
+ </div>
700
+ </file>
701
+
702
+ <file name="animations.css">
703
+ .view-animate-container {
704
+ position:relative;
705
+ height:100px!important;
706
+ position:relative;
707
+ background:white;
708
+ border:1px solid black;
709
+ height:40px;
710
+ overflow:hidden;
711
+ }
712
+
713
+ .view-animate {
714
+ padding:10px;
715
+ }
716
+
717
+ .view-animate.ng-enter, .view-animate.ng-leave {
718
+ -webkit-transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
719
+ transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
720
+
721
+ display:block;
722
+ width:100%;
723
+ border-left:1px solid black;
724
+
725
+ position:absolute;
726
+ top:0;
727
+ left:0;
728
+ right:0;
729
+ bottom:0;
730
+ padding:10px;
731
+ }
732
+
733
+ .view-animate.ng-enter {
734
+ left:100%;
735
+ }
736
+ .view-animate.ng-enter.ng-enter-active {
737
+ left:0;
738
+ }
739
+ .view-animate.ng-leave.ng-leave-active {
740
+ left:-100%;
741
+ }
742
+ </file>
743
+
744
+ <file name="script.js">
745
+ angular.module('ngViewExample', ['ngRoute', 'ngAnimate'],
746
+ function($routeProvider, $locationProvider) {
747
+ $routeProvider.when('/Book/:bookId', {
748
+ templateUrl: 'book.html',
749
+ controller: BookCntl,
750
+ controllerAs: 'book'
751
+ });
752
+ $routeProvider.when('/Book/:bookId/ch/:chapterId', {
753
+ templateUrl: 'chapter.html',
754
+ controller: ChapterCntl,
755
+ controllerAs: 'chapter'
756
+ });
757
+
758
+ // configure html5 to get links working on jsfiddle
759
+ $locationProvider.html5Mode(true);
760
+ });
761
+
762
+ function MainCntl($route, $routeParams, $location) {
763
+ this.$route = $route;
764
+ this.$location = $location;
765
+ this.$routeParams = $routeParams;
766
+ }
767
+
768
+ function BookCntl($routeParams) {
769
+ this.name = "BookCntl";
770
+ this.params = $routeParams;
771
+ }
772
+
773
+ function ChapterCntl($routeParams) {
774
+ this.name = "ChapterCntl";
775
+ this.params = $routeParams;
776
+ }
777
+ </file>
778
+
779
+ <file name="scenario.js">
780
+ it('should load and compile correct template', function() {
781
+ element('a:contains("Moby: Ch1")').click();
782
+ var content = element('.doc-example-live [ng-view]').text();
783
+ expect(content).toMatch(/controller\: ChapterCntl/);
784
+ expect(content).toMatch(/Book Id\: Moby/);
785
+ expect(content).toMatch(/Chapter Id\: 1/);
786
+
787
+ element('a:contains("Scarlet")').click();
788
+ content = element('.doc-example-live [ng-view]').text();
789
+ expect(content).toMatch(/controller\: BookCntl/);
790
+ expect(content).toMatch(/Book Id\: Scarlet/);
791
+ });
792
+ </file>
793
+ </example>
794
+ */
795
+
796
+
797
+ /**
798
+ * @ngdoc event
799
+ * @name ngRoute.directive:ngView#$viewContentLoaded
800
+ * @eventOf ngRoute.directive:ngView
801
+ * @eventType emit on the current ngView scope
802
+ * @description
803
+ * Emitted every time the ngView content is reloaded.
804
+ */
805
+ ngViewFactory.$inject = ['$route', '$anchorScroll', '$compile', '$controller', '$animate'];
806
+ function ngViewFactory( $route, $anchorScroll, $compile, $controller, $animate) {
807
+ return {
808
+ restrict: 'ECA',
809
+ terminal: true,
810
+ priority: 400,
811
+ transclude: 'element',
812
+ link: function(scope, $element, attr, ctrl, $transclude) {
813
+ var currentScope,
814
+ currentElement,
815
+ autoScrollExp = attr.autoscroll,
816
+ onloadExp = attr.onload || '';
817
+
818
+ scope.$on('$routeChangeSuccess', update);
819
+ update();
820
+
821
+ function cleanupLastView() {
822
+ if (currentScope) {
823
+ currentScope.$destroy();
824
+ currentScope = null;
825
+ }
826
+ if(currentElement) {
827
+ $animate.leave(currentElement);
828
+ currentElement = null;
829
+ }
830
+ }
831
+
832
+ function update() {
833
+ var locals = $route.current && $route.current.locals,
834
+ template = locals && locals.$template;
835
+
836
+ if (template) {
837
+ var newScope = scope.$new();
838
+ $transclude(newScope, function(clone) {
839
+ clone.html(template);
840
+ $animate.enter(clone, null, currentElement || $element, function onNgViewEnter () {
841
+ if (angular.isDefined(autoScrollExp)
842
+ && (!autoScrollExp || scope.$eval(autoScrollExp))) {
843
+ $anchorScroll();
844
+ }
845
+ });
846
+
847
+ cleanupLastView();
848
+
849
+ var link = $compile(clone.contents()),
850
+ current = $route.current;
851
+
852
+ currentScope = current.scope = newScope;
853
+ currentElement = clone;
854
+
855
+ if (current.controller) {
856
+ locals.$scope = currentScope;
857
+ var controller = $controller(current.controller, locals);
858
+ if (current.controllerAs) {
859
+ currentScope[current.controllerAs] = controller;
860
+ }
861
+ clone.data('$ngControllerController', controller);
862
+ clone.children().data('$ngControllerController', controller);
863
+ }
864
+
865
+ link(currentScope);
866
+ currentScope.$emit('$viewContentLoaded');
867
+ currentScope.$eval(onloadExp);
868
+ });
869
+ } else {
870
+ cleanupLastView();
871
+ }
872
+ }
873
+ }
874
+ };
875
+ }
876
+
877
+
878
+ })(window, window.angular);