diff --git a/js/angular/controller/navViewController.js b/js/angular/controller/navViewController.js index 96fe31e352..1b8448c54d 100644 --- a/js/angular/controller/navViewController.js +++ b/js/angular/controller/navViewController.js @@ -6,9 +6,10 @@ IonicModule '$compile', '$controller', '$ionicNavBarDelegate', + '$ionicNavViewDelegate', '$ionicHistory', '$ionicViewSwitcher', -function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate, $ionicHistory, $ionicViewSwitcher) { +function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate, $ionicNavViewDelegate, $ionicHistory, $ionicViewSwitcher) { var DATA_ELE_IDENTIFIER = '$eleId'; var VIEW_STATUS_ACTIVE = 'active'; @@ -36,6 +37,9 @@ function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate, var viewData = { name: navViewName, state: null }; $element.data('$uiView', viewData); + var deregisterInstance = $ionicNavViewDelegate._registerInstance(self, $attrs.delegateHandle); + $scope.$on('$destroy', deregisterInstance); + return viewData; }; @@ -154,6 +158,18 @@ function($scope, $element, $attrs, $compile, $controller, $ionicNavBarDelegate, }; + self.clearCache = function() { + var viewElements = $element.children(); + + for (var x = 0, l = viewElements.length; x < l; x++) { + if (navViewAttr(viewElements.eq(x)) == VIEW_STATUS_CACHED) { + $ionicViewSwitcher.destroyViewEle(viewElements.eq(x)); + } + } + + }; + + self.getViewElements = function() { return $element.children(); }; diff --git a/js/angular/service/history.js b/js/angular/service/history.js index 08ed93bada..d00f594384 100644 --- a/js/angular/service/history.js +++ b/js/angular/service/history.js @@ -22,7 +22,8 @@ IonicModule '$location', '$window', '$ionicViewSwitcher', -function($rootScope, $state, $location, $window, $ionicViewSwitcher) { + '$ionicNavViewDelegate', +function($rootScope, $state, $location, $window, $ionicViewSwitcher, $ionicNavViewDelegate) { // history actions while navigating views var ACTION_INITIAL_VIEW = 'initialView'; @@ -586,6 +587,18 @@ function($rootScope, $state, $location, $window, $ionicViewSwitcher) { } }, + /** + * @ngdoc method + * @name $ionicHistory#clearCache + * @description Removes all cached views within every {@link ionic.directive:ionNavView}. + * This both removes the view element from the DOM, and destroy it's scope. + */ + clearCache: function() { + $ionicNavViewDelegate._instances.forEach(function(instance) { + instance.clearCache(); + }); + }, + /** * @ngdoc method * @name $ionicHistory#nextViewOptions diff --git a/js/angular/service/navViewDelegate.js b/js/angular/service/navViewDelegate.js new file mode 100644 index 0000000000..3cd78d9934 --- /dev/null +++ b/js/angular/service/navViewDelegate.js @@ -0,0 +1,6 @@ + +IonicModule +.service('$ionicNavViewDelegate', delegateService([ + 'clearCache' +])); + diff --git a/js/angular/service/viewSwitcher.js b/js/angular/service/viewSwitcher.js index 43f37bbc82..3e9a6a5ebc 100644 --- a/js/angular/service/viewSwitcher.js +++ b/js/angular/service/viewSwitcher.js @@ -90,7 +90,7 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe function destroyViewEle(ele) { // we found an element that should be removed // destroy its scope, then remove the element - if (ele) { + if (ele && ele.length) { var viewScope = ele.scope(); viewScope && viewScope.$destroy(); ele.remove(); @@ -253,9 +253,9 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe // 7) start the transition viewTransition.run(1); - for (var x = 0; x < $ionicNavBarDelegate._instances.length; x++) { - $ionicNavBarDelegate._instances[x].triggerTransitionStart(transitionId); - } + $ionicNavBarDelegate._instances.forEach(function(instance) { + instance.triggerTransitionStart(transitionId); + }); if (!viewTransition.shouldAnimate) { // no animated transition @@ -285,9 +285,9 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe switcher.cleanup(enteringData); } - for (var x = 0; x < $ionicNavBarDelegate._instances.length; x++) { - $ionicNavBarDelegate._instances[x].triggerTransitionEnd(); - } + $ionicNavBarDelegate._instances.forEach(function(instance) { + instance.triggerTransitionEnd(); + }); // remove any references that could cause memory issues nextTransition = nextDirection = enteringView = leavingView = enteringEle = leavingEle = null; @@ -338,7 +338,7 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe oldestAccess = viewElement.data(DATA_VIEW_ACCESSED); removableEle = viewElements.eq(x); - } else if (viewElement.data(DATA_DESTROY_ELE) && cachedAttr(viewElement) != VIEW_STATUS_ACTIVE) { + } else if (viewElement.data(DATA_DESTROY_ELE) && navViewAttr(viewElement) != VIEW_STATUS_ACTIVE) { destroyViewEle(viewElement); } } @@ -415,6 +415,7 @@ function($timeout, $document, $q, $ionicClickBlock, $ionicConfig, $ionicNavBarDe getTransitionData: getTransitionData, historyCursorAttr: historyCursorAttr, navViewAttr: navViewAttr, + destroyViewEle: destroyViewEle }; diff --git a/test/unit/angular/directive/navView.unit.js b/test/unit/angular/directive/navView.unit.js index 813eeff598..8ea77cadcb 100644 --- a/test/unit/angular/directive/navView.unit.js +++ b/test/unit/angular/directive/navView.unit.js @@ -2,7 +2,7 @@ describe('Ionic nav-view', function() { beforeEach(module('ionic')); - var compile, viewService, $rootScope, elem; + var compile, $rootScope, elem; var aState = { template: 'aState template' @@ -131,8 +131,7 @@ describe('Ionic nav-view', function() { .state('ionViewCacheFalseProperty', ionViewCacheFalsePropertyState); })); - beforeEach(inject(function(_$compile_, $ionicHistory, $ionicConfig, $rootScope) { - viewService = $ionicHistory; + beforeEach(inject(function(_$compile_, $ionicConfig, $rootScope) { $compile = _$compile_; scope = $rootScope.$new(); elem = angular.element('
'); @@ -816,6 +815,64 @@ describe('Ionic nav-view', function() { expect(leave.transitionId).toEqual(4); })); + it('should clear ion-nav-view cache', inject(function ($state, $q, $timeout, $compile, $ionicHistory) { + elem.append($compile('
')(scope)); + + $state.go(page1State); + $q.flush(); + $timeout.flush(); + $timeout.flush(); + + $state.go(page2State); + $q.flush(); + $timeout.flush(); + $timeout.flush(); + + $state.go(page3State); + $q.flush(); + $timeout.flush(); + $timeout.flush(); + + var divs = elem.find('ion-nav-view').find('div'); + expect(divs.length).toBe(3); + + expect(divs.eq(0).attr('nav-view')).toBe('cached'); + expect(divs.eq(0).text()).toBe('page1'); + + expect(divs.eq(1).attr('nav-view')).toBe('cached'); + expect(divs.eq(1).text()).toBe('page2'); + + expect(divs.eq(2).attr('nav-view')).toBe('active'); + expect(divs.eq(2).text()).toBe('page3'); + + $ionicHistory.clearCache(); + + var divs = elem.find('ion-nav-view').find('div'); + expect(divs.length).toBe(1); + + expect(divs.eq(0).attr('nav-view')).toBe('active'); + expect(divs.eq(0).text()).toBe('page3'); + + $ionicHistory.clearCache(); + + var divs = elem.find('ion-nav-view').find('div'); + expect(divs.length).toBe(1); + + expect(divs.eq(0).attr('nav-view')).toBe('active'); + expect(divs.eq(0).text()).toBe('page3'); + + $state.go(page2State); + $q.flush(); + $timeout.flush(); + $timeout.flush(); + + var divs = elem.find('ion-nav-view').find('div'); + expect(divs.length).toBe(1); + + expect(divs.eq(0).attr('nav-view')).toBe('active'); + expect(divs.eq(0).text()).toBe('page2'); + })); + }); angular.module('ngMock').config(function ($provide) {