Add method to remove all observers of property

This commit is contained in:
Bartek Fabiszewski 2019-12-03 21:50:33 +01:00
parent e634e3a01d
commit c6c51c27e2
2 changed files with 109 additions and 19 deletions

View File

@ -36,11 +36,7 @@ export default class uObserve {
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') {
if (Array.isArray(obj)) {
this.observeArray(obj, p1);
} else {
this.observeRecursive(obj, p1); this.observeRecursive(obj, p1);
}
} else { } else {
throw new Error('Invalid arguments'); throw new Error('Invalid arguments');
} }
@ -66,11 +62,11 @@ export default class uObserve {
} }
static isObserved(obj, property) { static isObserved(obj, property) {
if (typeof obj !== 'object' || obj === null) { if (typeof obj !== 'object' || obj === null || !obj.hasOwnProperty(property)) {
return false; return false;
} }
return obj.hasOwnProperty(property) && obj.hasOwnProperty('_values') && return obj.hasOwnProperty('_values') && obj._values.hasOwnProperty(property) &&
obj._values.hasOwnProperty(property) && !!Object.getOwnPropertyDescriptor(obj, property)['set']; !!Object.getOwnPropertyDescriptor(obj, property)['set'];
} }
/** /**
@ -129,12 +125,16 @@ export default class uObserve {
* @param {ObserveCallback} observer * @param {ObserveCallback} observer
*/ */
static observeRecursive(obj, observer) { static observeRecursive(obj, observer) {
if (Array.isArray(obj)) {
this.observeArray(obj, observer);
} else {
for (const prop in obj) { for (const prop in obj) {
if (obj.hasOwnProperty(prop)) { if (obj.hasOwnProperty(prop)) {
uObserve.observeProperty(obj, prop, observer); uObserve.observeProperty(obj, prop, observer);
} }
} }
} }
}
/** /**
* Observe array * Observe array
@ -148,7 +148,7 @@ export default class uObserve {
const descriptor = Object.getOwnPropertyDescriptor(Array.prototype, operation); const descriptor = Object.getOwnPropertyDescriptor(Array.prototype, operation);
descriptor.value = function () { descriptor.value = function () {
const result = Array.prototype[operation].apply(arr, arguments); const result = Array.prototype[operation].apply(arr, arguments);
console.log(`[${operation}] ` + arr.length ? `[${arr[0]}, …](${arr.length})` : arr); console.log(`[${operation}] ` + (arr.length ? `[${arr[0]}, …](${arr.length})` : arr));
uObserve.notify(arr._observers, arr); uObserve.notify(arr._observers, arr);
return result; return result;
}; };
@ -182,8 +182,8 @@ export default class uObserve {
/** /**
* Remove observer from object's property or all it's properties * Remove observer from object's property or all it's properties
* unobserve(obj, prop, observer) observes given property prop; * unobserve(obj, prop, observer) unobserves given property prop;
* unobserve(obj, observer) observes all properties of object obj. * unobserve(obj, observer) unobserves all properties of object obj.
* @param {Object} obj * @param {Object} obj
* @param {(string|ObserveCallback)} p1 * @param {(string|ObserveCallback)} p1
* @param {ObserveCallback=} p2 * @param {ObserveCallback=} p2
@ -202,6 +202,32 @@ export default class uObserve {
} }
} }
/**
* Remove all observers from object's property or all it's properties
* unobserve(obj, prop) removes all observes from given property prop;
* unobserve(obj) removes all observers from all properties of object obj.
* @param {Object} obj
* @param {string} property
*/
static unobserveAll(obj, property) {
if (this.isObserved(obj, property)) {
console.log(`Removing all observers for ${property}`);
if (Array.isArray(obj[property])) {
this.restoreArrayPrototypes(obj[property]);
} else if (typeof obj[property] === 'object' && obj[property] !== null) {
for (const prop in obj[property]) {
if (obj[property].hasOwnProperty(prop)) {
this.unobserveAll(obj[property], prop);
}
}
}
delete obj._observers[property];
delete obj[property];
obj[property] = obj._values[property];
delete obj._values[property];
}
}
/** /**
* Remove observer from object's property * Remove observer from object's property
* @param {Object} obj * @param {Object} obj
@ -241,13 +267,17 @@ export default class uObserve {
static unobserveArray(arr, observer) { static unobserveArray(arr, observer) {
this.removeObserver(arr, observer); this.removeObserver(arr, observer);
if (!arr._observers.size) { if (!arr._observers.size) {
this.restoreArrayPrototypes(arr);
}
}
static restoreArrayPrototypes(arr) {
[ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift' ].forEach( [ 'pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift' ].forEach(
(operation) => { (operation) => {
const descriptor = Object.getOwnPropertyDescriptor(Array.prototype, operation); const descriptor = Object.getOwnPropertyDescriptor(Array.prototype, operation);
Object.defineProperty(arr, operation, descriptor); Object.defineProperty(arr, operation, descriptor);
}); });
} }
}
/** /**
* Remove observer from object's property * Remove observer from object's property

View File

@ -335,6 +335,66 @@ describe('Observe tests', () => {
expect(array).toEqual([ 1, 2, 3 ]); expect(array).toEqual([ 1, 2, 3 ]);
}); });
it('should remove all observers of object property', () => {
// given
let result2 = false;
let resultValue2;
const observer = (value) => {
result = true;
resultValue = value;
};
const observer2 = (value) => {
result2 = true;
resultValue2 = value;
};
uObserve.observe(object, 'observed', observer);
uObserve.observe(object, 'observed', observer2);
// when
uObserve.unobserveAll(object, 'observed');
expect(result).toBe(false);
expect(result2).toBe(false);
object.observed = 2;
// then
expect(result).toBe(false);
expect(resultValue).toBe(undefined);// eslint-disable-line no-undefined
expect(result2).toBe(false);
// noinspection JSUnusedAssignment
expect(resultValue2).toBe(undefined);// eslint-disable-line no-undefined
expect(object.observed).toBe(2);
});
it('should remove all observers from array property', () => {
// given
let result2 = false;
let resultValue2;
const observer = (value) => {
result = true;
resultValue = value;
};
const observer2 = (value) => {
result2 = true;
resultValue2 = value;
};
const array = [ 1, 2 ];
object.arr = array;
uObserve.observe(object, 'arr', observer);
uObserve.observe(object, 'arr', observer2);
// when
uObserve.unobserveAll(object, 'arr');
expect(result).toBe(false);
expect(result2).toBe(false);
array.push(3);
// then
expect(result).toBe(false);
expect(result2).toBe(false);
expect(resultValue).toEqual(undefined);// eslint-disable-line no-undefined
// noinspection JSUnusedAssignment
expect(resultValue2).toEqual(undefined);// eslint-disable-line no-undefined
expect(object.arr).toEqual([ 1, 2, 3 ]);
});
it('should throw error when observing non-existing property', () => { it('should throw error when observing non-existing property', () => {
// given // given
const nonExisting = '___non-existing___'; const nonExisting = '___non-existing___';