Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat(select): enable matcher functions
This change enables developers to specify a matcher function (equality comparer) when binding to select value attributes or checkbox/radio checked attributes. If a matcher function is not supplied the default matcher (===) will be used.

Fixes #94
  • Loading branch information
jdanyow committed Nov 30, 2015
1 parent 2662f7f commit c69aa68
Showing 1 changed file with 57 additions and 24 deletions.
81 changes: 57 additions & 24 deletions src/element-observation.js
Expand Up @@ -155,7 +155,7 @@ export class SelectValueObserver {
this.value = newValue;
this.synchronizeOptions();
// queue up an initial sync after the bindings have been evaluated.
if (this.element.options.length > 0 && !this.initialSync) {
if (!this.initialSync) {
this.initialSync = true;
this.observerLocator.taskQueue.queueMicroTask(this);
}
Expand All @@ -167,54 +167,85 @@ export class SelectValueObserver {
}

synchronizeOptions() {
var value = this.value, i, options, option, optionValue, clear, isArray;
let value = this.value, clear, isArray;

if (value === null || value === undefined) {
clear = true;
} else if (Array.isArray(value)) {
isArray = true;
}

options = this.element.options;
i = options.length;
let options = this.element.options;
let i = options.length;
let matcher = this.element.matcher || ((a, b) => a === b);
while(i--) {
option = options.item(i);
let option = options.item(i);
if (clear) {
option.selected = false;
continue;
}
optionValue = option.hasOwnProperty('model') ? option.model : option.value;
let optionValue = option.hasOwnProperty('model') ? option.model : option.value;
if (isArray) {
option.selected = value.indexOf(optionValue) !== -1;
option.selected = !!value.find(item => !!matcher(optionValue, item));
continue;
}
option.selected = value === optionValue;
option.selected = !!matcher(optionValue, value);
}
}

synchronizeValue() {
var options = this.element.options, option, i, ii, count = 0, value = [];
let options = this.element.options,
count = 0,
value = [];

for(i = 0, ii = options.length; i < ii; i++) {
option = options.item(i);
for (let i = 0, ii = options.length; i < ii; i++) {
let option = options.item(i);
if (!option.selected) {
continue;
}
value[count] = option.hasOwnProperty('model') ? option.model : option.value;
value.push(option.hasOwnProperty('model') ? option.model : option.value);
count++;
}

if (!this.element.multiple) {
if (this.element.multiple) {
// multi-select
if (Array.isArray(this.value)) {
let matcher = this.element.matcher || ((a, b) => a === b);
// remove items that are no longer selected.
let i = 0;
while (i < this.value.length) {
let a = this.value[i];
if (value.findIndex(b => matcher(a, b)) === -1) {
this.value.splice(i, 1);
} else {
i++;
}
}
// add items that have been selected.
i = 0;
while (i < value.length) {
let a = value[i];
if (this.value.findIndex(b => matcher(a, b)) === -1) {
this.value.push(a);
}
i++;
}
return; // don't notify.
}
} else {
// single-select
if (count === 0) {
value = null;
} else {
value = value[0];
}
}

this.oldValue = this.value;
this.value = value;
this.notify();
if (value !== this.value) {
this.oldValue = this.value;
this.value = value;
this.notify();
}
}

notify() {
Expand Down Expand Up @@ -289,7 +320,7 @@ export class CheckedObserver {
this.value = newValue;
this.synchronizeElement();
// queue up an initial sync after the bindings have been evaluated.
if (!this.element.hasOwnProperty('model') && !this.initialSync) {
if (!this.initialSync) {
this.initialSync = true;
this.observerLocator.taskQueue.queueMicroTask(this);
}
Expand All @@ -301,26 +332,28 @@ export class CheckedObserver {
}

synchronizeElement() {
var value = this.value,
let value = this.value,
element = this.element,
elementValue = element.hasOwnProperty('model') ? element.model : element.value,
isRadio = element.type === 'radio';
isRadio = element.type === 'radio',
matcher = element.matcher || ((a, b) => a === b);

element.checked =
isRadio && value === elementValue
isRadio && !!matcher(value, elementValue)
|| !isRadio && value === true
|| !isRadio && Array.isArray(value) && value.indexOf(elementValue) !== -1;
|| !isRadio && Array.isArray(value) && !!value.find(item => !!matcher(item, elementValue));
}

synchronizeValue(){
var value = this.value,
let value = this.value,
element = this.element,
elementValue = element.hasOwnProperty('model') ? element.model : element.value,
index;
index,
matcher = element.matcher || ((a, b) => a === b);

if (element.type === 'checkbox') {
if (Array.isArray(value)) {
index = value.indexOf(elementValue);
index = value.findIndex(item => !!matcher(item, elementValue));
if (element.checked && index === -1) {
value.push(elementValue);
} else if (!element.checked && index !== -1) {
Expand Down

0 comments on commit c69aa68

Please sign in to comment.