Skip to content
This repository was archived by the owner on Oct 2, 2019. It is now read-only.

Avoid double-compilation of ui-select-choices directive. #170

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions src/select.css
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@
}

/* Fix dropdown width for Selectize theme */
.selectize-control > .selectize-dropdown {
.selectize-control > .selectize-dropdown,
.selectize-control > ui-select-choices > .selectize-dropdown {
width: 100%;
}

Expand Down Expand Up @@ -88,17 +89,24 @@
right: 15px;
}

.ui-select-bootstrap > .ui-select-choices {
.ui-select-bootstrap > .ui-select-choices,
.ui-select-bootstrap > ui-select-choices > .ui-select-choices {
width: 100%;
}

/* See Scrollable Menu with Bootstrap 3 http://stackoverflow.com/questions/19227496 */
.ui-select-bootstrap > .ui-select-choices {
.ui-select-bootstrap > .ui-select-choices,
.ui-select-bootstrap ui-select-choices > .ui-select-choices {
height: auto;
max-height: 200px;
overflow-x: hidden;
}

/* Open Bootstrap dropdown-menu that is nested in ui-select-choices element */
.open > ui-select-choices > .dropdown-menu {
display: block;
}

.ui-select-bootstrap .ui-select-choices-row>a {
display: block;
padding: 3px 20px;
Expand Down
78 changes: 34 additions & 44 deletions src/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
ctrl.selected = undefined;
ctrl.open = false;
ctrl.focus = false;
ctrl.focusser = undefined; //Reference to input element used to handle focus events
ctrl.focusser = undefined; //Reference to input element used to handle focus events
ctrl.disabled = undefined; // Initialized inside uiSelect directive link function
ctrl.resetSearchInput = undefined; // Initialized inside uiSelect directive link function
ctrl.refreshDelay = undefined; // Initialized inside uiSelectChoices directive link function
Expand Down Expand Up @@ -249,7 +249,7 @@
_resetSearchInput();
ctrl.open = false;
$timeout(function(){
ctrl.focusser[0].focus();
ctrl.focusser[0].focus();
},0,false);
}
};
Expand Down Expand Up @@ -430,7 +430,7 @@
if (e.which === KEY.TAB || KEY.isControl(e) || KEY.isFunctionKey(e) || e.which === KEY.ESC || e.which == KEY.ENTER || e.which === KEY.BACKSPACE) {
return;
}

$select.activate(focusser.val()); //User pressed some regualar key, so we pass it to the search input
focusser.val('');
scope.$digest();
Expand Down Expand Up @@ -547,10 +547,10 @@
}
element.querySelectorAll('.ui-select-match').replaceWith(transcludedMatch);

var transcludedChoices = transcluded.querySelectorAll('.ui-select-choices');
var transcludedChoices = transcluded.querySelectorAll('ui-select-choices, [ui-select-choices]');
transcludedChoices.removeAttr('ui-select-choices'); //To avoid loop in case directive as attr
if (transcludedChoices.length !== 1) {
throw uiSelectMinErr('transcluded', "Expected 1 .ui-select-choices but got '{0}'.", transcludedChoices.length);
throw uiSelectMinErr('transcluded', "Expected 1 ui-select-choices but got '{0}'.", transcludedChoices.length);
}
element.querySelectorAll('.ui-select-choices').replaceWith(transcludedChoices);
});
Expand All @@ -559,51 +559,49 @@
}])

.directive('uiSelectChoices',
['uiSelectConfig', 'RepeatParser', 'uiSelectMinErr', '$compile',
function(uiSelectConfig, RepeatParser, uiSelectMinErr, $compile) {
['uiSelectConfig', 'RepeatParser', 'uiSelectMinErr', '$templateCache',
function(uiSelectConfig, RepeatParser, uiSelectMinErr, $templateCache) {

return {
restrict: 'EA',
require: '^uiSelect',
replace: true,
transclude: true,
templateUrl: function(tElement) {
compile: function(tElement, tAttrs) {
// Gets theme attribute from parent (ui-select)
var theme = tElement.parent().attr('theme') || uiSelectConfig.theme;
return theme + '/choices.tpl.html';
},

compile: function(tElement, tAttrs) {
var templateUrl = theme + '/choices.tpl.html';
var choicesContent = angular.element($templateCache.get(templateUrl));

if (!tAttrs.repeat) throw uiSelectMinErr('repeat', "Expected 'repeat' expression.");

return function link(scope, element, attrs, $select, transcludeFn) {

// var repeat = RepeatParser.parse(attrs.repeat);
var groupByExp = attrs.groupBy;
var groupByExp = tAttrs.groupBy;
var parserResult = RepeatParser.parse(tAttrs.repeat, groupByExp);

$select.parseRepeatAttr(attrs.repeat, groupByExp); //Result ready at $select.parserResult
if(groupByExp) {
var groups = choicesContent.querySelectorAll('.ui-select-choices-group');
if (groups.length !== 1) throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-group but got '{0}'.", groups.length);
groups.attr('ng-repeat', RepeatParser.getGroupNgRepeatExpression());
}

if(groupByExp) {
var groups = element.querySelectorAll('.ui-select-choices-group');
if (groups.length !== 1) throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-group but got '{0}'.", groups.length);
groups.attr('ng-repeat', RepeatParser.getGroupNgRepeatExpression());
}
var choices = choicesContent.querySelectorAll('.ui-select-choices-row');
if (choices.length !== 1) {
throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-row but got '{0}'.", choices.length);
}

var choices = element.querySelectorAll('.ui-select-choices-row');
if (choices.length !== 1) {
throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-row but got '{0}'.", choices.length);
}
choices.attr('ng-repeat', RepeatParser.getNgRepeatExpression(parserResult.itemName, '$select.items', parserResult.trackByExp, groupByExp))
.attr('ng-mouseenter', '$select.setActiveItem('+parserResult.itemName +')')
.attr('ng-click', '$select.select(' + parserResult.itemName + ')');

choices.attr('ng-repeat', RepeatParser.getNgRepeatExpression($select.parserResult.itemName, '$select.items', $select.parserResult.trackByExp, groupByExp))
.attr('ng-mouseenter', '$select.setActiveItem('+$select.parserResult.itemName +')')
.attr('ng-click', '$select.select(' + $select.parserResult.itemName + ')');
var rowsInner = choicesContent.querySelectorAll('.ui-select-choices-row-inner');
if (rowsInner.length !== 1)
throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-row-inner but got '{0}'.", rowsInner.length);

var rowsInner = element.querySelectorAll('.ui-select-choices-row-inner');
if (rowsInner.length !== 1) throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-row-inner but got '{0}'.", rowsInner.length);
rowsInner.attr('uis-transclude-append', ''); //Adding uisTranscludeAppend directive to row element after choices element has ngRepeat
// transclude main element children to inner row for repetition via
// ng-repeat and make choices content the new child of the main element
rowsInner.append(tElement.children());
tElement.append(choicesContent);

$compile(element, transcludeFn)(scope); //Passing current transcludeFn to be able to append elements correctly from uisTranscludeAppend
return function link(scope, element, attrs, $select) {
$select.parseRepeatAttr(attrs.repeat, attrs.groupBy);

scope.$watch('$select.search', function() {
$select.activeIndex = 0;
Expand All @@ -619,15 +617,7 @@
}
};
}])
.directive('uisTranscludeAppend', function () {
return {
link: function (scope, element, attrs, ctrl, transclude) {
transclude(scope, function (clone) {
element.append(clone);
});
}
};
})

.directive('uiSelectMatch', ['uiSelectConfig', function(uiSelectConfig) {
return {
restrict: 'EA',
Expand Down
14 changes: 7 additions & 7 deletions test/select.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ describe('ui-select tests', function() {
<ui-select-match></ui-select-match> \
</ui-select>'
);
}).toThrow(new Error('[ui.select:transcluded] Expected 1 .ui-select-choices but got \'0\'.'));
}).toThrow(new Error('[ui.select:transcluded] Expected 1 ui-select-choices but got \'0\'.'));
});

it('should throw when no repeat attribute is provided to ui-select-choices', function() {
Expand Down Expand Up @@ -332,7 +332,7 @@ describe('ui-select tests', function() {
clickItem(el, 'Samantha');
expect(scope.selection.selected).toBe(scope.people[5]);
});

it('should parse the model correctly using alias', function() {
var el = compileTemplate(
'<ui-select ng-model="selection.selected"> \
Expand All @@ -347,7 +347,7 @@ describe('ui-select tests', function() {
scope.$digest();
expect(getMatchLabel(el)).toEqual('Samantha');
});

it('should format the model correctly using property of alias', function() {
var el = compileTemplate(
'<ui-select ng-model="selection.selected"> \
Expand All @@ -361,7 +361,7 @@ describe('ui-select tests', function() {
clickItem(el, 'Samantha');
expect(scope.selection.selected).toBe('Samantha');
});

it('should parse the model correctly using property of alias', function() {
var el = compileTemplate(
'<ui-select ng-model="selection.selected"> \
Expand All @@ -376,7 +376,7 @@ describe('ui-select tests', function() {
scope.$digest();
expect(getMatchLabel(el)).toEqual('Samantha');
});

it('should parse the model correctly using property of alias with async choices data', function() {
var el = compileTemplate(
'<ui-select ng-model="selection.selected"> \
Expand Down Expand Up @@ -415,13 +415,13 @@ describe('ui-select tests', function() {
scope.$digest();
expect(getMatchLabel(el)).toEqual('Samantha');
});

it('should format the model correctly without alias', function() {
var el = createUiSelect();
clickItem(el, 'Samantha');
expect(scope.selection.selected).toBe(scope.people[5]);
});

it('should parse the model correctly without alias', function() {
var el = createUiSelect();
scope.selection.selected = scope.people[5];
Expand Down