| /** |
| * @ngdoc directive |
| * @name ui.router.state.directive:ui-view |
| * |
| * @requires ui.router.state.$state |
| * @requires $compile |
| * @requires $controller |
| * @requires $injector |
| * @requires ui.router.state.$uiViewScroll |
| * @requires $document |
| * |
| * @restrict ECA |
| * |
| * @description |
| * The ui-view directive tells $state where to place your templates. |
| * |
| * @param {string=} name A view name. The name should be unique amongst the other views in the |
| * same state. You can have views of the same name that live in different states. |
| * |
| * @param {string=} autoscroll It allows you to set the scroll behavior of the browser window |
| * when a view is populated. By default, $anchorScroll is overridden by ui-router's custom scroll |
| * service, {@link ui.router.state.$uiViewScroll}. This custom service let's you |
| * scroll ui-view elements into view when they are populated during a state activation. |
| * |
| * *Note: To revert back to old [`$anchorScroll`](http://docs.angularjs.org/api/ng.$anchorScroll) |
| * functionality, call `$uiViewScrollProvider.useAnchorScroll()`.* |
| * |
| * @param {string=} onload Expression to evaluate whenever the view updates. |
| * |
| * @example |
| * A view can be unnamed or named. |
| * <pre> |
| * <!-- Unnamed --> |
| * <div ui-view></div> |
| * |
| * <!-- Named --> |
| * <div ui-view="viewName"></div> |
| * </pre> |
| * |
| * You can only have one unnamed view within any template (or root html). If you are only using a |
| * single view and it is unnamed then you can populate it like so: |
| * <pre> |
| * <div ui-view></div> |
| * $stateProvider.state("home", { |
| * template: "<h1>HELLO!</h1>" |
| * }) |
| * </pre> |
| * |
| * The above is a convenient shortcut equivalent to specifying your view explicitly with the {@link ui.router.state.$stateProvider#views `views`} |
| * config property, by name, in this case an empty name: |
| * <pre> |
| * $stateProvider.state("home", { |
| * views: { |
| * "": { |
| * template: "<h1>HELLO!</h1>" |
| * } |
| * } |
| * }) |
| * </pre> |
| * |
| * But typically you'll only use the views property if you name your view or have more than one view |
| * in the same template. There's not really a compelling reason to name a view if its the only one, |
| * but you could if you wanted, like so: |
| * <pre> |
| * <div ui-view="main"></div> |
| * </pre> |
| * <pre> |
| * $stateProvider.state("home", { |
| * views: { |
| * "main": { |
| * template: "<h1>HELLO!</h1>" |
| * } |
| * } |
| * }) |
| * </pre> |
| * |
| * Really though, you'll use views to set up multiple views: |
| * <pre> |
| * <div ui-view></div> |
| * <div ui-view="chart"></div> |
| * <div ui-view="data"></div> |
| * </pre> |
| * |
| * <pre> |
| * $stateProvider.state("home", { |
| * views: { |
| * "": { |
| * template: "<h1>HELLO!</h1>" |
| * }, |
| * "chart": { |
| * template: "<chart_thing/>" |
| * }, |
| * "data": { |
| * template: "<data_thing/>" |
| * } |
| * } |
| * }) |
| * </pre> |
| * |
| * Examples for `autoscroll`: |
| * |
| * <pre> |
| * <!-- If autoscroll present with no expression, |
| * then scroll ui-view into view --> |
| * <ui-view autoscroll/> |
| * |
| * <!-- If autoscroll present with valid expression, |
| * then scroll ui-view into view if expression evaluates to true --> |
| * <ui-view autoscroll='true'/> |
| * <ui-view autoscroll='false'/> |
| * <ui-view autoscroll='scopeVariable'/> |
| * </pre> |
| */ |
| $ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate']; |
| function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate) { |
| |
| function getService() { |
| return ($injector.has) ? function(service) { |
| return $injector.has(service) ? $injector.get(service) : null; |
| } : function(service) { |
| try { |
| return $injector.get(service); |
| } catch (e) { |
| return null; |
| } |
| }; |
| } |
| |
| var service = getService(), |
| $animator = service('$animator'), |
| $animate = service('$animate'); |
| |
| // Returns a set of DOM manipulation functions based on which Angular version |
| // it should use |
| function getRenderer(attrs, scope) { |
| var statics = function() { |
| return { |
| enter: function (element, target, cb) { target.after(element); cb(); }, |
| leave: function (element, cb) { element.remove(); cb(); } |
| }; |
| }; |
| |
| if ($animate) { |
| return { |
| enter: function(element, target, cb) { |
| var promise = $animate.enter(element, null, target, cb); |
| if (promise && promise.then) promise.then(cb); |
| }, |
| leave: function(element, cb) { |
| var promise = $animate.leave(element, cb); |
| if (promise && promise.then) promise.then(cb); |
| } |
| }; |
| } |
| |
| if ($animator) { |
| var animate = $animator && $animator(scope, attrs); |
| |
| return { |
| enter: function(element, target, cb) {animate.enter(element, null, target); cb(); }, |
| leave: function(element, cb) { animate.leave(element); cb(); } |
| }; |
| } |
| |
| return statics(); |
| } |
| |
| var directive = { |
| restrict: 'ECA', |
| terminal: true, |
| priority: 400, |
| transclude: 'element', |
| compile: function (tElement, tAttrs, $transclude) { |
| return function (scope, $element, attrs) { |
| var previousEl, currentEl, currentScope, latestLocals, |
| onloadExp = attrs.onload || '', |
| autoScrollExp = attrs.autoscroll, |
| renderer = getRenderer(attrs, scope); |
| |
| scope.$on('$stateChangeSuccess', function() { |
| updateView(false); |
| }); |
| scope.$on('$viewContentLoading', function() { |
| updateView(false); |
| }); |
| |
| updateView(true); |
| |
| function cleanupLastView() { |
| if (previousEl) { |
| previousEl.remove(); |
| previousEl = null; |
| } |
| |
| if (currentScope) { |
| currentScope.$destroy(); |
| currentScope = null; |
| } |
| |
| if (currentEl) { |
| renderer.leave(currentEl, function() { |
| previousEl = null; |
| }); |
| |
| previousEl = currentEl; |
| currentEl = null; |
| } |
| } |
| |
| function updateView(firstTime) { |
| var newScope, |
| name = getUiViewName(scope, attrs, $element, $interpolate), |
| previousLocals = name && $state.$current && $state.$current.locals[name]; |
| |
| if (!firstTime && previousLocals === latestLocals) return; // nothing to do |
| newScope = scope.$new(); |
| latestLocals = $state.$current.locals[name]; |
| |
| var clone = $transclude(newScope, function(clone) { |
| renderer.enter(clone, $element, function onUiViewEnter() { |
| if(currentScope) { |
| currentScope.$emit('$viewContentAnimationEnded'); |
| } |
| |
| if (angular.isDefined(autoScrollExp) && !autoScrollExp || scope.$eval(autoScrollExp)) { |
| $uiViewScroll(clone); |
| } |
| }); |
| cleanupLastView(); |
| }); |
| |
| currentEl = clone; |
| currentScope = newScope; |
| /** |
| * @ngdoc event |
| * @name ui.router.state.directive:ui-view#$viewContentLoaded |
| * @eventOf ui.router.state.directive:ui-view |
| * @eventType emits on ui-view directive scope |
| * @description * |
| * Fired once the view is **loaded**, *after* the DOM is rendered. |
| * |
| * @param {Object} event Event object. |
| */ |
| currentScope.$emit('$viewContentLoaded'); |
| currentScope.$eval(onloadExp); |
| } |
| }; |
| } |
| }; |
| |
| return directive; |
| } |
| |
| $ViewDirectiveFill.$inject = ['$compile', '$controller', '$state', '$interpolate']; |
| function $ViewDirectiveFill ( $compile, $controller, $state, $interpolate) { |
| return { |
| restrict: 'ECA', |
| priority: -400, |
| compile: function (tElement) { |
| var initial = tElement.html(); |
| return function (scope, $element, attrs) { |
| var current = $state.$current, |
| name = getUiViewName(scope, attrs, $element, $interpolate), |
| locals = current && current.locals[name]; |
| |
| if (! locals) { |
| return; |
| } |
| |
| $element.data('$uiView', { name: name, state: locals.$$state }); |
| $element.html(locals.$template ? locals.$template : initial); |
| |
| var link = $compile($element.contents()); |
| |
| if (locals.$$controller) { |
| locals.$scope = scope; |
| locals.$element = $element; |
| var controller = $controller(locals.$$controller, locals); |
| if (locals.$$controllerAs) { |
| scope[locals.$$controllerAs] = controller; |
| } |
| $element.data('$ngControllerController', controller); |
| $element.children().data('$ngControllerController', controller); |
| } |
| |
| link(scope); |
| }; |
| } |
| }; |
| } |
| |
| /** |
| * Shared ui-view code for both directives: |
| * Given scope, element, and its attributes, return the view's name |
| */ |
| function getUiViewName(scope, attrs, element, $interpolate) { |
| var name = $interpolate(attrs.uiView || attrs.name || '')(scope); |
| var inherited = element.inheritedData('$uiView'); |
| return name.indexOf('@') >= 0 ? name : (name + '@' + (inherited ? inherited.state.name : '')); |
| } |
| |
| angular.module('ui.router.state').directive('uiView', $ViewDirective); |
| angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill); |