Skip to content

Commit

Permalink
fix(uiView): test pass against 1.0.8 and 1.2.4
Browse files Browse the repository at this point in the history
  • Loading branch information
nateabele committed Dec 28, 2013
1 parent e994389 commit a402415
Show file tree
Hide file tree
Showing 5 changed files with 200 additions and 138 deletions.
4 changes: 2 additions & 2 deletions files.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ routerFiles = {
angular: function(version) {
return [
'lib/angular-' + version + '/angular.js',
'lib/angular-' + version + '/angular-mocks.js',
];
'lib/angular-' + version + '/angular-mocks.js'
].concat(version === '1.2.4' ? ['lib/angular-' + version + '/angular-animate.js'] : []);
}
};

Expand Down
181 changes: 106 additions & 75 deletions src/viewDirective.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
*
* @param {string} ui-view A view name.
*/
$ViewDirective.$inject = ['$state', '$compile', '$controller', '$injector', '$uiViewScroll'];
function $ViewDirective( $state, $compile, $controller, $injector, $uiViewScroll) {
$ViewDirective.$inject = ['$state', '$compile', '$controller', '$injector', '$uiViewScroll', '$document'];
function $ViewDirective( $state, $compile, $controller, $injector, $uiViewScroll, $document) {

function getService() {
return ($injector.has) ? function(service) {
Expand All @@ -30,118 +30,149 @@ function $ViewDirective( $state, $compile, $controller, $injector, $ui
};
}

var viewIsUpdating = false, service = getService(),
$animator = service('$animator'), $animate = service('$animate'),
hasAnimator = !!($animator || $animate);
var viewIsUpdating = false,
service = getService(),
$animator = service('$animator'),
$animate = service('$animate');

// Returns a set of DOM manipulation functions based on whether animation
// should be performed
var renderer = function(shouldAnimate) {
return (hasAnimator && shouldAnimate) ? {
remove: function(element) { $animate.leave(element.contents()); },
// remove: function(element) { animate.leave(element.contents(), element); },
restore: function(compiled, element) { $animate.enter(compiled, element); },
// restore: function(compiled, element) { animate.enter(compiled, element); },
populate: function(template, element) {
var contents = angular.element('<div></div>').html(template).contents();
// animate.enter(contents, element);
$animate.enter(contents, element);
return contents;
}
} : {
remove: function(element) { element.html(''); },
restore: function(compiled, element) { element.append(compiled); },
populate: function(template, element) {
element.html(template);
return element.contents();
}
function getRenderer(element, attrs, scope) {
var statics = function() {
return {
leave: function (element) { element.remove(); },
enter: function (element, parent, anchor) { anchor.after(element); }
};
};
};

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(); }); }
};
};
}

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 statics;
}

var directive = {
restrict: 'ECA',
terminal: true,
priority: 1000,
transclude: true,
compile: function (element, attr, transclude) {
return function(scope, element, attr) {
var viewScope, viewLocals,
name = attr[directive.name] || attr.name || '',
onloadExp = attr.onload || '',
autoscrollExp = attr.autoscroll,
animate = $animator && $animator(scope, attr),
initialView = transclude(scope);

// Put back the compiled initial view
element.append(initialView);

// Find the details of the parent view directive (if any) and use it
// to derive our own qualified view name, then hang our own details
// off the DOM so child directives can find it.
var parent = element.parent().inheritedData('$uiView');
if (name.indexOf('@') < 0) name = name + '@' + (parent ? parent.state.name : '');
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');

var currentScope, currentEl, viewLocals,
name = attrs[directive.name] || attrs.name || '',
onloadExp = attrs.onload || '',
autoscrollExp = attrs.autoscroll,
renderer = getRenderer(element, attrs, $scope);

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

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

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

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

function updateView(doAnimate) {
var locals = $state.$current && $state.$current.locals[name];
if (locals === viewLocals) return; // nothing to do
var render = renderer(doAnimate);
updateView();

// Remove existing content
render.remove(element);
function cleanupLastView() {
if (currentEl) {
renderer(true).leave(currentEl);
currentEl = null;
}

// Destroy previous view scope
if (viewScope) {
viewScope.$destroy();
viewScope = null;
if (currentScope) {
currentScope.$destroy();
currentScope = null;
}
}

if (!locals) {
viewLocals = null;
view.state = null;
function updateView() {
var locals = $state.$current && $state.$current.locals[name];

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

// Restore the initial view
return render.restore(initialView, element);
if (!locals) {
cleanupLastView();
currentEl = element.clone();

This comment has been minimized.

Copy link
@chinchang

chinchang Jan 5, 2014

@nateabele Why was template population modified to clone the ui-view first on every state transition and insert it into DOM? This seems to be causing issues related to CSS transitions on newly appended nodes.
Just wanted to know your reason to decide if I should open an issue for this or not.

currentEl.html(initial);
anchor.after(currentEl);

currentScope = $scope.$new();
$compile(currentEl.contents())(currentScope);
return;
}

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

cleanupLastView();

currentEl = element.clone();
currentEl.html(locals.$template ? locals.$template : initial);
renderer(true).enter(currentEl, parentEl, anchor);

currentEl.data('$uiView', view);

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

var link = $compile(render.populate(locals.$template, element));
viewScope = scope.$new();
var link = $compile(currentEl.contents());

currentScope = $scope.$new();

if (locals.$$controller) {
locals.$scope = viewScope;
locals.$scope = currentScope;
var controller = $controller(locals.$$controller, locals);
element.children().data('$ngControllerController', controller);
currentEl.children().data('$ngControllerController', controller);
}
link(viewScope);
viewScope.$emit('$viewContentLoaded');
if (onloadExp) viewScope.$eval(onloadExp);

if (!angular.isDefined(autoscrollExp) || !autoscrollExp || scope.$eval(autoscrollExp)) {
$uiViewScroll(element);
link(currentScope);

currentScope.$emit('$viewContentLoaded');
if (onloadExp) currentScope.$eval(onloadExp);

if (!angular.isDefined(autoscrollExp) || !autoscrollExp || $scope.$eval(autoscrollExp)) {
$uiViewScroll(currentEl);
}
}
};
}
};

return directive;
}

Expand Down
8 changes: 2 additions & 6 deletions test/compat/matchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,15 +149,11 @@ beforeEach(function() {
angular.element(this.actual).hasClass(clazz);
},

/**
* innerText compatibility shim for Firefox
*/
toMatchText: function(text) {
var isFirefox = /firefox/i.test(navigator.userAgent);
this.message = function() {
return "Expected '" + this.actual.nodeName + "' element to have text '" + text + "'";
return "Expected '" + (this.actual && this.actual.nodeName) + "' element to have text '" + text + "'";
};
return this.actual[isFirefox ? 'textContent' : 'innerText'] === text;
return this.actual && this.actual.text && this.actual.text() === text;
}

});
Expand Down
2 changes: 1 addition & 1 deletion test/stateDirectivesSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ describe('uiStateRef', function() {
scope.$apply();

$compile(el)(scope);
template = $compile(angular.element('<ui-view></ui-view>'))(scope);
template = $compile(angular.element('<div><ui-view></ui-view><div>'))(scope);
scope.$digest();
}));

Expand Down
Loading

0 comments on commit a402415

Please sign in to comment.