Skip to content

Commit

Permalink
fix(ionTabs): do not pre-transclude; stops error on compile
Browse files Browse the repository at this point in the history
Closes #730
  • Loading branch information
ajoslin committed Mar 8, 2014
1 parent 9cfd95f commit ecfdbaa
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 43 deletions.
53 changes: 27 additions & 26 deletions js/ext/angular/src/directive/ionicTabBar.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ function($scope, $ionicViewService, $rootScope, $element) {
}])

// Generic controller directive
.directive('ionTab', ['$rootScope', '$animate', '$ionicBind', '$compile', '$ionicViewService', function($rootScope, $animate, $ionicBind, $compile, $ionicViewService) {
.directive('ionTab', ['$rootScope', '$animate', '$ionicBind', '$compile', '$ionicViewService',
function($rootScope, $animate, $ionicBind, $compile, $ionicViewService) {

//Returns ' key="value"' if value exists
function attrStr(k,v) {
Expand All @@ -154,10 +155,17 @@ function($scope, $ionicViewService, $rootScope, $element) {
restrict: 'E',
require: ['^ionTabs', 'ionTab'],
replace: true,
transclude: 'element',
controller: '$ionicTab',
scope: true,
compile: function(element, attr, transclude) {
compile: function(element, attr) {
//Do we have a navView?
var navView = element[0].querySelector('ion-nav-view') ||
element[0].querySelector('data-ion-nav-view');
var navViewName = navView && navView.getAttribute('name');

//Remove the contents of the element so we can compile them later, if tab is selected
var tabContent = angular.element('<div class="pane">')
.append( element.contents().remove() );
return function link($scope, $element, $attr, ctrls) {
var childScope, childElement, tabNavElement;
tabsCtrl = ctrls[0],
Expand All @@ -174,6 +182,19 @@ function($scope, $ionicViewService, $rootScope, $element) {
href: '@',
});

tabsCtrl.add($scope);
$scope.$on('$destroy', function() {
tabsCtrl.remove($scope);
tabNavElement.isolateScope().$destroy();
tabNavElement.remove();
});

if (navViewName) {
$scope.navViewName = navViewName;
$scope.$on('$stateChangeSuccess', selectTabIfMatchesState);
selectTabIfMatchesState();
}

tabNavElement = angular.element(
'<ion-tab-nav' +
attrStr('title', attr.title) +
Expand All @@ -188,13 +209,6 @@ function($scope, $ionicViewService, $rootScope, $element) {
tabNavElement.data('$ionTabController', tabCtrl);
tabsCtrl.$tabsElement.append($compile(tabNavElement)($scope));

tabsCtrl.add($scope);
$scope.$on('$destroy', function() {
tabsCtrl.remove($scope);
tabNavElement.isolateScope().$destroy();
tabNavElement.remove();
});

$scope.$watch('$tabSelected', function(value) {
if (!value) {
$scope.$broadcast('tab.hidden', $scope);
Expand All @@ -205,26 +219,13 @@ function($scope, $ionicViewService, $rootScope, $element) {
childElement = null;
if (value) {
childScope = $scope.$new();
transclude(childScope, function(clone) {
//remove title attr to stop hover annoyance!
clone[0].removeAttribute('title');
$animate.enter(clone, tabsCtrl.$element);
clone.addClass('pane');
childElement = clone;
});
childElement = tabContent.clone();
$animate.enter(childElement, tabsCtrl.$element);
$compile(childElement)(childScope);
$scope.$broadcast('tab.shown', $scope);
}
});

transclude($scope, function(clone) {
var navView = clone[0].querySelector('ion-nav-view');
if (navView) {
$scope.navViewName = navView.getAttribute('name');
selectTabIfMatchesState();
$scope.$on('$stateChangeSuccess', selectTabIfMatchesState);
}
});

function selectTabIfMatchesState() {
// this tab's ui-view is the current one, go to it!
if ($ionicViewService.isCurrentStateNavView($scope.navViewName)) {
Expand Down
49 changes: 32 additions & 17 deletions js/ext/angular/test/directive/ionicTabBar.unit.js
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ describe('tabs', function() {
beforeEach(module('ionic'));
var tabsCtrl, tabsEl, scope;
function setup(attrs, content) {
inject(function($compile, $rootScope, $injector) {
inject(function($compile, $rootScope) {
tabsEl = angular.element('<ion-tabs><ion-tab '+(attrs||'')+'>'+(content||'')+'</ion-tab></ion-tabs>');

$compile(tabsEl)($rootScope.$new());
Expand All @@ -256,6 +256,19 @@ describe('tabs', function() {
spyOn(tabsCtrl, 'remove');
});
}

it('should not initially compile content until selected', inject(function($compile, $rootScope) {
var el = $compile('<ion-tabs>' +
'<ion-tab></ion-tab>' +
'<ion-tab><div ng-init="$root.elephant = \'banana\'"></div></ion-tab>' +
'</ion-tabs>')($rootScope);
$rootScope.$apply();
expect($rootScope.elephant).toBeUndefined();
el.controller('ionTabs').select(1);
$rootScope.$apply();
expect($rootScope.elephant).toBe('banana');
}));


it('should add itself to tabsCtrl and remove on $destroy', function() {
var el = setup();
Expand Down Expand Up @@ -292,26 +305,28 @@ describe('tabs', function() {
expect(tabsCtrl.tabs[0].navViewName).toBeUndefined();
});

it('should set navViewName and select when necessary if a child nav-view', inject(function($ionicViewService, $rootScope) {
var isCurrent = false;
spyOn($ionicViewService, 'isCurrentStateNavView').andCallFake(function(name) {
return isCurrent;
});
angular.forEach(['ion-nav-view', 'data-ion-nav-view'], function(directive) {
it('should set navViewName and select when necessary if a child '+directive, inject(function($ionicViewService, $rootScope) {
var isCurrent = false;
spyOn($ionicViewService, 'isCurrentStateNavView').andCallFake(function(name) {
return isCurrent;
});

setup('', '<ion-nav-view name="banana"></ion-nav-view>');
spyOn(tabsCtrl, 'select');
var tab = tabsCtrl.tabs[0];
setup('', '<' + directive + ' name="banana"></' + directive + '>');
spyOn(tabsCtrl, 'select');
var tab = tabsCtrl.tabs[0];

expect(tab.navViewName).toBe('banana');
expect($ionicViewService.isCurrentStateNavView).toHaveBeenCalledWith('banana');
expect(tab.navViewName).toBe('banana');
expect($ionicViewService.isCurrentStateNavView).toHaveBeenCalledWith('banana');

$ionicViewService.isCurrentStateNavView.reset();
isCurrent = true;
$rootScope.$broadcast('$stateChangeSuccess');
$ionicViewService.isCurrentStateNavView.reset();
isCurrent = true;
$rootScope.$broadcast('$stateChangeSuccess');

expect($ionicViewService.isCurrentStateNavView).toHaveBeenCalledWith('banana');
expect(tabsCtrl.select).toHaveBeenCalledWith(tab);
}));
expect($ionicViewService.isCurrentStateNavView).toHaveBeenCalledWith('banana');
expect(tabsCtrl.select).toHaveBeenCalledWith(tab);
}));
});

it('should transclude on $tabSelected=true', function() {
setup('', '<div class="inside-content"></div>');
Expand Down

0 comments on commit ecfdbaa

Please sign in to comment.