Repository URL to install this package:
|
Version:
1.1.1-1195 ▾
|
/**
* @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#methods_state `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>
*
* Resolve data:
*
* The resolved data from the state's `resolve` block is placed on the scope as `$resolve` (this
* can be customized using [[ViewDeclaration.resolveAs]]). This can be then accessed from the template.
*
* Note that when `controllerAs` is being used, `$resolve` is set on the controller instance *after* the
* controller is instantiated. The `$onInit()` hook can be used to perform initialization code which
* depends on `$resolve` data.
*
* Example usage of $resolve in a view template
* <pre>
* $stateProvider.state('home', {
* template: '<my-component user="$resolve.user"></my-component>',
* resolve: {
* user: function(UserService) { return UserService.fetchUser(); }
* }
* });
* </pre>
*/
$ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll', '$interpolate', '$q'];
function $ViewDirective( $state, $injector, $uiViewScroll, $interpolate, $q) {
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) {
if (angular.version.minor > 2) {
$animate.enter(element, null, target).then(cb);
} else {
$animate.enter(element, null, target, cb);
}
},
leave: function(element, cb) {
if (angular.version.minor > 2) {
$animate.leave(element).then(cb);
} else {
$animate.leave(element, 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),
inherited = $element.inheritedData('$uiView');
scope.$on('$stateChangeSuccess', function() {
updateView(false);
});
updateView(true);
function cleanupLastView() {
if (previousEl) {
previousEl.remove();
previousEl = null;
}
if (currentScope) {
currentScope.$destroy();
currentScope = null;
}
if (currentEl) {
var $uiViewData = currentEl.data('$uiViewAnim');
renderer.leave(currentEl, function() {
$uiViewData.$$animLeave.resolve();
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];
/**
* @ngdoc event
* @name ui.router.state.directive:ui-view#$viewContentLoading
* @eventOf ui.router.state.directive:ui-view
* @eventType emits on ui-view directive scope
* @description
*
* Fired once the view **begins loading**, *before* the DOM is rendered.
*
* @param {Object} event Event object.
* @param {string} viewName Name of the view.
*/
newScope.$emit('$viewContentLoading', name);
var clone = $transclude(newScope, function(clone) {
var animEnter = $q.defer(), animLeave = $q.defer();
var viewAnimData = {
$animEnter: animEnter.promise,
$animLeave: animLeave.promise,
$$animLeave: animLeave
};
clone.data('$uiViewAnim', viewAnimData);
renderer.enter(clone, $element, function onUiViewEnter() {
animEnter.resolve();
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.
* @param {string} viewName Name of the view.
*/
currentScope.$emit('$viewContentLoaded', name);
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();
if (tElement.empty) {
tElement.empty();
} else {
// ng 1.0.0 doesn't have empty(), which cleans up data and handlers
tElement[0].innerHTML = null;
}
return function (scope, $element, attrs) {
var current = $state.$current,
name = getUiViewName(scope, attrs, $element, $interpolate),
locals = current && current.locals[name];
if (! locals) {
$element.html(initial);
$compile($element.contents())(scope);
return;
}
$element.data('$uiView', { name: name, state: locals.$$state });
$element.html(locals.$template ? locals.$template : initial);
var resolveData = angular.extend({}, locals);
scope[locals.$$resolveAs] = resolveData;
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;
scope[locals.$$controllerAs][locals.$$resolveAs] = resolveData;
}
if (isFunction(controller.$onInit)) controller.$onInit();
$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 uiViewCreatedBy = element.inheritedData('$uiView');
return name.indexOf('@') >= 0 ? name : (name + '@' + (uiViewCreatedBy ? uiViewCreatedBy.state.name : ''));
}
angular.module('ui.router.state').directive('uiView', $ViewDirective);
angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill);