model.removeElement(atom)
expect(completeSpy).toHaveBeenCalled()
})
+
+ it('should subscribe to existing elements when queries are assigned', () => {
+ const expression = new CustomFieldQueryExpression()
+ const nextSpy = jest.spyOn(model.changed, 'next')
+ model.queries = [expression]
+ expression.changed.next(expression)
+ expect(nextSpy).toHaveBeenCalledWith(model)
+ })
})
})
} from '@ng-bootstrap/ng-bootstrap'
import { NgSelectComponent, NgSelectModule } from '@ng-select/ng-select'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
-import { first, Subject, takeUntil } from 'rxjs'
+import { first, Subject, Subscription, takeUntil } from 'rxjs'
import { CustomField, CustomFieldDataType } from 'src/app/data/custom-field'
import {
CUSTOM_FIELD_QUERY_MAX_ATOMS,
import { DocumentLinkComponent } from '../input/document-link/document-link.component'
export class CustomFieldQueriesModel {
- public queries: CustomFieldQueryElement[] = []
+ private _queries: CustomFieldQueryElement[] = []
+ private rootSubscriptions: Subscription[] = []
public readonly changed = new Subject<CustomFieldQueriesModel>()
+ public get queries(): CustomFieldQueryElement[] {
+ return this._queries
+ }
+
+ public set queries(value: CustomFieldQueryElement[]) {
+ this.teardownRootSubscriptions()
+ this._queries = value ?? []
+ for (const element of this._queries) {
+ this.rootSubscriptions.push(
+ element.changed.subscribe(() => {
+ this.changed.next(this)
+ })
+ )
+ }
+ }
+
public clear(fireEvent = true) {
this.queries = []
if (fireEvent) {
public addExpression(
expression: CustomFieldQueryExpression = new CustomFieldQueryExpression()
) {
- if (this.queries.length > 0) {
- ;(
- (this.queries[0] as CustomFieldQueryExpression)
- .value as CustomFieldQueryElement[]
- ).push(expression)
- } else {
- this.queries.push(expression)
+ if (this.queries.length === 0) {
+ this.queries = [expression]
+ return
}
+ ;(
+ (this.queries[0] as CustomFieldQueryExpression)
+ .value as CustomFieldQueryElement[]
+ ).push(expression)
expression.changed.subscribe(() => {
this.changed.next(this)
})
this.changed.next(this)
}
}
+
+ private teardownRootSubscriptions() {
+ for (const subscription of this.rootSubscriptions) {
+ subscription.unsubscribe()
+ }
+ this.rootSubscriptions = []
+ }
}
@Component({
-import { fakeAsync, tick } from '@angular/core/testing'
import {
CustomFieldQueryElementType,
CustomFieldQueryLogicalOperator,
expect(atom.serialize()).toEqual([1, 'operator', 'value'])
})
- it('should emit changed on value change after debounce', fakeAsync(() => {
+ it('should emit changed on value change immediately', () => {
const atom = new CustomFieldQueryAtom()
const changeSpy = jest.spyOn(atom.changed, 'next')
atom.value = 'new value'
- tick(1000)
expect(changeSpy).toHaveBeenCalled()
- }))
+ })
+
+ it('should ignore duplicate array emissions', () => {
+ const atom = new CustomFieldQueryAtom()
+ atom.operator = CustomFieldQueryOperator.In
+ const changeSpy = jest.fn()
+ atom.changed.subscribe(changeSpy)
+
+ atom.value = [1, 2]
+ expect(changeSpy).toHaveBeenCalledTimes(1)
+
+ changeSpy.mockClear()
+ atom.value = [1, 2]
+ expect(changeSpy).not.toHaveBeenCalled()
+ })
+
+ it('should emit when array values differ while length matches', () => {
+ const atom = new CustomFieldQueryAtom()
+ atom.operator = CustomFieldQueryOperator.In
+ const changeSpy = jest.fn()
+ atom.changed.subscribe(changeSpy)
+
+ atom.value = [1, 2]
+ changeSpy.mockClear()
+ atom.value = [1, 3]
+ expect(changeSpy).toHaveBeenCalledTimes(1)
+ })
})
describe('CustomFieldQueryExpression', () => {
-import { Subject, debounceTime, distinctUntilChanged } from 'rxjs'
+import { Subject, distinctUntilChanged } from 'rxjs'
import { v4 as uuidv4 } from 'uuid'
import {
CUSTOM_FIELD_QUERY_VALUE_TYPES_BY_OPERATOR,
protected override connectValueModelChanged(): void {
this.valueModelChanged
- .pipe(debounceTime(1000), distinctUntilChanged())
+ .pipe(
+ distinctUntilChanged((previous, current) => {
+ if (Array.isArray(previous) && Array.isArray(current)) {
+ if (previous.length !== current.length) {
+ return false
+ }
+ for (let i = 0; i < previous.length; i++) {
+ if (previous[i] !== current[i]) {
+ return false
+ }
+ }
+ return true
+ }
+ return previous === current
+ })
+ )
.subscribe(() => {
this.changed.next(this)
})