Add method to remove all observers of property
This commit is contained in:
parent
e634e3a01d
commit
c6c51c27e2
@ -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
|
||||||
|
@ -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___';
|
||||||
|
Loading…
x
Reference in New Issue
Block a user