mirror of https://github.com/artf/grapesjs.git
nocodeframeworkdrag-and-dropsite-buildersite-generatortemplate-builderui-builderweb-builderweb-builder-frameworkwebsite-builderno-codepage-builder
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
217 lines
6.6 KiB
217 lines
6.6 KiB
/**
|
|
* This module manages data sources within the editor.
|
|
* Once the editor is instantiated, you can use the following API to manage data sources:
|
|
*
|
|
* ```js
|
|
* const editor = grapesjs.init({ ... });
|
|
* const dsm = editor.DataSources;
|
|
* ```
|
|
*
|
|
* {REPLACE_EVENTS}
|
|
*
|
|
* ## Methods
|
|
* * [add](#add) - Add a new data source.
|
|
* * [get](#get) - Retrieve a data source by its ID.
|
|
* * [getAll](#getall) - Retrieve all data sources.
|
|
* * [remove](#remove) - Remove a data source by its ID.
|
|
* * [clear](#clear) - Remove all data sources.
|
|
*
|
|
* [DataSource]: datasource.html
|
|
*
|
|
* @module DataSources
|
|
*/
|
|
|
|
import { isEmpty } from 'underscore';
|
|
import { ItemManagerModule, ModuleConfig } from '../abstract/Module';
|
|
import { AddOptions, collectionEvents, ObjectAny, RemoveOptions } from '../common';
|
|
import EditorModel from '../editor/model/Editor';
|
|
import { get, set, stringToPath } from '../utils/mixins';
|
|
import defConfig, { DataSourcesConfig } from './config/config';
|
|
import DataRecord from './model/DataRecord';
|
|
import DataSource from './model/DataSource';
|
|
import DataSources from './model/DataSources';
|
|
import { DataSourcesEvents, DataSourceProps, DataRecordProps } from './types';
|
|
import { Events } from 'backbone';
|
|
|
|
export default class DataSourceManager extends ItemManagerModule<DataSourcesConfig & ModuleConfig, DataSources> {
|
|
storageKey = 'dataSources';
|
|
events = DataSourcesEvents;
|
|
destroy(): void {}
|
|
|
|
constructor(em: EditorModel) {
|
|
super(em, 'DataSources', new DataSources([], em), DataSourcesEvents, defConfig());
|
|
Object.assign(this, Events); // Mixin Backbone.Events
|
|
}
|
|
|
|
/**
|
|
* Add new data source.
|
|
* @param {Object} props Data source properties.
|
|
* @returns {[DataSource]} Added data source.
|
|
* @example
|
|
* const ds = dsm.add({
|
|
* id: 'my_data_source_id',
|
|
* records: [
|
|
* { id: 'id1', name: 'value1' },
|
|
* { id: 'id2', name: 'value2' }
|
|
* ]
|
|
* });
|
|
*/
|
|
add<DRProps extends DataRecordProps>(props: DataSourceProps<DRProps>, opts: AddOptions = {}): DataSource<DRProps> {
|
|
const { all } = this;
|
|
props.id = props.id || this._createId();
|
|
|
|
return all.add(props, opts) as DataSource<DRProps>;
|
|
}
|
|
|
|
/**
|
|
* Get data source.
|
|
* @param {String} id Data source id.
|
|
* @returns {[DataSource]} Data source.
|
|
* @example
|
|
* const ds = dsm.get('my_data_source_id');
|
|
*/
|
|
get(id: string) {
|
|
return this.all.get(id);
|
|
}
|
|
|
|
/**
|
|
* Get value from data sources by path.
|
|
* @param {String} path Path to value.
|
|
* @param {any} defValue Default value if the path is not found.
|
|
* @returns {any}
|
|
* const value = dsm.getValue('ds_id.record_id.propName', 'defaultValue');
|
|
*/
|
|
getValue(path: string | string[], defValue?: any) {
|
|
return get(this.getContext(), path, defValue);
|
|
}
|
|
|
|
/**
|
|
* Set value in data sources by path.
|
|
* @param {String} path Path to value in format 'dataSourceId.recordId.propName'
|
|
* @param {any} value Value to set
|
|
* @returns {Boolean} Returns true if the value was set successfully
|
|
* @example
|
|
* dsm.setValue('ds_id.record_id.propName', 'new value');
|
|
*/
|
|
setValue(path: string, value: any) {
|
|
const [ds, record, propPath] = this.fromPath(path);
|
|
|
|
if (record && (propPath || propPath === '')) {
|
|
let attrs = { ...record.attributes };
|
|
if (set(attrs, propPath || '', value)) {
|
|
record.set(attrs);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private getContext() {
|
|
return this.all.reduce((acc, ds) => {
|
|
acc[ds.id] = ds.records.reduce((accR, dr, i) => {
|
|
const dataRecord = dr;
|
|
|
|
const attributes = { ...dataRecord.attributes };
|
|
delete attributes.__p;
|
|
accR[dataRecord.id || i] = attributes;
|
|
|
|
return accR;
|
|
}, {} as ObjectAny);
|
|
return acc;
|
|
}, {} as ObjectAny);
|
|
}
|
|
|
|
/**
|
|
* Remove data source.
|
|
* @param {String|[DataSource]} id Id of the data source.
|
|
* @returns {[DataSource]} Removed data source.
|
|
* @example
|
|
* const removed = dsm.remove('DS_ID');
|
|
*/
|
|
remove(id: string | DataSource, opts?: RemoveOptions) {
|
|
return this.__remove(id, opts);
|
|
}
|
|
|
|
/**
|
|
* Retrieve a data source, data record, and optional property path based on a string path.
|
|
* This method parses a string path to identify and retrieve the corresponding data source
|
|
* and data record. If a property path is included in the input, it will also be returned.
|
|
* The method is useful for accessing nested data within data sources.
|
|
*
|
|
* @param {String} path - The string path in the format 'dataSourceId.recordId.property'.
|
|
* @returns {[DataSource?, DataRecord?, String?]} - An array containing the data source,
|
|
* data record, and optional property path.
|
|
* @example
|
|
* const [dataSource, dataRecord, propPath] = dsm.fromPath('my_data_source_id.record_id.myProp');
|
|
* // e.g., [DataSource, DataRecord, 'myProp']
|
|
*/
|
|
fromPath(path: string) {
|
|
const result: [DataSource?, DataRecord?, string?] = [];
|
|
const [dsId, drId, ...resPath] = stringToPath(path || '');
|
|
const dataSource = this.get(dsId);
|
|
const dataRecord = dataSource?.records.get(drId);
|
|
dataSource && result.push(dataSource);
|
|
|
|
if (dataRecord) {
|
|
result.push(dataRecord);
|
|
resPath.length && result.push(resPath.join('.'));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Store data sources to a JSON object.
|
|
* @returns {Array} Stored data sources.
|
|
*/
|
|
store() {
|
|
const data: DataSourceProps[] = [];
|
|
this.all.forEach((dataSource) => {
|
|
const { skipFromStorage, transformers, records, schema, ...rest } = dataSource.attributes;
|
|
|
|
if (!skipFromStorage) {
|
|
data.push({
|
|
...rest,
|
|
id: rest.id!,
|
|
schema: !isEmpty(schema) ? schema : undefined,
|
|
records: !rest.provider ? records : undefined,
|
|
});
|
|
}
|
|
});
|
|
|
|
return { [this.storageKey]: data };
|
|
}
|
|
|
|
/**
|
|
* Load data sources from a JSON object.
|
|
* @param {Object} data The data object containing data sources.
|
|
* @returns {Object} Loaded data sources.
|
|
*/
|
|
load(data: any) {
|
|
const { config, all, events, em } = this;
|
|
const result = this.loadProjectData(data);
|
|
|
|
if (config.autoloadProviders) {
|
|
const dsWithProviders = all.filter((ds) => ds.hasProvider);
|
|
|
|
if (!!dsWithProviders.length) {
|
|
const loadProviders = async () => {
|
|
em.trigger(events.providerLoadAllBefore);
|
|
const providersToLoad = dsWithProviders.map((ds) => ds.loadProvider());
|
|
await Promise.all(providersToLoad);
|
|
em.trigger(events.providerLoadAll);
|
|
};
|
|
loadProviders();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
postLoad() {
|
|
const { em, all } = this;
|
|
em.listenTo(all, collectionEvents, (m, c, o) => em.changesUp(o || c));
|
|
this.em.UndoManager.add(all);
|
|
}
|
|
}
|
|
|