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

Commit d17889c

Browse files
committed
feat(ngModel) bind to single property instead of entire object
1 parent 468b3be commit d17889c

File tree

1 file changed

+55
-41
lines changed

1 file changed

+55
-41
lines changed

src/select.js

Lines changed: 55 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -43,57 +43,40 @@
4343
* Original discussion about parsing "repeat" attribute instead of fully relying on ng-repeat:
4444
* https://github.com/angular-ui/ui-select/commit/5dd63ad#commitcomment-5504697
4545
*/
46-
.service('RepeatParser', ['uiSelectMinErr', function(uiSelectMinErr) {
46+
.service('RepeatParser', ['uiSelectMinErr','$parse', function(uiSelectMinErr, $parse) {
4747
var self = this;
4848

4949
/**
5050
* Example:
5151
* expression = "address in addresses | filter: {street: $select.search} track by $index"
52-
* lhs = "address",
53-
* rhs = "addresses | filter: {street: $select.search}",
52+
* itemName = "address",
53+
* source = "addresses | filter: {street: $select.search}",
5454
* trackByExp = "$index",
55-
* valueIdentifier = "address",
56-
* keyIdentifier = undefined
5755
*/
5856
self.parse = function(expression) {
59-
if (!expression) {
60-
throw uiSelectMinErr('repeat', "Expected 'repeat' expression.");
61-
}
6257

63-
var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
58+
var match = expression.match(/^\s*(?:([\s\S]+?)\s+as\s+)?([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/);
6459

6560
if (!match) {
6661
throw uiSelectMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
67-
expression);
68-
}
69-
70-
var lhs = match[1]; // Left-hand side
71-
var rhs = match[2]; // Right-hand side
72-
var trackByExp = match[3];
73-
74-
match = lhs.match(/^(?:([\$\w]+)|\(([\$\w]+)\s*,\s*([\$\w]+)\))$/);
75-
if (!match) {
76-
throw uiSelectMinErr('iidexp', "'_item_' in '_item_ in _collection_' should be an identifier or '(_key_, _value_)' expression, but got '{0}'.",
77-
lhs);
62+
expression);
7863
}
7964

80-
// Unused for now
81-
// var valueIdentifier = match[3] || match[1];
82-
// var keyIdentifier = match[2];
83-
8465
return {
85-
lhs: lhs,
86-
rhs: rhs,
87-
trackByExp: trackByExp
66+
itemName: match[2], // (lhs) Left-hand side,
67+
source: match[3], // (rhs) Right-hand side,
68+
trackByExp: match[4],
69+
modelMapper: $parse(match[1] || match[2])
8870
};
71+
8972
};
9073

9174
self.getGroupNgRepeatExpression = function() {
9275
return '($group, $items) in $select.groups';
9376
};
9477

95-
self.getNgRepeatExpression = function(lhs, rhs, trackByExp, grouped) {
96-
var expression = lhs + ' in ' + (grouped ? '$items' : rhs);
78+
self.getNgRepeatExpression = function(itemName, source, trackByExp, grouped) {
79+
var expression = itemName + ' in ' + (grouped ? '$items' : source);
9780
if (trackByExp) {
9881
expression += ' track by ' + trackByExp;
9982
}
@@ -180,14 +163,15 @@
180163
ctrl.items = items;
181164
}
182165

183-
var repeat = RepeatParser.parse(repeatAttr),
184-
setItemsFn = groupByExp ? updateGroups : setPlainItems;
166+
var setItemsFn = groupByExp ? updateGroups : setPlainItems;
167+
168+
ctrl.parserResult = RepeatParser.parse(repeatAttr);
185169

186170
ctrl.isGrouped = !!groupByExp;
187-
ctrl.itemProperty = repeat.lhs;
171+
ctrl.itemProperty = ctrl.parserResult.itemName;
188172

189173
// See https://github.com/angular/angular.js/blob/v1.2.15/src/ng/directive/ngRepeat.js#L259
190-
$scope.$watchCollection(repeat.rhs, function(items) {
174+
$scope.$watchCollection(ctrl.parserResult.source, function(items) {
191175

192176
if (items === undefined || items === null) {
193177
// If the user specifies undefined or null => reset the collection
@@ -204,6 +188,7 @@
204188
}
205189

206190
});
191+
207192
};
208193

209194
var _refreshDelayPromise;
@@ -355,6 +340,32 @@
355340
var $select = ctrls[0];
356341
var ngModel = ctrls[1];
357342

343+
//From view --> model
344+
ngModel.$parsers.unshift(function (inputValue) {
345+
var locals = {};
346+
locals[$select.parserResult.itemName] = inputValue;
347+
var result = $select.parserResult.modelMapper(scope, locals);
348+
return result;
349+
});
350+
351+
//From model --> view
352+
ngModel.$formatters.unshift(function (inputValue) {
353+
var match = $select.parserResult.source.match(/^\s*([\S]+).*$/);
354+
var data = scope[match[1]];
355+
if (data){
356+
for (var i = data.length - 1; i >= 0; i--) {
357+
var locals = {};
358+
locals[$select.parserResult.itemName] = data[i];
359+
var result = $select.parserResult.modelMapper(scope, locals);
360+
if (result == inputValue){
361+
return data[i];
362+
}
363+
}
364+
}
365+
return inputValue;
366+
});
367+
368+
358369
//Idea from: https://github.com/ivaynberg/select2/blob/79b5bf6db918d7560bdd959109b7bcfb47edaf43/select2.js#L1954
359370
var focusser = angular.element("<input ng-disabled='$select.disabled' class='ui-select-focusser ui-select-offscreen' type='text' aria-haspopup='true' role='button' />");
360371
$compile(focusser)(scope);
@@ -533,9 +544,15 @@
533544
},
534545

535546
compile: function(tElement, tAttrs) {
536-
var repeat = RepeatParser.parse(tAttrs.repeat);
537-
var groupByExp = tAttrs.groupBy;
547+
548+
if (!tAttrs.repeat) throw uiSelectMinErr('repeat', "Expected 'repeat' expression.");
549+
538550
return function link(scope, element, attrs, $select, transcludeFn) {
551+
552+
// var repeat = RepeatParser.parse(attrs.repeat);
553+
var groupByExp = attrs.groupBy;
554+
555+
$select.parseRepeatAttr(attrs.repeat, groupByExp); //Result ready at $select.parserResult
539556

540557
if(groupByExp) {
541558
var groups = element.querySelectorAll('.ui-select-choices-group');
@@ -548,10 +565,9 @@
548565
throw uiSelectMinErr('rows', "Expected 1 .ui-select-choices-row but got '{0}'.", choices.length);
549566
}
550567

551-
choices.attr('ng-repeat', RepeatParser.getNgRepeatExpression(repeat.lhs, '$select.items', repeat.trackByExp, groupByExp))
552-
.attr('ng-mouseenter', '$select.setActiveItem('+repeat.lhs+')')
553-
.attr('ng-click', '$select.select(' + repeat.lhs + ')');
554-
568+
choices.attr('ng-repeat', RepeatParser.getNgRepeatExpression($select.parserResult.itemName, '$select.items', $select.parserResult.trackByExp, groupByExp))
569+
.attr('ng-mouseenter', '$select.setActiveItem('+$select.parserResult.itemName +')')
570+
.attr('ng-click', '$select.select(' + $select.parserResult.itemName + ')');
555571

556572
transcludeFn(function(clone) {
557573
var rowsInner = element.querySelectorAll('.ui-select-choices-row-inner');
@@ -562,8 +578,6 @@
562578
$compile(element)(scope);
563579
});
564580

565-
$select.parseRepeatAttr(attrs.repeat, groupByExp);
566-
567581
scope.$watch('$select.search', function() {
568582
$select.activeIndex = 0;
569583
$select.refresh(attrs.refresh);

0 commit comments

Comments
 (0)