diff --git a/src/checked-observer.js b/src/checked-observer.js index 00ad5cb9..64ad0155 100644 --- a/src/checked-observer.js +++ b/src/checked-observer.js @@ -1,6 +1,7 @@ import {subscriberCollection} from './subscriber-collection'; const checkedArrayContext = 'CheckedObserver:array'; +const checkedValueContext = 'CheckedObserver:value'; @subscriberCollection() export class CheckedObserver { @@ -42,8 +43,14 @@ export class CheckedObserver { } call(context, splices) { - // called by task queue and array observer. + // called by task queue, array observer, and model/value observer. this.synchronizeElement(); + // if the input's model or value property is data-bound, subscribe to it's + // changes to enable synchronizing the element's checked status when a change occurs. + if (!this.valueObserver + && (this.valueObserver = this.element.__observers__.model || this.element.__observers__.value)) { + this.valueObserver.subscribe(checkedValueContext, this); + } } synchronizeElement() { @@ -117,5 +124,8 @@ export class CheckedObserver { this.arrayObserver.unsubscribe(checkedArrayContext, this); this.arrayObserver = null; } + if (this.valueObserver) { + this.valueObserver.unsubscribe(checkedValueContext, this); + } } } diff --git a/src/observer-locator.js b/src/observer-locator.js index 695f21ef..7c1942e2 100644 --- a/src/observer-locator.js +++ b/src/observer-locator.js @@ -179,6 +179,7 @@ export class ObserverLocator { || propertyName === 'style' || propertyName === 'css' || propertyName === 'value' && (obj.tagName.toLowerCase() === 'input' || obj.tagName.toLowerCase() === 'select') || propertyName === 'checked' && obj.tagName.toLowerCase() === 'input' + || propertyName === 'model' && obj.tagName.toLowerCase() === 'input' || /^xlink:.+$/.exec(propertyName)) { return this.getObserver(obj, propertyName); } diff --git a/test/checked-observer.spec.js b/test/checked-observer.spec.js index c1fa2bb7..df121670 100644 --- a/test/checked-observer.spec.js +++ b/test/checked-observer.spec.js @@ -22,6 +22,7 @@ describe('CheckedObserver', () => { beforeAll(() => { obj = { selectedItems: [] }; el = createElement(''); + observerLocator.getObserver(el, 'value'); document.body.appendChild(el); binding = getBinding(observerLocator, obj, 'selectedItems', el, 'checked', bindingMode.twoWay).binding; }); @@ -39,6 +40,19 @@ describe('CheckedObserver', () => { }, 0); }); + it('responds to element value change', done => { + expect(el.checked).toBe(true); + el.__observers__.value.setValue('ZZZZ'); + setTimeout(() => { + expect(el.checked).toBe(false); + el.__observers__.value.setValue('A'); + setTimeout(() => { + expect(el.checked).toBe(true); + done(); + }); + }, 0); + }); + it('responds to element change', done => { el.checked = false; el.dispatchEvent(DOM.createCustomEvent('change')); @@ -77,6 +91,7 @@ describe('CheckedObserver', () => { obj = { selectedItems: [], itemA: {} }; el = createElement(''); el.model = obj.itemA; + observerLocator.getObserver(el, 'model'); document.body.appendChild(el); binding = getBinding(observerLocator, obj, 'selectedItems', el, 'checked', bindingMode.twoWay).binding; }); @@ -94,6 +109,19 @@ describe('CheckedObserver', () => { }, 0); }); + it('responds to element value change', done => { + expect(el.checked).toBe(true); + el.__observers__.model.setValue({}); + setTimeout(() => { + expect(el.checked).toBe(false); + el.__observers__.model.setValue(obj.itemA); + setTimeout(() => { + expect(el.checked).toBe(true); + done(); + }); + }, 0); + }); + it('responds to element change', done => { el.checked = false; el.dispatchEvent(DOM.createCustomEvent('change')); diff --git a/test/observer-locator.spec.js b/test/observer-locator.spec.js index a22b1478..3d1162a1 100644 --- a/test/observer-locator.spec.js +++ b/test/observer-locator.spec.js @@ -165,4 +165,8 @@ describe('ObserverLocator', () => { it('getAccessor returns ValueAttributeObserver for input.value', () => { expect(locator.getAccessor(document.createElement('input'), 'value') instanceof ValueAttributeObserver).toBe(true); }); + + it('getAccessor returns SetterObserver for input.model', () => { + expect(locator.getAccessor(document.createElement('input'), 'model') instanceof SetterObserver).toBe(true); + }); });