Skip to content

Commit

Permalink
fix(uiView): Refactoring uiView directive to copy ngView logic
Browse files Browse the repository at this point in the history
- Changed the structure of the uiView directive to work more like ngView.
- Updated tests to work with new structure.
- Fixes the issue with using ngIf in conjunction with uiView.
- Added in extensive test for ngIf fix.

Closes angular-ui#857, angular-ui#552
  • Loading branch information
Cody Lundquist committed Mar 4, 2014
1 parent d9f7b89 commit 72d5907
Show file tree
Hide file tree
Showing 2 changed files with 162 additions and 108 deletions.
174 changes: 88 additions & 86 deletions src/viewDirective.js
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,8 @@
* <ui-view autoscroll='scopeVariable'/>
* </pre>
*/
$ViewDirective.$inject = ['$state', '$compile', '$controller', '$injector', '$uiViewScroll', '$document'];
function $ViewDirective( $state, $compile, $controller, $injector, $uiViewScroll, $document) {
$ViewDirective.$inject = ['$state', '$injector', '$uiViewScroll'];
function $ViewDirective( $state, $injector, $uiViewScroll) {

function getService() {
return ($injector.has) ? function(service) {
Expand All @@ -135,135 +135,98 @@ function $ViewDirective( $state, $compile, $controller, $injector, $ui
$animator = service('$animator'),
$animate = service('$animate');

// Returns a set of DOM manipulation functions based on whether animation
// should be performed
function getRenderer(element, attrs, scope) {
// Returns a set of DOM manipulation functions based on which Angular version
// it should use
function getRenderer(attrs, scope) {
var statics = function() {
return {
leave: function (element) { element.remove(); },
enter: function (element, parent, anchor) { anchor.after(element); }
enter: function (element, target) { target.after(element); },
leave: function (element) { element.remove(); }
};
};

if ($animate) {
return function(shouldAnimate) {
return !shouldAnimate ? statics() : {
enter: function(element, parent, anchor) { $animate.enter(element, null, anchor); },
leave: function(element) { $animate.leave(element, function() { element.remove(); }); }
};
return {
enter: function(element, target) { $animate.enter(element, null, target); },
leave: function(element) { $animate.leave(element); }
};
}

if ($animator) {
var animate = $animator && $animator(scope, attrs);

return function(shouldAnimate) {
return !shouldAnimate ? statics() : {
enter: function(element, parent, anchor) { animate.enter(element, parent); },
leave: function(element) { animate.leave(element.contents(), element); }
};
return {
enter: function(element, target) { animate.enter(element, null, target); },
leave: function(element) { animate.leave(element.contents(), element); }
};
}

return statics;
return statics();
}

var directive = {
restrict: 'ECA',
compile: function (element, attrs) {
var initial = element.html(),
isDefault = true,
anchor = angular.element($document[0].createComment(' ui-view-anchor ')),
parentEl = element.parent();

element.prepend(anchor);

return function ($scope) {
var inherited = parentEl.inheritedData('$uiView');

terminal: true,
priority: 400,
transclude: 'element',
compile: function (tElement, tAttrs, $transclude) {
return function (scope, $element, attrs) {
var currentScope, currentEl, viewLocals,
name = attrs[directive.name] || attrs.name || '',
onloadExp = attrs.onload || '',
loaded = false,
onloadExp = attrs.onload || '',
autoscrollExp = attrs.autoscroll,
renderer = getRenderer(element, attrs, $scope);
renderer = getRenderer(attrs, scope),
parentEl = $element.parent(),
inherited = parentEl.inheritedData('$uiView'),
name = attrs[directive.name] || attrs.name || '';

if (name.indexOf('@') < 0) name = name + '@' + (inherited ? inherited.state.name : '');
var view = { name: name, state: null };

var eventHook = function () {
if (viewIsUpdating) return;

viewIsUpdating = true;

try { updateView(true); } catch (e) {
viewIsUpdating = false;
try { updateView(); } catch (e) {
throw e;
} finally {
viewIsUpdating = false;
}
viewIsUpdating = false;
};

$scope.$on('$stateChangeSuccess', eventHook);
$scope.$on('$viewContentLoading', eventHook);

updateView(false);
scope.$on('$stateChangeSuccess', eventHook);
scope.$on('$viewContentLoading', eventHook);
updateView();

function cleanupLastView() {
if (currentEl) {
renderer(true).leave(currentEl);
currentEl = null;
}

if (currentScope) {
currentScope.$destroy();
currentScope = null;
}
}

function updateView(shouldAnimate) {
var locals = $state.$current && $state.$current.locals[name];

if (isDefault) {
isDefault = false;
element.replaceWith(anchor);
}

if (!locals) {
cleanupLastView();
currentEl = element.clone();
currentEl.html(initial);
renderer(shouldAnimate).enter(currentEl, parentEl, anchor);

currentScope = $scope.$new();
$compile(currentEl.contents())(currentScope);
return;
if (currentEl) {
renderer.leave(currentEl);
currentEl = null;
}
}

if (locals === viewLocals) return; // nothing to do

cleanupLastView();

currentEl = element.clone();
currentEl.html(locals.$template ? locals.$template : initial);
renderer(true).enter(currentEl, parentEl, anchor);
function updateView() {
var newScope = scope.$new(),
locals = $state.$current && $state.$current.locals[name];

currentEl.data('$uiView', view);
if (loaded && locals === viewLocals) return; // nothing to do

loaded = true;
viewLocals = locals;
view.state = locals.$$state;

var link = $compile(currentEl.contents());

currentScope = $scope.$new();

if (locals.$$controller) {
locals.$scope = currentScope;
var controller = $controller(locals.$$controller, locals);
if ($state.$current.controllerAs) {
currentScope[$state.$current.controllerAs] = controller;
}
currentEl.children().data('$ngControllerController', controller);
}
var clone = $transclude(newScope, function(clone) {
clone.data('$uiViewName', name);
renderer.enter(clone, currentEl || $element);
cleanupLastView();
});

link(currentScope);
currentEl = clone;
currentScope = newScope;

/**
* @ngdoc event
Expand All @@ -278,7 +241,7 @@ function $ViewDirective( $state, $compile, $controller, $injector, $ui
currentScope.$emit('$viewContentLoaded');
if (onloadExp) currentScope.$eval(onloadExp);

if (!angular.isDefined(autoscrollExp) || !autoscrollExp || $scope.$eval(autoscrollExp)) {
if (!angular.isDefined(autoscrollExp) || !autoscrollExp || scope.$eval(autoscrollExp)) {
$uiViewScroll(currentEl);
}
}
Expand All @@ -289,4 +252,43 @@ function $ViewDirective( $state, $compile, $controller, $injector, $ui
return directive;
}

$ViewDirectiveFill.$inject = ['$compile', '$controller', '$state'];
function $ViewDirectiveFill ($compile, $controller, $state) {
return {
restrict: 'ECA',
priority: -400,
compile: function (tElement) {
var initial = tElement.html();

return function (scope, $element) {
var current = $state.$current,
name = $element.data('$uiViewName'),
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;
var controller = $controller(locals.$$controller, locals);
if (current.controllerAs) {
scope[current.controllerAs] = controller;
}
$element.data('$ngControllerController', controller);
$element.children().data('$ngControllerController', controller);
}

link(scope);
};
}
};
}

angular.module('ui.router.state').directive('uiView', $ViewDirective);
angular.module('ui.router.state').directive('uiView', $ViewDirectiveFill);
Loading

0 comments on commit 72d5907

Please sign in to comment.