From 9e5caf5983cd93ad9e426378548eaede6e73ff37 Mon Sep 17 00:00:00 2001 From: Adam Bradley Date: Thu, 13 Nov 2014 14:20:11 -0600 Subject: [PATCH] fix(ionView): observe for attr changes --- js/angular/controller/viewController.js | 59 ++++++++++++++++---- test/unit/angular/directive/view.unit.js | 71 ++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 12 deletions(-) diff --git a/js/angular/controller/viewController.js b/js/angular/controller/viewController.js index 6272953b0b..5020af7e74 100644 --- a/js/angular/controller/viewController.js +++ b/js/angular/controller/viewController.js @@ -12,6 +12,7 @@ function($scope, $element, $attrs, $compile, $ionicHistory, $ionicViewSwitcher) var navViewCtrl; var navBarDelegateHandle; var hasViewHeaderBar; + var deregisters = []; var deregIonNavBarInit = $scope.$on('ionNavBar.init', function(ev, delegateHandle){ // this view has its own ion-nav-bar, remember the navBarDelegateHandle for this view @@ -38,13 +39,8 @@ function($scope, $element, $attrs, $compile, $ionicHistory, $ionicViewSwitcher) // add listeners for when this view changes $scope.$on('$ionicView.beforeEnter', self.beforeEnter); - $scope.$on('$ionicView.afterEnter', self.afterEnter); - - // watch to see if the hideNavBar attribute changes - var hideNavAttr = isDefined($attrs.hideNavBar) ? $attrs.hideNavBar : 'false'; - $scope.$watch(hideNavAttr, function(value) { - navViewCtrl.showBar(!value); - }); + $scope.$on('$ionicView.afterEnter', afterEnter); + $scope.$on('$ionicView.beforeLeave', deregisterObservers); }; self.beforeEnter = function(ev, transData) { @@ -68,16 +64,55 @@ function($scope, $element, $attrs, $compile, $ionicHistory, $ionicViewSwitcher) transition: transData.transition, transitionId: transData.transitionId, shouldAnimate: transData.shouldAnimate, - showBack: transData.showBack && !$attrs.hideBackButton, + showBack: transData.showBack && !attrTrue('hideBackButton'), buttons: buttons, navBarDelegate: navBarDelegateHandle || null, - showNavBar: !($attrs.hideNavBar === "true" || $attrs.hideNavBar === ""), + showNavBar: !attrTrue('hideNavBar'), hasHeaderBar: !!hasViewHeaderBar }); + + // make sure any existing observers are cleaned up + deregisterObservers(); } }; + function afterEnter() { + // only listen for title updates after it has entered + // but also deregister the observe before it leaves + var viewTitleAttr = isDefined($attrs.viewTitle) && 'viewTitle' || isDefined($attrs.title) && 'title'; + if (viewTitleAttr) { + deregisters.push($attrs.$observe(viewTitleAttr, function(val) { + navViewCtrl.title(val); + $ionicHistory.currentTitle(val); + })); + } + + if (isDefined($attrs.hideBackButton)) { + deregisters.push($attrs.$observe('hideBackButton', function() { + navViewCtrl.showBackButton(!attrTrue('hideBackButton')); + })); + } + + if (isDefined($attrs.hideNavBar)) { + deregisters.push($attrs.$observe('hideNavBar', function() { + navViewCtrl.showBar(!attrTrue('hideNavBar')); + })); + } + + $ionicViewSwitcher.setActiveView($element.parent()); + } + + + function deregisterObservers() { + // remove all existing $attrs.$observe's + for (var x = 0; x < deregisters.length; x++) { + deregisters[x](); + } + deregisters = []; + } + + function generateButton(html) { if (html) { // every time a view enters we need to recreate its view buttons if they exist @@ -86,9 +121,9 @@ function($scope, $element, $attrs, $compile, $ionicHistory, $ionicViewSwitcher) } - self.afterEnter = function(ev, transitionData) { - $ionicViewSwitcher.setActiveView($element.parent()); - }; + function attrTrue(key) { + return $attrs[key] == 'true' || $attrs[key] === ''; + } self.navElement = function(type, html) { diff --git a/test/unit/angular/directive/view.unit.js b/test/unit/angular/directive/view.unit.js index 1cddf5f476..d2ebc755a4 100644 --- a/test/unit/angular/directive/view.unit.js +++ b/test/unit/angular/directive/view.unit.js @@ -11,7 +11,9 @@ describe('ionView directive', function() { el = angular.element(''); el.data('$ionNavViewController', { beforeEnter: function(d) { beforeEnterData = d; }, + title: jasmine.createSpy('title'), showBar: jasmine.createSpy('showBar'), + showBackButton: jasmine.createSpy('showBackButton') }); content && el.html(content); @@ -116,4 +118,73 @@ describe('ionView directive', function() { expect( beforeEnterData.hasHeaderBar ).toBe(true); })); + it('should only observe title attr after afterEnter and before beforeLeave', inject(function($rootScope) { + var el = setup('view-title="{{ myTitle }}"', {myTitle: 'My Title'}); + $rootScope.$broadcast('$ionicView.beforeEnter', {}); + var spy = el.data('$ionNavViewController').title; + expect(spy).not.toHaveBeenCalled(); + spy.reset(); + + $rootScope.$broadcast('$ionicView.afterEnter', {}); + expect(spy).not.toHaveBeenCalled(); + spy.reset(); + + el.scope().myTitle = 'My New Title'; + $rootScope.$digest(); + expect(spy).toHaveBeenCalledWith('My New Title'); + spy.reset(); + + $rootScope.$broadcast('$ionicView.beforeLeave', {}); + el.scope().myTitle = 'My Other New Title'; + $rootScope.$digest(); + expect(spy).not.toHaveBeenCalled(); + spy.reset(); + })); + + it('should only observe hideNavBar attr after afterEnter and before beforeLeave', inject(function($rootScope) { + var el = setup('hide-nav-bar="{{ hide }}"', {hide: false}); + $rootScope.$broadcast('$ionicView.beforeEnter', {}); + var spy = el.data('$ionNavViewController').showBar; + expect(spy).not.toHaveBeenCalled(); + spy.reset(); + + $rootScope.$broadcast('$ionicView.afterEnter', {}); + expect(spy).not.toHaveBeenCalled(); + spy.reset(); + + el.scope().hide = true; + $rootScope.$digest(); + expect(spy).toHaveBeenCalledWith(false); + spy.reset(); + + $rootScope.$broadcast('$ionicView.beforeLeave', {}); + el.scope().hide = false; + $rootScope.$digest(); + expect(spy).not.toHaveBeenCalled(); + spy.reset(); + })); + + it('should only observe hideBackButton attr after afterEnter and before beforeLeave', inject(function($rootScope) { + var el = setup('hide-back-button="{{ hide }}"', {hide: false}); + $rootScope.$broadcast('$ionicView.beforeEnter', {}); + var spy = el.data('$ionNavViewController').showBackButton; + expect(spy).not.toHaveBeenCalled(); + spy.reset(); + + $rootScope.$broadcast('$ionicView.afterEnter', {}); + expect(spy).not.toHaveBeenCalled(); + spy.reset(); + + el.scope().hide = true; + $rootScope.$digest(); + expect(spy).toHaveBeenCalledWith(false); + spy.reset(); + + $rootScope.$broadcast('$ionicView.beforeLeave', {}); + el.scope().hide = false; + $rootScope.$digest(); + expect(spy).not.toHaveBeenCalled(); + spy.reset(); + })); + });