This commit is contained in:
Niels Lyngsø
2023-01-13 08:37:57 +01:00
parent febf72c76c
commit f2355f6135
5 changed files with 194 additions and 2 deletions

View File

@@ -0,0 +1,54 @@
import { appendToFrozenArray, UniqueBehaviorSubject } from "./unique-behavior-subject";
/**
* @export
* @class UniqueObjectBehaviorSubject
* @extends {UniqueBehaviorSubject<T>}
* @description - A RxJS UniqueObjectBehaviorSubject which deepFreezes the object-data to ensure its not manipulated from any implementations.
* Additionally the Subject ensures the data is unique, not updating any Observes unless there is an actual change of the content.
*
* The UniqueObjectBehaviorSubject provides methods to append data when the data is an Object.
*/
export class UniqueArrayBehaviorSubject<T> extends UniqueBehaviorSubject<T[]> {
constructor(initialData: T[], private _uniqueCompare?: (existingEntry: T, newEntry: T) => boolean) {
super(initialData);
}
/**
* @method append
* @param {Partial<T>} partialData - A object containing some of the data for this Subject.
* @description - Append some new data to this Object.
* @example <caption>Example append some data.</caption>
* const data = [
* { key: 1, value: 'foo'},
* { key: 2, value: 'bar'}
* ];
* const mySubject = new UniqueArrayBehaviorSubject(data);
* mySubject.append({ key: 1, value: 'replaced-foo'});
*/
appendOne(entry: T) {
this.next(appendToFrozenArray(this.getValue(), entry, this._uniqueCompare))
}
/**
* @method append
* @param {T[]} entries - A array of new data to be added in this Subject.
* @description - Append some new data to this Object, if it compares to existing data it will replace it.
* @example <caption>Example append some data.</caption>
* const data = [
* { key: 1, value: 'foo'},
* { key: 2, value: 'bar'}
* ];
* const mySubject = new UniqueArrayBehaviorSubject(data);
* mySubject.append([
* { key: 1, value: 'replaced-foo'},
* { key: 3, value: 'another-bla'}
* ]);
*/
append(entries: T[]) {
entries.forEach(x => this.appendOne(x))
}
}

View File

@@ -0,0 +1,60 @@
import { expect } from '@open-wc/testing';
import { createObservablePart, UniqueBehaviorSubject } from './unique-behavior-subject';
describe('UniqueBehaviorSubject', () => {
type ObjectType = {key: string, another: string};
let subject: UniqueBehaviorSubject<ObjectType>;
let initialData: ObjectType;
beforeEach(() => {
initialData = {key: 'some', another: 'myValue'};
subject = new UniqueBehaviorSubject(initialData);
});
it('getValue gives the initial data', () => {
expect(subject.value.another).to.be.equal(initialData.another);
});
it('update via next', () => {
subject.next({key: 'some', another: 'myNewValue'});
expect(subject.value.another).to.be.equal('myNewValue');
});
it('replays latests, no matter the amount of subscriptions.', (done) => {
const observer = subject.asObservable();
observer.subscribe((value) => {
expect(value).to.be.equal(initialData);
});
observer.subscribe((value) => {
expect(value).to.be.equal(initialData);
done();
});
});
it('use createObservablePart, updates on its specific change.', (done) => {
let amountOfCallbacks = 0;
const subObserver = createObservablePart(subject, data => data.another);
subObserver.subscribe((value) => {
amountOfCallbacks++;
if(amountOfCallbacks === 1) {
expect(value).to.be.equal('myValue');
}
if(amountOfCallbacks === 2) {
expect(value).to.be.equal('myNewValue');
done();
}
});
subject.next({key: 'change_this_first_should_not_trigger_update', another: 'myValue'});
subject.next({key: 'some', another: 'myNewValue'});
});
});

View File

@@ -40,10 +40,10 @@ export function naiveObjectComparison(objOne: any, objTwo: any): boolean {
* const newDataSet = appendToFrozenArray(mySubject.getValue(), entry, x => x.key === key);
* mySubject.next(newDataSet);
*/
export function appendToFrozenArray<T>(data: T[], entry: T, uniqueMethod?: (entry: T) => boolean): T[] {
export function appendToFrozenArray<T>(data: T[], entry: T, uniqueMethod?: (existingEntry: T, newEntry: T) => boolean): T[] {
const unFrozenDataSet = [...data];
if(uniqueMethod) {
const indexToReplace = unFrozenDataSet.findIndex(uniqueMethod);
const indexToReplace = unFrozenDataSet.findIndex((x) => uniqueMethod(x, entry));
if(indexToReplace !== -1) {
unFrozenDataSet[indexToReplace] = entry;
} else {

View File

@@ -0,0 +1,52 @@
import { expect } from '@open-wc/testing';
import { createObservablePart } from './unique-behavior-subject';
import { UniqueObjectBehaviorSubject } from './unique-object-behavior-subject';
describe('UniqueObjectBehaviorSubject', () => {
type ObjectType = {key: string, another: string};
let subject: UniqueObjectBehaviorSubject<ObjectType>;
let initialData: ObjectType;
beforeEach(() => {
initialData = {key: 'some', another: 'myValue'};
subject = new UniqueObjectBehaviorSubject(initialData);
});
it('replays latests, no matter the amount of subscriptions.', (done) => {
const observer = subject.asObservable();
observer.subscribe((value) => {
expect(value).to.be.equal(initialData);
});
observer.subscribe((value) => {
expect(value).to.be.equal(initialData);
done();
});
});
it('use createObservablePart, updates on its specific change.', (done) => {
let amountOfCallbacks = 0;
const subObserver = createObservablePart(subject, data => data.another);
subObserver.subscribe((value) => {
amountOfCallbacks++;
if(amountOfCallbacks === 1) {
expect(value).to.be.equal('myValue');
}
if(amountOfCallbacks === 2) {
expect(value).to.be.equal('myNewValue');
done();
}
});
subject.append({key: 'change_this_first_should_not_trigger_update'});
subject.append({another: 'myNewValue'});
});
});

View File

@@ -0,0 +1,26 @@
import { UniqueBehaviorSubject } from "./unique-behavior-subject";
/**
* @export
* @class UniqueObjectBehaviorSubject
* @extends {UniqueBehaviorSubject<T>}
* @description - A RxJS UniqueObjectBehaviorSubject which deepFreezes the object-data to ensure its not manipulated from any implementations.
* Additionally the Subject ensures the data is unique, not updating any Observes unless there is an actual change of the content.
*
* The UniqueObjectBehaviorSubject provides methods to append data when the data is an Object.
*/
export class UniqueObjectBehaviorSubject<T> extends UniqueBehaviorSubject<T> {
/**
* @method append
* @param {Partial<T>} partialData - A object containing some of the data for this Subject.
* @description - Append some new data to this Object.
* @example <caption>Example append some data.</caption>
* const data = {key: 'myKey', value: 'myInitialValue'};
* const mySubject = new UniqueObjectBehaviorSubject(data)
* mySubject.append({value: 'myNewValue'})
*/
append(partialData: Partial<T>) {
this.next({ ...this.getValue(), ...partialData });
}
}