diff --git a/src/components/tabs/js/tabsController.js b/src/components/tabs/js/tabsController.js index 48362251fda..7bdb684c456 100644 --- a/src/components/tabs/js/tabsController.js +++ b/src/components/tabs/js/tabsController.js @@ -159,6 +159,7 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp * @param stretchTabs */ function handleStretchTabs (stretchTabs) { + var elements = getElements(); angular.element(elements.wrapper).toggleClass('md-stretch-tabs', shouldStretchTabs()); updateInkBarStyles(); } @@ -169,6 +170,8 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp function handleMaxTabWidth (newWidth, oldWidth) { if (newWidth !== oldWidth) { + var elements = getElements(); + angular.forEach(elements.tabs, function(tab) { tab.style.maxWidth = newWidth + 'px'; }); @@ -200,7 +203,9 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp * @param left */ function handleOffsetChange (left) { + var elements = getElements(); var newValue = ctrl.shouldCenterTabs ? '' : '-' + left + 'px'; + angular.element(elements.paging).css($mdConstant.CSS.TRANSFORM, 'translate3d(' + newValue + ', 0, 0)'); $scope.$broadcast('$mdTabsPaginationChanged'); } @@ -212,7 +217,7 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp */ function handleFocusIndexChange (newIndex, oldIndex) { if (newIndex === oldIndex) return; - if (!elements.tabs[ newIndex ]) return; + if (!getElements().tabs[ newIndex ]) return; adjustOffset(); redirectFocus(); } @@ -320,6 +325,7 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp * Slides the tabs over approximately one page forward. */ function nextPage () { + var elements = getElements(); var viewportWidth = elements.canvas.clientWidth, totalWidth = viewportWidth + ctrl.offsetLeft, i, tab; @@ -334,7 +340,8 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp * Slides the tabs over approximately one page backward. */ function previousPage () { - var i, tab; + var i, tab, elements = getElements(); + for (i = 0; i < elements.tabs.length; i++) { tab = elements.tabs[ i ]; if (tab.offsetLeft + tab.offsetWidth >= ctrl.offsetLeft) break; @@ -355,7 +362,7 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp } function handleInkBar (hide) { - angular.element(elements.inkBar).toggleClass('ng-hide', hide); + angular.element(getElements().inkBar).toggleClass('ng-hide', hide); } /** @@ -461,6 +468,7 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp * @returns {*|boolean} */ function canPageForward () { + var elements = getElements(); var lastTab = elements.tabs[ elements.tabs.length - 1 ]; return lastTab && lastTab.offsetLeft + lastTab.offsetWidth > elements.canvas.clientWidth + ctrl.offsetLeft; @@ -497,7 +505,7 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp function shouldPaginate () { if (ctrl.noPagination || !loaded) return false; var canvasWidth = $element.prop('clientWidth'); - angular.forEach(elements.dummies, function (tab) { canvasWidth -= tab.offsetWidth; }); + angular.forEach(getElements().dummies, function (tab) { canvasWidth -= tab.offsetWidth; }); return canvasWidth < 0; } @@ -553,6 +561,7 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp * Sets or clears fixed width for md-pagination-wrapper depending on if tabs should stretch. */ function updatePagingWidth() { + var elements = getElements(); if (shouldStretchTabs()) { angular.element(elements.paging).css('width', ''); } else { @@ -566,7 +575,7 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp function calcPagingWidth () { var width = 1; - angular.forEach(elements.dummies, function (element) { + angular.forEach(getElements().dummies, function (element) { //-- Uses the larger value between `getBoundingClientRect().width` and `offsetWidth`. This // prevents `offsetWidth` value from being rounded down and causing wrapping issues, but // also handles scenarios where `getBoundingClientRect()` is inaccurate (ie. tabs inside @@ -616,13 +625,15 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp * issues when attempting to focus an item that is out of view. */ function redirectFocus () { - elements.dummies[ ctrl.focusIndex ].focus(); + getElements().dummies[ ctrl.focusIndex ].focus(); } /** * Forces the pagination to move the focused tab into view. */ function adjustOffset (index) { + var elements = getElements(); + if (index == null) index = ctrl.focusIndex; if (!elements.tabs[ index ]) return; if (ctrl.shouldCenterTabs) return; @@ -669,6 +680,8 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp if (!ctrl.dynamicHeight) return $element.css('height', ''); if (!ctrl.tabs.length) return queue.push(updateHeightFromContent); + var elements = getElements(); + var tabContent = elements.contents[ ctrl.selectedIndex ], contentHeight = tabContent ? tabContent.offsetHeight : 0, tabsHeight = elements.wrapper.offsetHeight, @@ -727,6 +740,8 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp * @returns {*} */ function updateInkBarStyles () { + var elements = getElements(); + if (!elements.tabs[ ctrl.selectedIndex ]) { angular.element(elements.inkBar).css({ left: 'auto', right: 'auto' }); return; @@ -755,6 +770,7 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp * Adds left/right classes so that the ink bar will animate properly. */ function updateInkBarClassName () { + var elements = getElements(); var newIndex = ctrl.selectedIndex, oldIndex = ctrl.lastSelectedIndex, ink = angular.element(elements.inkBar); @@ -770,6 +786,8 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp * @returns {*} */ function fixOffset (value) { + var elements = getElements(); + if (!elements.tabs.length || !ctrl.shouldPaginate) return 0; var lastTab = elements.tabs[ elements.tabs.length - 1 ], totalWidth = lastTab.offsetLeft + lastTab.offsetWidth; @@ -784,6 +802,7 @@ function MdTabsController ($scope, $element, $window, $mdConstant, $mdTabInkRipp * @param element */ function attachRipple (scope, element) { + var elements = getElements(); var options = { colorElement: angular.element(elements.inkBar) }; $mdTabInkRipple.attach(scope, element, options); } diff --git a/src/components/tabs/js/tabsDirective.js b/src/components/tabs/js/tabsDirective.js index ece61e366f7..0d67992e4cc 100644 --- a/src/components/tabs/js/tabsDirective.js +++ b/src/components/tabs/js/tabsDirective.js @@ -154,7 +154,7 @@ function MdTabs () { 'md-scope="::tab.parent"> ' + ' ' + ' ' + - '
' + + ' ' + ' ' + - '
' + + ' ' + ' ' + ' ' + ' ' + diff --git a/src/components/tabs/js/tabsDummyWrapperDirective.js b/src/components/tabs/js/tabsDummyWrapperDirective.js new file mode 100644 index 00000000000..358d4257df7 --- /dev/null +++ b/src/components/tabs/js/tabsDummyWrapperDirective.js @@ -0,0 +1,36 @@ +angular + .module('material.components.tabs') + .directive('mdTabsDummyWrapper', MdTabsDummyWrapper); + +/** + * @private + * + * @param $mdUtil + * @returns {{require: string, link: link}} + * @constructor + * + * @ngInject + */ +function MdTabsDummyWrapper ($mdUtil) { + return { + require: '^?mdTabs', + link: function link (scope, element, attr, ctrl) { + if (!ctrl) return; + + var observer = new MutationObserver(function(mutations) { + ctrl.updatePagination(); + ctrl.updateInkBarStyles(); + }); + var config = { childList: true, subtree: true }; + + observer.observe(element[0], config); + + // Disconnect the observer + scope.$on('$destroy', function() { + if (observer) { + observer.disconnect(); + } + }); + } + }; +} diff --git a/src/components/tabs/js/tabsTemplateDirective.js b/src/components/tabs/js/tabsTemplateDirective.js index 6d0f54923f7..d04d5b7c1c5 100644 --- a/src/components/tabs/js/tabsTemplateDirective.js +++ b/src/components/tabs/js/tabsTemplateDirective.js @@ -15,13 +15,12 @@ function MdTabsTemplate ($compile, $mdUtil) { }; function link (scope, element, attr, ctrl) { if (!ctrl) return; + var compileScope = ctrl.enableDisconnect ? scope.compileScope.$new() : scope.compileScope; + element.html(scope.template); $compile(element.contents())(compileScope); - element.on('DOMSubtreeModified', function () { - ctrl.updatePagination(); - ctrl.updateInkBarStyles(); - }); + return $mdUtil.nextTick(handleScope); function handleScope () { diff --git a/src/components/tabs/tabs.spec.js b/src/components/tabs/tabs.spec.js index 793e479497d..a7131c5964e 100644 --- a/src/components/tabs/tabs.spec.js +++ b/src/components/tabs/tabs.spec.js @@ -232,6 +232,63 @@ describe('', function () { expect(tabs1[ 0 ].querySelector('md-tab-content').textContent.trim()).toBe('content that!'); }); + it('updates pagination and ink styles when string labels change', function(done) { + inject(function($rootScope) { + // Setup our initial label + $rootScope.$apply('label = "Some Label"'); + + // Init our variables + var template = ''; + var tabs = setup(template); + var ctrl = tabs.controller('mdTabs'); + + // Setup spies + spyOn(ctrl, 'updatePagination'); + spyOn(ctrl, 'updateInkBarStyles'); + + // Change the label + $rootScope.$apply('label="Another Label"'); + + // Use window.setTimeout to add our expectations to the end of the call stack, after the + // MutationObservers have already fired + window.setTimeout(function() { + // Fire expectations + expect(ctrl.updatePagination.calls.count()).toBe(1); + expect(ctrl.updateInkBarStyles.calls.count()).toBe(1); + + done(); + }); + }) + }); + + it('updates pagination and ink styles when HTML labels change', function(done) { + inject(function($rootScope) { + // Setup our initial label + $rootScope.$apply('label = "Some Label"'); + + // Init our variables + var template = '{{label}}'; + var tabs = setup(template); + var ctrl = tabs.controller('mdTabs'); + + // Setup spies + spyOn(ctrl, 'updatePagination'); + spyOn(ctrl, 'updateInkBarStyles'); + + // Change the label + $rootScope.$apply('label="Another Label"'); + + // Use window.setTimeout to add our expectations to the end of the call stack, after the + // MutationObservers have already fired + window.setTimeout(function() { + // Fire expectations + expect(ctrl.updatePagination.calls.count()).toBe(1); + expect(ctrl.updateInkBarStyles.calls.count()).toBe(1); + + done(); + }); + }) + }); }); describe('aria', function () {