diff --git a/packages/core/src/data_sources/model/DataResolverListener.ts b/packages/core/src/data_sources/model/DataResolverListener.ts index f2a211904..ef72be56e 100644 --- a/packages/core/src/data_sources/model/DataResolverListener.ts +++ b/packages/core/src/data_sources/model/DataResolverListener.ts @@ -1,9 +1,11 @@ -import { DataSourcesEvents, DataSourceListener } from '../types'; +import { DataSourceListener } from '../types'; import { stringToPath } from '../../utils/mixins'; import { Model } from '../../common'; import EditorModel from '../../editor/model/Editor'; import DataVariable, { DataVariableType } from './DataVariable'; import { DataResolver } from '../types'; +import DataRecord from './DataRecord'; +import DataSource from './DataSource'; import { DataCondition, DataConditionOutputChangedEvent, @@ -88,34 +90,52 @@ export default class DataResolverListener { const path = dataVariable.getResolverPath(); if (!path) return dataListeners; - const normPath = stringToPath(path || '').join('.'); + const dsAll = em.DataSources.all; + const [dsId, drKey, ...propPathParts] = stringToPath(path || ''); const [ds, dr] = em.DataSources.fromPath(path!); + const isIndexPath = !!ds && drKey !== undefined && ds.isRecordIndex(drKey); + const onMatchingSourceChange = (ds: DataSource) => ds.id === dsId && onChangeAndRewatch(); - if (ds) { - dataListeners.push(this.createListener(ds.records, 'add remove reset', onChangeAndRewatch)); + dataListeners.push( + this.createListener(dsAll, 'add remove', onMatchingSourceChange), + this.createListener(dsAll, 'reset', onChangeAndRewatch), + ); + + if (!ds) return dataListeners; + + if (drKey === undefined) { + dataListeners.push(this.createListener(ds.records, 'add remove reset change', onChangeAndRewatch)); + return dataListeners; } if (dr) { - dataListeners.push(this.createListener(dr, 'change')); + const onRecordChange = (record: DataRecord) => this.onRecordChange(record, propPathParts); + dataListeners.push(this.createListener(dr, 'change', onRecordChange)); } + if (isIndexPath) { + dataListeners.push(this.createListener(ds.records, 'add remove reset', onChangeAndRewatch)); + return dataListeners; + } + + const onMatchingRecordChange = (record: DataRecord) => `${record.id}` === `${drKey}` && onChangeAndRewatch(); dataListeners.push( - this.createListener(em.DataSources.all, 'add remove reset', onChangeAndRewatch), - this.createListener(em, `${DataSourcesEvents.path}:${normPath}`), - this.createListener(em, DataSourcesEvents.path, ({ path: eventPath }: { path: string }) => { - if ( - // Skip same path as it's already handled be the listener above - eventPath !== path && - eventPath.startsWith(path) - ) { - this.onChange(); - } - }), + this.createListener(ds.records, 'add remove', onMatchingRecordChange), + this.createListener(ds.records, 'reset', onChangeAndRewatch), ); return dataListeners; } + private onRecordChange(record: DataRecord, propPath: string[]) { + const changed = Object.keys(record.changedAttributes() || {}); + const [rootProp] = propPath; + + if (!changed.length || changed.includes('id') || !propPath.length || (rootProp && changed.includes(rootProp))) { + this.onChange(); + } + } + private removeListeners() { this.listeners.forEach((ls) => this.model.stopListening(ls.obj, ls.event, ls.callback)); this.listeners = [];