mirror of
https://github.com/The-Low-Code-Foundation/OpenNoodl.git
synced 2026-01-12 15:22:55 +01:00
Co-Authored-By: Eric Tuvesson <eric.tuvesson@gmail.com> Co-Authored-By: mikaeltellhed <2311083+mikaeltellhed@users.noreply.github.com> Co-Authored-By: kotte <14197736+mrtamagotchi@users.noreply.github.com> Co-Authored-By: Anders Larsson <64838990+anders-topp@users.noreply.github.com> Co-Authored-By: Johan <4934465+joolsus@users.noreply.github.com> Co-Authored-By: Tore Knudsen <18231882+torekndsn@users.noreply.github.com> Co-Authored-By: victoratndl <99176179+victoratndl@users.noreply.github.com>
278 lines
7.7 KiB
JavaScript
278 lines
7.7 KiB
JavaScript
'use strict';
|
|
|
|
const { Node } = require('../../../../noodl-runtime');
|
|
|
|
var Model = require('../../../model');
|
|
|
|
var ModelNodeDefinition = {
|
|
name: 'Model2',
|
|
docs: 'https://docs.noodl.net/nodes/data/object/object-node',
|
|
displayNodeName: 'Object',
|
|
shortDesc:
|
|
'Stores any amount of properties and can be used standalone or together with Collections and For Each nodes.',
|
|
category: 'Data',
|
|
usePortAsLabel: 'modelId',
|
|
color: 'data',
|
|
dynamicports: [
|
|
{
|
|
name: 'conditionalports/extended',
|
|
condition: 'idSource = explicit OR idSource NOT SET',
|
|
inputs: ['modelId']
|
|
}
|
|
],
|
|
initialize: function () {
|
|
var internal = this._internal;
|
|
internal.inputValues = {};
|
|
internal.dirtyValues = {};
|
|
|
|
var _this = this;
|
|
this._internal.onModelChangedCallback = function (args) {
|
|
if (_this.isInputConnected('fetch') === true) return;
|
|
|
|
if (_this.hasOutput('prop-' + args.name)) _this.flagOutputDirty('prop-' + args.name);
|
|
|
|
if (_this.hasOutput('changed-' + args.name)) _this.sendSignalOnOutput('changed-' + args.name);
|
|
|
|
_this.sendSignalOnOutput('changed');
|
|
};
|
|
},
|
|
getInspectInfo() {
|
|
const model = this._internal.model;
|
|
if (!model) return '[No Object]';
|
|
|
|
return [
|
|
{ type: 'text', value: 'Id: ' + model.getId() },
|
|
{ type: 'value', value: model.data }
|
|
];
|
|
},
|
|
outputs: {
|
|
id: {
|
|
type: 'string',
|
|
displayName: 'Id',
|
|
group: 'General',
|
|
getter: function () {
|
|
return this._internal.model ? this._internal.model.getId() : this._internal.modelId;
|
|
}
|
|
},
|
|
changed: {
|
|
type: 'signal',
|
|
displayName: 'Changed',
|
|
group: 'Events'
|
|
},
|
|
fetched: {
|
|
type: 'signal',
|
|
displayName: 'Fetched',
|
|
group: 'Events'
|
|
}
|
|
},
|
|
inputs: {
|
|
idSource: {
|
|
type: {
|
|
name: 'enum',
|
|
enums: [
|
|
{ label: 'Specify explicitly', value: 'explicit' },
|
|
{ label: 'From repeater', value: 'foreach' }
|
|
],
|
|
allowEditOnly: true
|
|
},
|
|
default: 'explicit',
|
|
displayName: 'Get Id from',
|
|
group: 'General',
|
|
set: function (value) {
|
|
if (value === 'foreach') {
|
|
this.scheduleAfterInputsHaveUpdated(() => {
|
|
// Find closest nodescope that have a _forEachModel
|
|
var component = this.nodeScope.componentOwner;
|
|
while (component !== undefined && component._forEachModel === undefined && component.parentNodeScope) {
|
|
component = component.parentNodeScope.componentOwner;
|
|
}
|
|
this.setModel(component !== undefined ? component._forEachModel : undefined);
|
|
});
|
|
}
|
|
}
|
|
},
|
|
modelId: {
|
|
type: {
|
|
name: 'string',
|
|
identifierOf: 'ModelName',
|
|
identifierDisplayName: 'Object Ids'
|
|
},
|
|
displayName: 'Id',
|
|
group: 'General',
|
|
set: function (value) {
|
|
if (value instanceof Model) value = value.getId();
|
|
// Can be passed as model as well
|
|
else if (typeof value === 'object') value = Model.create(value).getId(); // If this is an js object, dereference it
|
|
|
|
this._internal.modelId = value; // Wait to fetch data
|
|
if (this.isInputConnected('fetch') === false) this.setModelID(value);
|
|
else {
|
|
this.flagOutputDirty('id');
|
|
}
|
|
}
|
|
},
|
|
properties: {
|
|
type: { name: 'stringlist', allowEditOnly: true },
|
|
displayName: 'Properties',
|
|
group: 'Properties',
|
|
set: function (value) {}
|
|
},
|
|
fetch: {
|
|
displayName: 'Fetch',
|
|
group: 'Actions',
|
|
valueChangedToTrue: function () {
|
|
this.scheduleSetModel();
|
|
}
|
|
}
|
|
},
|
|
prototypeExtensions: {
|
|
scheduleStore: function () {
|
|
if (this.hasScheduledStore) return;
|
|
this.hasScheduledStore = true;
|
|
|
|
var internal = this._internal;
|
|
this.scheduleAfterInputsHaveUpdated(() => {
|
|
this.hasScheduledStore = false;
|
|
if (!internal.model) return;
|
|
|
|
for (var i in internal.dirtyValues) {
|
|
internal.model.set(i, internal.inputValues[i], { resolve: true });
|
|
}
|
|
internal.dirtyValues = {}; // Reset dirty values
|
|
});
|
|
},
|
|
scheduleSetModel: function () {
|
|
if (this.hasScheduledSetModel) return;
|
|
this.hasScheduledSetModel = true;
|
|
|
|
var internal = this._internal;
|
|
this.scheduleAfterInputsHaveUpdated(() => {
|
|
this.hasScheduledSetModel = false;
|
|
this.setModelID(this._internal.modelId);
|
|
});
|
|
},
|
|
setModelID: function (id) {
|
|
var model = (this.nodeScope.modelScope || Model).get(id);
|
|
this.setModel(model);
|
|
this.sendSignalOnOutput('fetched');
|
|
},
|
|
setModel: function (model) {
|
|
if (this._internal.model)
|
|
// Remove old listener if existing
|
|
this._internal.model.off('change', this._internal.onModelChangedCallback);
|
|
|
|
this._internal.model = model;
|
|
this.flagOutputDirty('id');
|
|
|
|
// In set idSource, we are calling setModel with undefined
|
|
if (model) {
|
|
model.on('change', this._internal.onModelChangedCallback);
|
|
|
|
// We have a new model, mark all outputs as dirty
|
|
for (var key in model.data) {
|
|
if (this.hasOutput('prop-' + key)) this.flagOutputDirty('prop-' + key);
|
|
}
|
|
}
|
|
},
|
|
_onNodeDeleted: function () {
|
|
Node.prototype._onNodeDeleted.call(this);
|
|
if (this._internal.model) this._internal.model.off('change', this._internal.onModelChangedCallback);
|
|
},
|
|
registerOutputIfNeeded: function (name) {
|
|
if (this.hasOutput(name)) {
|
|
return;
|
|
}
|
|
|
|
if (name.startsWith('prop-'))
|
|
this.registerOutput(name, {
|
|
getter: userOutputGetter.bind(this, name.substring('prop-'.length))
|
|
});
|
|
},
|
|
registerInputIfNeeded: function (name) {
|
|
var _this = this;
|
|
|
|
if (this.hasInput(name)) {
|
|
return;
|
|
}
|
|
|
|
if (name.startsWith('prop-'))
|
|
this.registerInput(name, {
|
|
set: userInputSetter.bind(this, name.substring('prop-'.length))
|
|
});
|
|
}
|
|
}
|
|
};
|
|
|
|
function userOutputGetter(name) {
|
|
/* jshint validthis:true */
|
|
return this._internal.model ? this._internal.model.get(name, { resolve: true }) : undefined;
|
|
}
|
|
|
|
function userInputSetter(name, value) {
|
|
/* jshint validthis:true */
|
|
this._internal.inputValues[name] = value;
|
|
|
|
// Store on change if no connection to store or new
|
|
const model = this._internal.model;
|
|
const valueChanged = model ? model.get(name) !== value : true;
|
|
if (valueChanged) {
|
|
this._internal.dirtyValues[name] = true;
|
|
this.scheduleStore();
|
|
}
|
|
}
|
|
|
|
function updatePorts(nodeId, parameters, editorConnection) {
|
|
var ports = [];
|
|
|
|
// Add value outputs
|
|
var properties = parameters.properties;
|
|
if (properties) {
|
|
properties = properties ? properties.split(',') : undefined;
|
|
for (var i in properties) {
|
|
var p = properties[i];
|
|
|
|
ports.push({
|
|
type: {
|
|
name: '*',
|
|
allowConnectionsOnly: true
|
|
},
|
|
plug: 'input/output',
|
|
group: 'Properties',
|
|
name: 'prop-' + p,
|
|
displayName: p
|
|
});
|
|
|
|
ports.push({
|
|
type: 'signal',
|
|
plug: 'output',
|
|
group: 'Changed Events',
|
|
displayName: p + ' Changed',
|
|
name: 'changed-' + p
|
|
});
|
|
}
|
|
}
|
|
|
|
editorConnection.sendDynamicPorts(nodeId, ports, {
|
|
detectRenamed: {
|
|
plug: 'input/output'
|
|
}
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
node: ModelNodeDefinition,
|
|
setup: function (context, graphModel) {
|
|
if (!context.editorConnection || !context.editorConnection.isRunningLocally()) {
|
|
return;
|
|
}
|
|
|
|
graphModel.on('nodeAdded.Model2', function (node) {
|
|
updatePorts(node.id, node.parameters, context.editorConnection);
|
|
|
|
node.on('parameterUpdated', function (event) {
|
|
updatePorts(node.id, node.parameters, context.editorConnection);
|
|
});
|
|
});
|
|
}
|
|
};
|