Skip to content

Commit dc74200

Browse files
author
Adam Bradley
committed
refactor navView and viewService
1 parent fb4dafd commit dc74200

File tree

2 files changed

+162
-125
lines changed

2 files changed

+162
-125
lines changed

js/ext/angular/src/directive/ionicViewState.js

Lines changed: 61 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -249,107 +249,95 @@ angular.module('ionic.ui.viewState', ['ionic.service.view', 'ionic.service.gestu
249249
}])
250250

251251

252-
.directive('navView', ['$ionicViewService', '$state', '$anchorScroll', '$compile', '$controller', '$animate',
253-
function( $ionicViewService, $state, $anchorScroll, $compile, $controller, $animate) {
254-
252+
.directive('navView', ['$ionicViewService', '$state', '$compile', '$controller',
253+
function( $ionicViewService, $state, $compile, $controller) {
254+
// IONIC's fork of Angular UI Router, v0.2.7
255+
// the navView handles registering views in the history, which animation to use, and which
255256
var viewIsUpdating = false;
256-
var animation;
257257

258258
var directive = {
259259
restrict: 'E',
260260
terminal: true,
261261
priority: 2000,
262262
transclude: true,
263-
264-
link: function(scope, $element, attr, ctrl, $transclude) {
265-
var currentElement,
266-
autoScrollExp = attr.autoscroll,
267-
onloadExp = attr.onload || '',
268-
viewLocals,
269-
viewScope,
270-
name = attr[directive.name] || attr.name || '',
271-
parent = $element.parent().inheritedData('$uiView');
272-
273-
if (name.indexOf('@') < 0) name = name + '@' + (parent ? parent.state.name : '');
274-
var view = { name: name, state: null, animation: null };
275-
$element.data('$uiView', view);
276-
277-
var climbElement = $element[0];
278-
while(!animation && climbElement) {
279-
animation = climbElement.getAttribute('animation');
280-
climbElement = climbElement.parentElement;
281-
}
282-
283-
var eventHook = function() {
284-
if (viewIsUpdating) return;
285-
viewIsUpdating = true;
286-
287-
try { update(true); } catch (e) {
263+
compile: function (element, attr, transclude) {
264+
return function(scope, element, attr) {
265+
var viewScope, viewLocals,
266+
name = attr[directive.name] || attr.name || '',
267+
onloadExp = attr.onload || '',
268+
initialView = transclude(scope);
269+
270+
// Put back the compiled initial view
271+
element.append(initialView);
272+
273+
// Find the details of the parent view directive (if any) and use it
274+
// to derive our own qualified view name, then hang our own details
275+
// off the DOM so child directives can find it.
276+
var parent = element.parent().inheritedData('$uiView');
277+
if (name.indexOf('@') < 0) name = name + '@' + (parent ? parent.state.name : '');
278+
var view = { name: name, state: null };
279+
element.data('$uiView', view);
280+
281+
var eventHook = function() {
282+
if (viewIsUpdating) return;
283+
viewIsUpdating = true;
284+
285+
try { updateView(true); } catch (e) {
286+
viewIsUpdating = false;
287+
throw e;
288+
}
288289
viewIsUpdating = false;
289-
throw e;
290-
}
291-
viewIsUpdating = false;
292-
};
293-
294-
scope.$on('$stateChangeSuccess', eventHook);
295-
scope.$on('$viewContentLoading', eventHook);
296-
update(false);
290+
};
297291

298-
function update(doAnimation) {
299-
var locals = $state.$current && $state.$current.locals[name],
300-
template = (locals && locals.$template ? locals.$template : null);
292+
scope.$on('$stateChangeSuccess', eventHook);
293+
scope.$on('$viewContentLoading', eventHook);
294+
updateView(false);
301295

302-
if (locals === viewLocals) return; // nothing to do here, go about your business
296+
function updateView(doAnimate) {
297+
var locals = $state.$current && $state.$current.locals[name];
298+
if (locals === viewLocals) return; // nothing to do
299+
var renderer = $ionicViewService.getRenderer(element, attr, scope);
303300

304-
var transitionOptions = {
305-
parentElement: $element,
306-
doAnimation: doAnimation,
307-
leavingScope: viewScope,
308-
leavingElement: currentElement,
309-
navDirection: null
310-
};
301+
// Destroy previous view scope
302+
if (viewScope) {
303+
viewScope.$destroy();
304+
viewScope = null;
305+
}
311306

312-
if (template) {
313-
currentElement = angular.element(template.trim());
307+
if (!locals) {
308+
viewLocals = null;
309+
view.state = null;
314310

315-
var registerData = {};
316-
if(currentElement[0].tagName !== 'TABS') {
317-
// the tabs directive shouldn't register in the view history (its tab will)
318-
registerData = $ionicViewService.register(scope);
319-
transitionOptions.navDirection = registerData.navDirection;
311+
// Restore the initial view
312+
return element.append(initialView);
320313
}
321314

315+
var newElement = angular.element('<div></div>').html(locals.$template).contents();
316+
renderer().register(newElement);
317+
318+
// Remove existing content
319+
renderer(doAnimate).leave();
320+
322321
viewLocals = locals;
323322
view.state = locals.$$state;
324323

325-
var link = $compile(currentElement),
326-
current = $state.current;
324+
renderer(doAnimate).enter(newElement);
327325

328-
viewScope = current.scope = scope.$new();
329-
viewScope.$navDirection = transitionOptions.navDirection;
326+
var link = $compile(newElement);
327+
viewScope = scope.$new();
330328

331329
if (locals.$$controller) {
332330
locals.$scope = viewScope;
333331
var controller = $controller(locals.$$controller, locals);
334-
if (current.controllerAs) {
335-
viewScope[current.controllerAs] = controller;
336-
}
337-
currentElement.data('$ngControllerController', controller);
338-
currentElement.children().data('$ngControllerController', controller);
332+
element.children().data('$ngControllerController', controller);
339333
}
340-
341334
link(viewScope);
342-
343335
viewScope.$emit('$viewContentLoaded');
344-
viewScope.$eval(onloadExp);
345-
viewScope.animation = animation;
336+
if (onloadExp) viewScope.$eval(onloadExp);
346337

347-
transitionOptions.enteringScope = viewScope;
348-
transitionOptions.enteringElement = currentElement;
338+
newElement = null;
349339
}
350-
351-
$ionicViewService.transition(transitionOptions);
352-
}
340+
};
353341
}
354342
};
355343
return directive;

js/ext/angular/src/service/ionicView.js

Lines changed: 101 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
angular.module('ionic.service.view', ['ui.router'])
22

33

4-
.run( ['$rootScope', '$state', '$location', '$document',
5-
function( $rootScope, $state, $location, $document) {
4+
.run( ['$rootScope', '$state', '$location', '$document', '$animate',
5+
function( $rootScope, $state, $location, $document, $animate) {
66

77
// init the variables that keep track of the view history
88
$rootScope.$viewHistory = {
99
histories: { root: { historyId: 'root', parentHistoryId: null, stack: [], cursor: -1 } },
1010
backView: null,
1111
forwardView: null,
12-
currentView: null
12+
currentView: null,
13+
disabledRegistrableTagNames: []
1314
};
1415

1516
$rootScope.$on('viewState.changeHistory', function(e, data) {
@@ -95,7 +96,8 @@ angular.module('ionic.service.view', ['ui.router'])
9596

9697
return {
9798

98-
register: function(containerScope) {
99+
register: function(containerScope, element) {
100+
99101
var viewHistory = $rootScope.$viewHistory,
100102
currentStateId = this.getCurrentStateId(),
101103
hist = this._getHistory(containerScope),
@@ -109,6 +111,15 @@ angular.module('ionic.service.view', ['ui.router'])
109111
historyId: hist.historyId
110112
};
111113

114+
if(element && !this.isTagNameRegistrable(element)) {
115+
// first check to see if this element can even be registered as a view.
116+
// Certain tags are only containers for views, but are not views themselves.
117+
// For example, the <tabs> directive contains a <tab> and the <tab> is the
118+
// view, but the <tabs> directive itself should not be registered as a view.
119+
rsp.navAction = 'disabledByTagName';
120+
return rsp;
121+
}
122+
112123
if(currentView &&
113124
currentView.stateId === currentStateId &&
114125
currentView.historyId === hist.historyId) {
@@ -328,67 +339,105 @@ angular.module('ionic.service.view', ['ui.router'])
328339
return { historyId: 'root', scope: $rootScope };
329340
},
330341

331-
transition: function(opts) {
332-
if(!opts || !opts.enteringElement) return;
342+
getRenderer: function(navViewElement, navViewAttrs, navViewScope) {
343+
var service = this;
344+
var registerData;
345+
var doAnimation;
346+
347+
// climb up the DOM and see which animation classname to use, if any
348+
var animationClass = null;
349+
var el = navViewElement[0];
350+
while(!animationClass && el) {
351+
animationClass = el.getAttribute('animation');
352+
el = el.parentElement;
353+
}
354+
el = null;
355+
356+
function setAnimationClass() {
357+
// add the animation CSS class we're gonna use to transition between views
358+
navViewElement[0].classList.add(animationClass);
333359

334-
if (opts.leavingScope) {
335-
opts.leavingScope.$destroy();
336-
opts.leavingScope = null;
360+
if(registerData.navDirection === 'back') {
361+
// animate like we're moving backward
362+
navViewElement[0].classList.add('reverse');
363+
} else {
364+
// defaults to animate forward
365+
// make sure the reverse class isn't already added
366+
navViewElement[0].classList.remove('reverse');
367+
}
337368
}
338369

339-
// use the directive's animation attribute first
340-
// if it doesn't exist, then use the given animation
341-
var animationClass = opts.animation || getAnimationClass();
370+
return function(shouldAnimate) {
342371

343-
if($animate && animationClass && opts.doAnimation !== false && opts.navDirection) {
344-
// set the animation we're gonna use
345-
this.setAnimationClass(opts.parentElement, animationClass, opts.navDirection);
346-
opts.enteringElement.addClass('ng-enter');
372+
return {
373+
374+
enter: function(element) {
347375

348-
// disable any pointer-events from being able to fire
349-
document.body.classList.add('disable-pointer-events');
376+
if(doAnimation && shouldAnimate) {
377+
// enter with an animation
378+
setAnimationClass();
350379

351-
// start the animations
352-
if(opts.leavingElement) {
353-
$animate.leave(opts.leavingElement, function() {
354-
// re-enable pointer-events
355-
document.body.classList.remove('disable-pointer-events');
356-
});
357-
}
358-
$animate.enter(opts.enteringElement, opts.parentElement);
380+
element.addClass('ng-enter');
381+
document.body.classList.add('disable-pointer-events');
359382

360-
} else {
361-
// no animation, just plain ol' add/remove DOM elements
362-
if(opts.leavingElement) {
363-
opts.leavingElement.remove();
364-
}
365-
opts.parentElement.append(opts.enteringElement);
366-
}
383+
$animate.enter(element, navViewElement, null, function() {
384+
document.body.classList.remove('disable-pointer-events');
385+
});
386+
return;
387+
}
388+
389+
// no animation
390+
navViewElement.append(element);
391+
},
367392

368-
function getAnimationClass(){
369-
// go up the ancestors looking for an animation value
370-
var climbScope = opts.enteringScope;
371-
while(climbScope) {
372-
if(climbScope.animation) {
373-
return climbScope.animation;
393+
leave: function() {
394+
var element = navViewElement.contents();
395+
396+
if(doAnimation && shouldAnimate) {
397+
// leave with an animation
398+
setAnimationClass();
399+
400+
$animate.leave(element, function() {
401+
element.remove();
402+
});
403+
return;
404+
}
405+
406+
// no animation
407+
element.remove();
408+
},
409+
410+
register: function(element) {
411+
// register a new view
412+
registerData = service.register(navViewScope, element);
413+
doAnimation = (animationClass !== null && registerData.navDirection !== null);
374414
}
375-
climbScope = climbScope.$parent;
376-
}
377-
}
415+
416+
};
417+
};
378418
},
379419

380-
setAnimationClass: function(element, animationClass, navDirection) {
381-
// add the animation we're gonna use
382-
element[0].classList.add(animationClass);
420+
disableRegisterByTagName: function(tagName) {
421+
// not every element should animate betwee transitions
422+
// For example, the <tabs> directive should not animate when it enters,
423+
// but instead the <tabs> directve would just show, and its children
424+
// <tab> directives would do the animating, but <tabs> itself is not a view
425+
$rootScope.$viewHistory.disabledRegistrableTagNames.push(tagName.toUpperCase());
426+
},
383427

384-
if(navDirection === 'back') {
385-
// animate backward
386-
element[0].classList.add('reverse');
387-
} else {
388-
// defaults to animate forward
389-
// make sure the reverse class isn't already added
390-
element[0].classList.remove('reverse');
428+
isTagNameRegistrable: function(element) {
429+
// check if this element has a tagName (at its root, not recursively)
430+
// that shouldn't be animated, like <tabs> or <side-menu>
431+
var x, y, disabledTags = $rootScope.$viewHistory.disabledRegistrableTagNames;
432+
for(x=0; x<element.length; x++) {
433+
if(element[x].nodeType !== 1) continue;
434+
for(y=0; y<disabledTags.length; y++) {
435+
if(element[x].tagName === disabledTags[y]) {
436+
return false;
437+
}
438+
}
391439
}
440+
return true;
392441
},
393442

394443
clearHistory: function() {

0 commit comments

Comments
 (0)