arnaucube
arnaucube/commonroutesAppPublic Self-hosted repository
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);