forked from angular-ui/bootstrap
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor(datepicker): optimize datepicker
- Add new class manipulation helper directive that uses 1 watcher as opposed to watchers for each element with ng-class - Optimize disabled state - Optimize class binding Closes angular-ui#2613 Closes angular-ui#3451 Closes angular-ui#3770 Closes angular-ui#5065 BREAKING CHANGE: For those using custom templates, the changes result in necessary changes being made to the templates to match the new class changing syntax
- Loading branch information
Showing
6 changed files
with
255 additions
and
23 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
// Avoiding use of ng-class as it creates a lot of watchers when a class is to be applied to | ||
// at most one element. | ||
angular.module('ui.bootstrap.isClass', []) | ||
.directive('uibIsClass', [ | ||
'$animate', | ||
function ($animate) { | ||
// 11111111 22222222 | ||
var ON_REGEXP = /^\s*([\s\S]+?)\s+on\s+([\s\S]+?)\s*$/; | ||
// 11111111 22222222 | ||
var IS_REGEXP = /^\s*([\s\S]+?)\s+for\s+([\s\S]+?)\s*$/; | ||
|
||
var dataPerTracked = {}; | ||
|
||
return { | ||
restrict: 'A', | ||
compile: function (tElement, tAttrs) { | ||
var linkedScopes = []; | ||
var instances = []; | ||
var expToData = {}; | ||
var lastActivated = null; | ||
var onExpMatches = tAttrs.uibIsClass.match(ON_REGEXP); | ||
var onExp = onExpMatches[2]; | ||
var expsStr = onExpMatches[1]; | ||
var exps = expsStr.split(','); | ||
|
||
return linkFn; | ||
|
||
function linkFn(scope, element, attrs) { | ||
linkedScopes.push(scope); | ||
instances.push({ | ||
scope: scope, | ||
element: element | ||
}); | ||
|
||
exps.forEach(function (exp, k) { | ||
addForExp(exp, scope); | ||
}); | ||
|
||
scope.$on('$destroy', removeScope); | ||
} | ||
|
||
function addForExp(exp, scope) { | ||
var matches = exp.match(IS_REGEXP); | ||
var clazz = scope.$eval(matches[1]); | ||
var compareWithExp = matches[2]; | ||
var data = expToData[exp]; | ||
if (!data) { | ||
var watchFn = function (compareWithVal) { | ||
var newActivated = null; | ||
instances.some(function (instance) { | ||
var thisVal = instance.scope.$eval(onExp); | ||
if (thisVal === compareWithVal) { | ||
newActivated = instance; | ||
return true; | ||
} | ||
}); | ||
if (data.lastActivated !== newActivated) { | ||
if (data.lastActivated) { | ||
$animate.removeClass(data.lastActivated.element, clazz); | ||
} | ||
if (newActivated) { | ||
$animate.addClass(newActivated.element, clazz); | ||
} | ||
data.lastActivated = newActivated; | ||
} | ||
}; | ||
expToData[exp] = data = { | ||
lastActivated: null, | ||
scope: scope, | ||
watchFn: watchFn, | ||
compareWithExp: compareWithExp, | ||
watcher: scope.$watch(compareWithExp, watchFn) | ||
}; | ||
} | ||
data.watchFn(scope.$eval(compareWithExp)); | ||
} | ||
|
||
function removeScope(e) { | ||
var removedScope = e.targetScope; | ||
var index = linkedScopes.indexOf(removedScope); | ||
linkedScopes.splice(index, 1); | ||
instances.splice(index, 1); | ||
if (linkedScopes.length) { | ||
var newWatchScope = linkedScopes[0]; | ||
angular.forEach(expToData, function (data) { | ||
if (data.scope === removedScope) { | ||
data.watcher = newWatchScope.$watch(data.compareWithExp, data.watchFn); | ||
data.scope = newWatchScope; | ||
} | ||
}); | ||
} | ||
else { | ||
expToData = {}; | ||
} | ||
} | ||
} | ||
}; | ||
}]); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
describe('uibIsClass', function() { | ||
var $rootScope; | ||
|
||
beforeEach(module('ui.bootstrap.isClass')); | ||
beforeEach(inject(function($compile, _$rootScope_) { | ||
$rootScope = _$rootScope_; | ||
$rootScope.activeClass = 'active'; | ||
$rootScope.items = [1, 2, 3]; | ||
element = $compile('<div><div ng-repeat="item in items" ' + | ||
'uib-is-class="activeClass for activeItem on item">{{ item }}</div></div>')($rootScope); | ||
$rootScope.$digest(); | ||
})); | ||
|
||
it('initializes classes correctly', function() { | ||
expect(element.find('.active').length).toEqual(0); | ||
}); | ||
|
||
it('sets classes correctly', function() { | ||
$rootScope.activeItem = 2; | ||
$rootScope.$digest(); | ||
expect(element.find('.active').text()).toEqual('2'); | ||
|
||
$rootScope.items.splice(1, 1); | ||
$rootScope.$digest(); | ||
expect(element.find('.active').length).toEqual(0); | ||
}); | ||
|
||
it('handles removal of items correctly', function() { | ||
$rootScope.activeItem = 2; | ||
$rootScope.$digest(); | ||
expect(element.find('.active').text()).toEqual('2'); | ||
|
||
$rootScope.items.splice(1, 1); | ||
$rootScope.$digest(); | ||
expect(element.find('.active').length).toEqual(0); | ||
|
||
$rootScope.activeItem = 1; | ||
$rootScope.$digest(); | ||
expect(element.find('.active').text()).toEqual('1'); | ||
}); | ||
|
||
it('handles moving of items', function() { | ||
$rootScope.activeItem = 2; | ||
$rootScope.items = [2, 1, 3]; | ||
$rootScope.$digest(); | ||
expect(element.find('.active').text()).toEqual('2'); | ||
expect(element.find('.active').length).toEqual(1); | ||
expect(element.find('.active').index()).toEqual(0); | ||
|
||
$rootScope.items = [4, 3, 2]; | ||
$rootScope.$digest(); | ||
expect(element.find('.active').text()).toEqual('2'); | ||
expect(element.find('.active').length).toEqual(1); | ||
expect(element.find('.active').index()).toEqual(2); | ||
}); | ||
|
||
it('handles emptying and re-adding the items', function() { | ||
$rootScope.activeItem = 2; | ||
$rootScope.items = []; | ||
$rootScope.$digest(); | ||
expect(element.find('.active').length).toEqual(0); | ||
|
||
$rootScope.items = [4, 3, 2]; | ||
$rootScope.$digest(); | ||
expect(element.find('.active').text()).toEqual('2'); | ||
expect(element.find('.active').index()).toEqual(2); | ||
}); | ||
|
||
it('handles undefined items', function() { | ||
$rootScope.activeItem = undefined; | ||
$rootScope.items = []; | ||
$rootScope.$digest(); | ||
expect(element.find('.active').length).toEqual(0); | ||
|
||
$rootScope.items = [4, 3, undefined]; | ||
$rootScope.$digest(); | ||
expect(element.find('.active').length).toEqual(1); | ||
expect(element.find('.active').text()).toEqual(''); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters