Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Commit

Permalink
wip: yet another way
Browse files Browse the repository at this point in the history
- noop add/removeOption in selectCtrl. Prevents options from being added
before by directives that compile before select
- add add/remove in select preLink
- noop add/remove again in ngOptions. Prevents options from being added
by directive that run postLink before ngOptions
  • Loading branch information
Narretz committed Sep 30, 2015
1 parent ca51125 commit e7cda24
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 44 deletions.
3 changes: 2 additions & 1 deletion src/ng/directive/ngOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -729,7 +729,8 @@ var ngOptionsDirective = ['$compile', '$parse', function($compile, $parse) {
require: ['select', 'ngModel'],
link: {
pre: function ngOptionsPreLink(scope, selectElement, attr, ctrls) {
ctrls[0].ngModelCtrl = null;
ctrls[0].addOption = noop;
ctrls[0].removeOption = noop;
},
post: ngOptionsPostLink
}
Expand Down
64 changes: 34 additions & 30 deletions src/ng/directive/select.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ var SelectController =
var self = this,
optionsMap = new HashMap();

self.optionsMap = optionsMap;

// If the ngModel doesn't get provided then provide a dummy noop version to prevent errors
self.ngModelCtrl = noopNgModelController;

Expand Down Expand Up @@ -68,31 +70,8 @@ var SelectController =
}
};


// Tell the select control that an option, with the given value, has been added
self.addOption = function(value, element) {
assertNotHasOwnProperty(value, '"option value"');
if (value === '') {
self.emptyOption = element;
}
var count = optionsMap.get(value) || 0;
optionsMap.put(value, count + 1);
};

// Tell the select control that an option, with the given value, has been removed
self.removeOption = function(value) {
var count = optionsMap.get(value);
if (count) {
if (count === 1) {
optionsMap.remove(value);
if (value === '') {
self.emptyOption = undefined;
}
} else {
optionsMap.put(value, count - 1);
}
}
};
self.addOption = noop;
self.removeOption = noop;

// Check whether the select control has an option matching the given value
self.hasOption = function(value) {
Expand Down Expand Up @@ -324,6 +303,36 @@ var selectDirective = function() {

selectCtrl.ngModelCtrl = ngModelCtrl;

// Tell the select control that an option, with the given value, has been added
selectCtrl.addOption = function(value, element) {
assertNotHasOwnProperty(value, '"option value"');
if (value === '') {
selectCtrl.emptyOption = element;
}
var count = selectCtrl.optionsMap.get(value) || 0;
selectCtrl.optionsMap.put(value, count + 1);

element.on('$destroy', function() {
selectCtrl.removeOption(value);
selectCtrl.ngModelCtrl.$render();
});
};

// Tell the select control that an option, with the given value, has been removed
selectCtrl.removeOption = function(value) {
var count = selectCtrl.optionsMap.get(value);
if (count) {
if (count === 1) {
selectCtrl.optionsMap.remove(value);
if (value === '') {
selectCtrl.emptyOption = undefined;
}
} else {
selectCtrl.optionsMap.put(value, count - 1);
}
}
};

// We delegate rendering to the `writeValue` method, which can be changed
// if the select can have multiple selected values or if the options are being
// generated by `ngOptions`
Expand Down Expand Up @@ -460,11 +469,6 @@ var optionDirective = ['$interpolate', function($interpolate) {
// The value attribute is static
addOption(attr.value);
}

element.on('$destroy', function() {
selectCtrl.removeOption(attr.value);
selectCtrl.ngModelCtrl.$render();
});
}
};
}
Expand Down
38 changes: 25 additions & 13 deletions test/ng/directive/ngOptionsSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,15 @@ describe('ngOptions', function() {

.directive('oCompileContents', function() {
return {
link: function(scope, element) {
linkLog.push('linkCompileContents');
$compile(element.contents())(scope);
priority: 2,
link: {
pre: function(scope, element) {
linkLog.push('linkCompileContents');
$compile(element.contents())(scope);
}
}
};
});
});

$provide.decorator('ngOptionsDirective', function($delegate) {

Expand Down Expand Up @@ -2170,29 +2173,38 @@ describe('ngOptions', function() {

it('should not throw when a directive compiles the blank option before ngOptions is linked', function() {
expect(function() {
createSelect({
'o-compile-contents': '',
'name': 'select',
'ng-model': 'value',
'ng-options': 'item for item in items'
createSelect({
'o-compile-contents': '',
'name': 'select',
'ng-model': 'value',
'ng-options': 'item for item in items'
}, true);
}).not.toThrow();

expect(linkLog).toEqual(['linkCompileContents', 'linkNgOptions']);

var options = element.find('option');
expect(options.length).toBe(1);
expect(options.eq(0).val()).toBe('');
expect(options.eq(0).text()).toBe('blank');
});


it('should not throw with a directive that replaces', inject(function($templateCache, $httpBackend) {
$templateCache.put('select_template.html', '<select ng-options="option as option for option in selectable_options"> <option value="">This is a test</option> </select>');
$templateCache.put('select_template.html', '<select ng-options="option as option for option in selectable_options"> <option value="">--select--</option> </select>');

scope.options = ['a', 'b', 'c', 'd'];
scope.options = ['a', 'b', 'c'];

expect(function() {
var element = $compile('<custom-select ng-model="value" options="options"></custom-select>')(scope);
element = $compile('<custom-select ng-model="value" options="options"></custom-select>')(scope);
scope.$digest();
dealoc(element);
}).not.toThrow();

var options = element.find('option');
expect(options.length).toBe(4);
expect(options.eq(0).val()).toBe('');
expect(options.eq(0).text()).toBe('--select--');
dealoc(element);
}));

});
Expand Down

0 comments on commit e7cda24

Please sign in to comment.