Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions js/angular/controller/navViewController.js
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate,
if (navViewAttr(viewElement) == VIEW_STATUS_ACTIVE) {
viewScope = viewElement.scope();
viewScope && viewScope.$emit(ev.name.replace('Tabs', 'View'), data);
viewScope && viewScope.$broadcast(ev.name.replace('Tabs', 'ParentView'), data);
break;
}
}
Expand Down
30 changes: 30 additions & 0 deletions js/angular/directive/view.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@
* show. Also contained is transition data, such as the transition type and
* direction that will be or was used.
*
* Life cycle events are emitted upwards from the transitioning view's scope. In some cases, it is
* desirable for a child/nested view to be notified of the event.
* For this use case, `$ionicParentView` life cycle events are broadcast downwards.
*
* <table class="table">
* <tr>
* <td><code>$ionicView.loaded</code></td>
Expand Down Expand Up @@ -83,6 +87,32 @@
* <td>The view's controller has been destroyed and its element has been
* removed from the DOM.</td>
* </tr>
* <tr>
* <td><code>$ionicParentView.enter</code></td>
* <td>The parent view has fully entered and is now the active view.
* This event will fire, whether it was the first load or a cached view.</td>
* </tr>
* <tr>
* <td><code>$ionicParentView.leave</code></td>
* <td>The parent view has finished leaving and is no longer the
* active view. This event will fire, whether it is cached or destroyed.</td>
* </tr>
* <tr>
* <td><code>$ionicParentView.beforeEnter</code></td>
* <td>The parent view is about to enter and become the active view.</td>
* </tr>
* <tr>
* <td><code>$ionicParentView.beforeLeave</code></td>
* <td>The parent view is about to leave and no longer be the active view.</td>
* </tr>
* <tr>
* <td><code>$ionicParentView.afterEnter</code></td>
* <td>The parent view has fully entered and is now the active view.</td>
* </tr>
* <tr>
* <td><code>$ionicParentView.afterLeave</code></td>
* <td>The parent view has finished leaving and is no longer the active view.</td>
* </tr>
* </table>
*
* ## LifeCycle Event Usage
Expand Down
3 changes: 0 additions & 3 deletions js/angular/service/history.js
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,6 @@ function($rootScope, $state, $location, $window, $timeout, $ionicViewSwitcher, $
// create an element from the viewLocals template
ele = $ionicViewSwitcher.createViewEle(viewLocals);
if (this.isAbstractEle(ele, viewLocals)) {
console.log('VIEW', 'abstractView', DIRECTION_NONE, viewHistory.currentView);
return {
action: 'abstractView',
direction: DIRECTION_NONE,
Expand Down Expand Up @@ -417,8 +416,6 @@ function($rootScope, $state, $location, $window, $timeout, $ionicViewSwitcher, $
}
}

console.log('VIEW', action, direction, viewHistory.currentView);

hist.cursor = viewHistory.currentView.index;

return {
Expand Down
143 changes: 125 additions & 18 deletions js/angular/service/viewSwitcher.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,32 +298,67 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe

},

emit: function(step, enteringData, leavingData) {
var enteringScope = enteringEle.scope(),
leavingScope = leavingEle && leavingEle.scope();
emit: function(step, enteringData, leavingData) {
var enteringScope = getScopeForElement(enteringEle, enteringData);
var leavingScope = getScopeForElement(leavingEle, leavingData);

if (step == 'after') {
if (enteringScope) {
enteringScope.$emit('$ionicView.enter', enteringData);
}
var prefixesAreEqual;

if (leavingScope) {
leavingScope.$emit('$ionicView.leave', leavingData);
if ( !enteringData.viewId || enteringData.abstractView ) {
// it's an abstract view, so treat it accordingly

} else if (enteringScope && leavingData && leavingData.viewId) {
enteringScope.$emit('$ionicNavView.leave', leavingData);
// we only get access to the leaving scope once in the transition,
// so dispatch all events right away if it exists
if ( leavingScope ) {
leavingScope.$emit('$ionicView.beforeLeave', leavingData);
leavingScope.$emit('$ionicView.leave', leavingData);
leavingScope.$emit('$ionicView.afterLeave', leavingData);
leavingScope.$broadcast('$ionicParentView.beforeLeave', leavingData);
leavingScope.$broadcast('$ionicParentView.leave', leavingData);
leavingScope.$broadcast('$ionicParentView.afterLeave', leavingData);
}
}
else {
// it's a regular view, so do the normal process
if (step == 'after') {
if (enteringScope) {
enteringScope.$emit('$ionicView.enter', enteringData);
enteringScope.$broadcast('$ionicParentView.enter', enteringData);
}

if (enteringScope) {
enteringScope.$emit('$ionicView.' + step + 'Enter', enteringData);
}
if (leavingScope) {
leavingScope.$emit('$ionicView.leave', leavingData);
leavingScope.$broadcast('$ionicParentView.leave', leavingData);
}
else if (enteringScope && leavingData && leavingData.viewId && enteringData.stateName !== leavingData.stateName) {
// we only want to dispatch this when we are doing a single-tier
// state change such as changing a tab, so compare the state
// for the same state-prefix but different suffix
prefixesAreEqual = compareStatePrefixes(enteringData.stateName, leavingData.stateName);
if ( prefixesAreEqual ) {
enteringScope.$emit('$ionicNavView.leave', leavingData);
}
}
}

if (leavingScope) {
leavingScope.$emit('$ionicView.' + step + 'Leave', leavingData);
if (enteringScope) {
enteringScope.$emit('$ionicView.' + step + 'Enter', enteringData);
enteringScope.$broadcast('$ionicParentView.' + step + 'Enter', enteringData);
}

} else if (enteringScope && leavingData && leavingData.viewId) {
enteringScope.$emit('$ionicNavView.' + step + 'Leave', leavingData);
if (leavingScope) {
leavingScope.$emit('$ionicView.' + step + 'Leave', leavingData);
leavingScope.$broadcast('$ionicParentView.' + step + 'Leave', leavingData);

} else if (enteringScope && leavingData && leavingData.viewId && enteringData.stateName !== leavingData.stateName) {
// we only want to dispatch this when we are doing a single-tier
// state change such as changing a tab, so compare the state
// for the same state-prefix but different suffix
prefixesAreEqual = compareStatePrefixes(enteringData.stateName, leavingData.stateName);
if ( prefixesAreEqual ) {
enteringScope.$emit('$ionicNavView.' + step + 'Leave', leavingData);
}
}
}
},

Expand Down Expand Up @@ -407,6 +442,15 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
containerEle.innerHTML = viewLocals.$template;
if (containerEle.children.length === 1) {
containerEle.children[0].classList.add('pane');
if ( viewLocals.$$state && viewLocals.$$state.self && viewLocals.$$state.self['abstract'] ) {
angular.element(containerEle.children[0]).attr("abstract", "true");
}
else {
if ( viewLocals.$$state && viewLocals.$$state.self ) {
angular.element(containerEle.children[0]).attr("state", viewLocals.$$state.self.name);
}

}
return jqLite(containerEle.children[0]);
}
}
Expand Down Expand Up @@ -491,4 +535,67 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe
}
}

function compareStatePrefixes(enteringStateName, exitingStateName) {
var enteringStateSuffixIndex = enteringStateName.lastIndexOf('.');
var exitingStateSuffixIndex = exitingStateName.lastIndexOf('.');

// if either of the prefixes are empty, just return false
if ( enteringStateSuffixIndex < 0 || exitingStateSuffixIndex < 0 ) {
return false;
}

var enteringPrefix = enteringStateName.substring(0, enteringStateSuffixIndex);
var exitingPrefix = exitingStateName.substring(0, exitingStateSuffixIndex);

return enteringPrefix === exitingPrefix;
}

function getScopeForElement(element, stateData) {
if ( !element ) {
return null;
}
// check if it's abstract
var attributeValue = angular.element(element).attr("abstract");
var stateValue = angular.element(element).attr("state");

if ( attributeValue !== "true" ) {
// it's not an abstract view, so make sure the element
// matches the state. Due to abstract view weirdness,
// sometimes it doesn't. If it doesn't, don't dispatch events
// so leave the scope undefined
if ( stateValue === stateData.stateName ) {
return angular.element(element).scope();
}
return null;
}
else {
// it is an abstract element, so look for element with the "state" attributeValue
// set to the name of the stateData state
var elements = aggregateNavViewChildren(element);
for ( var i = 0; i < elements.length; i++ ) {
var state = angular.element(elements[i]).attr("state");
if ( state === stateData.stateName ) {
stateData.abstractView = true;
return angular.element(elements[i]).scope();
}
}
// we didn't find a match, so return null
return null;
}
}

function aggregateNavViewChildren(element) {
var aggregate = [];
var navViews = angular.element(element).find("ion-nav-view");
for ( var i = 0; i < navViews.length; i++ ) {
var children = angular.element(navViews[i]).children();
var childrenAggregated = [];
for ( var j = 0; j < children.length; j++ ) {
childrenAggregated = childrenAggregated.concat(children[j]);
}
aggregate = aggregate.concat(childrenAggregated);
}
return aggregate;
}

}]);
Loading