Allow silent changes to observed property

This commit is contained in:
Bartek Fabiszewski 2019-12-01 16:04:42 +01:00
parent ae6b90bee3
commit e634e3a01d
2 changed files with 91 additions and 0 deletions

View File

@ -30,6 +30,9 @@ export default class uObserve {
* @param {ObserveCallback=} p2 * @param {ObserveCallback=} p2
*/ */
static observe(obj, p1, p2) { static observe(obj, p1, p2) {
if (typeof obj !== 'object' || obj === null) {
throw new Error('Invalid argument: invalid object');
}
if (typeof p2 === 'function') { if (typeof p2 === 'function') {
this.observeProperty(obj, p1, p2); this.observeProperty(obj, p1, p2);
} else if (typeof p1 === 'function') { } else if (typeof p1 === 'function') {
@ -62,6 +65,31 @@ export default class uObserve {
} }
} }
static isObserved(obj, property) {
if (typeof obj !== 'object' || obj === null) {
return false;
}
return obj.hasOwnProperty(property) && obj.hasOwnProperty('_values') &&
obj._values.hasOwnProperty(property) && !!Object.getOwnPropertyDescriptor(obj, property)['set'];
}
/**
* Set observed property value without notifying observers
* @param {Object} obj
* @param {string} property
* @param {*} value
*/
static setSilently(obj, property, value) {
if (!obj.hasOwnProperty(property)) {
throw new Error(`Invalid argument: object does not have property "${property}"`);
}
if (this.isObserved(obj, property)) {
obj._values[property] = value;
} else {
obj[property] = value;
}
}
/** /**
* Observe object's property. On change call observer * Observe object's property. On change call observer
* @param {Object} obj * @param {Object} obj
@ -69,6 +97,9 @@ export default class uObserve {
* @param {ObserveCallback} observer * @param {ObserveCallback} observer
*/ */
static observeProperty(obj, property, observer) { static observeProperty(obj, property, observer) {
if (!obj.hasOwnProperty(property)) {
throw new Error(`Invalid argument: object does not have property "${property}"`);
}
this.addObserver(obj, observer, property); this.addObserver(obj, observer, property);
if (!obj.hasOwnProperty('_values')) { if (!obj.hasOwnProperty('_values')) {
Object.defineProperty(obj, '_values', { enumerable: false, configurable: false, value: {} }); Object.defineProperty(obj, '_values', { enumerable: false, configurable: false, value: {} });

View File

@ -334,6 +334,65 @@ describe('Observe tests', () => {
expect(resultValue2).toEqual(undefined);// eslint-disable-line no-undefined expect(resultValue2).toEqual(undefined);// eslint-disable-line no-undefined
expect(array).toEqual([ 1, 2, 3 ]); expect(array).toEqual([ 1, 2, 3 ]);
}); });
it('should throw error when observing non-existing property', () => {
// given
const nonExisting = '___non-existing___';
expect(object.hasOwnProperty(nonExisting)).toBe(false);
// then
expect(() => uObserve.observe(object, nonExisting, (value) => {
result = true;
resultValue = value;
})).toThrow();
expect(object.hasOwnProperty(nonExisting)).toBe(false);
});
it('should throw error when observing non-object', () => {
// given
const nonExisting = '___non-existing___';
// then
expect(() => uObserve.observe(nonExisting, (value) => {
result = true;
resultValue = value;
})).toThrow();
});
it('should throw error when observing null object', () => {
// given
const nullObject = null;
// then
expect(() => uObserve.observe(nullObject, (value) => {
result = true;
resultValue = value;
})).toThrow();
});
it('should not notify observers when observed property is silently changed', () => {
// given
uObserve.observe(object, 'observed', (value) => {
result = true;
resultValue = value;
});
// when
expect(result).toBe(false);
uObserve.setSilently(object, 'observed', 2);
// then
expect(result).toBe(false);
// eslint-disable-next-line no-undefined
expect(resultValue).toBe(undefined);
});
it('should return true if property is observed', () => {
// when
uObserve.observe(object, 'observed', (value) => {
result = true;
resultValue = value;
});
// then
expect(uObserve.isObserved(object, 'observed')).toBe(true);
});
}); });
describe('when notify is called directly', () => { describe('when notify is called directly', () => {
@ -351,5 +410,6 @@ describe('Observe tests', () => {
expect(result).toBe(true); expect(result).toBe(true);
expect(result2).toBe(true); expect(result2).toBe(true);
}); });
}); });
}); });