/**
* @copyright 2016 Tridium, Inc. All Rights Reserved.
*/
/**
* @module nmodule/webEditors/rc/wb/mgr/model/columns/PropertyPathMgrColumn
*/
define([ 'underscore',
'Promise',
'nmodule/webEditors/rc/wb/table/model/Column',
'nmodule/webEditors/rc/wb/mgr/model/MgrColumn',
'nmodule/webEditors/rc/fe/baja/util/facetsUtils',
'nmodule/webEditors/rc/fe/baja/util/typeUtils' ], function (
_,
Promise,
Column,
MgrColumn,
facetsUtils,
typeUtils) {
'use strict';
const { last, initial, reduce, toArray, extend } = _;
const { toProperties } = facetsUtils;
const { getSlotDisplayName, getSlotFacets, getSlotType, isComplex } = typeUtils;
/**
* Create a slash delimited path string from the array of path elements.
*/
function toPathString(path) {
return '/' + path.join('/');
}
/**
* API Status: **Development**
*
* Column for a property several levels deep relative to the row's subject component.
* The constructor is passed a '/' delimited string specifying a property path.
*
* @class
* @alias module:nmodule/webEditors/rc/wb/mgr/model/columns/PropertyPathMgrColumn
* @mixes module:nmodule/webEditors/rc/wb/mgr/model/MgrColumn
* @extends module:nmodule/webEditors/rc/wb/table/model/Column
* @param {String} name Optional A name for this column (if not provided, will be calculated)
* @param {String} path A slot path, specified as a '/' delimited string
* @param {Object} [params]
* @param {Type} [params.type] The type declaring the target slot, used to
* obtain the slot facets.
* @param {function} [params.getDefaultValue] since Niagara 4.14, if the Complex containing the last
* property in the path does not actually contain that property, this function will be used to
* retrieve a default value. When committing, that default value will be newly added. It will
* receive the row's `Complex` instance to calculate a default value, if desired.
*/
var PropertyPathMgrColumn = function PropertyPathMgrColumn(name, path, params) {
var args;
// The 1st parameter is optional, so we need to decide whether we're using the 2 or 3 parameter constructor.
// 3rd parameter present, therefore should be the params object, so all parameters are as supplied.
// otherwise, 3rd parameter not present
if (!params) {
// If the 2nd parameter isn't present either,
// or the 2nd param is an Object (ie the params object),
// that means we're using the the 2 parameter constructor (ie path, params as opposed to name, path, params)
// so treat params as path, and path as name
if (!path || typeof path === 'object') {
params = path;
path = name;
name = null;
}
}
this.$path = path.split('/');
if (params) {
const { getDefaultValue, type } = params;
if (type) { this.$type = type; }
if (getDefaultValue) { this.$getDefaultValue = getDefaultValue; }
}
// use the 'name' parameter if supplied, otherwise extract the final slot's
// name from the path, so it can be used for the 'name' parameter we pass
// to the super constructor, along with the other arguments we received.
// We also need to adjust the arguments array, and make sure the
// params object is passed as the second argument.
args = toArray(arguments);
args.splice(0, name ? 2 : 1, name || this.getPropertyName());
Column.apply(this, args);
};
PropertyPathMgrColumn.prototype = Object.create(Column.prototype);
PropertyPathMgrColumn.prototype.constructor = PropertyPathMgrColumn;
/**
* Get the value of this column for the given row. This will follow the slot
* path to obtain the value of the slot descended from the row's subject.
*
* @param row
* @returns {*}
*/
PropertyPathMgrColumn.prototype.getValueFor = function (row) {
if (!isComplex(row.getSubject())) {
throw new Error('Complex required');
}
const container = this.getComplexFromPath(row);
const prop = this.getPropertyName();
if (container.has(prop)) {
return container.get(prop);
} else {
const getDefaultValue = this.$getDefaultValue;
if (getDefaultValue) {
return getDefaultValue(container);
}
}
throw new Error('Could not get slot value for path: ' + toPathString(this.$path));
};
MgrColumn.mixin(PropertyPathMgrColumn);
/**
* @private
* @param {module:nmodule/webEditors/rc/wb/table/model/Row} row
* @returns {*}
*/
PropertyPathMgrColumn.prototype.$getContext = function (row) {
const cx = this.data('context');
if (cx === null) {
return undefined;
}
const container = this.getComplexFromPath(row);
const propFacets = container.getFacets(this.getPropertyName());
const facets = propFacets ? propFacets.toObject() : {};
return extend(facets, cx);
};
/**
* If editing only one row, then the facets used to configure the field
* editor will be taken from the component instance containing the final
* property in the property path.
*
* If editing multiple rows, the facets will be derived from the specified
* Type and the frozen slot at the end of the property path. If these are
* not present, no facets will be used.
*
* @param {Array.<module:nmodule/webEditors/rc/wb/table/model/Row>} rows
* @returns {Object} config object for `fe.makeFor`, which might include facets
* if the path's last element had a type declared.
*/
PropertyPathMgrColumn.prototype.getConfigFor = function (rows) {
var value = this.coalesceRows(rows),
name = this.getPropertyName(),
type = this.$type,
facets;
//c.f. javax.baja.workbench.mgr.MgrColumn.PropPath#toEditor
if (rows.length === 1) {
var comp = this.getComplexFromPath(rows[0]);
facets = comp && comp.getFacets(name);
} else {
try {
facets = type && name && getSlotFacets(type, name);
} catch (ignore) {
//dynamic slot so could not retrieve slot facets
}
}
return { value: value, properties: toProperties(facets), formFactor: 'mini' };
};
/**
* Sets/adds the property on the slot resolved from the path. This requires the previous slots
* in the path to exist.
*
* @param {baja.Value} value
* @param {module:nmodule/webEditors/rc/wb/table/model/Row} row
* @param {Object} [params]
* @param {baja.comm.Batch} [params.batch]
* @returns {Promise} promise to be resolved when the value has
* been set on the slot resolved from the path.
*/
PropertyPathMgrColumn.prototype.commit = function (value, row, params) {
var name = this.getPropertyName(),
comp = this.getComplexFromPath(row),
batch = params && params.batch,
progressCallback = params && params.progressCallback,
promise;
promise = comp && comp[comp.has(name) ? 'set' : 'add']({
slot: name,
value: value,
batch: batch
});
if (progressCallback) { progressCallback(MgrColumn.COMMIT_READY); }
return promise;
};
/**
* Resolves the display name of the property slot at the end of the path.
*
* @returns {Promise} promise to be resolved with the display name for the final slot.
*/
PropertyPathMgrColumn.prototype.toDisplayName = function () {
var name = this.getPropertyName();
if (this.$displayName) {
return Promise.resolve(this.$displayName);
} else {
return Promise.resolve(this.$type ? getSlotDisplayName(this.$type, name)
: String(name));
}
};
/**
* Get the icon URI for the type of slot in the last component of the path.
* Returns null if the URI could not be obtained because the slot's declaring
* type was not specified in the path.
*
* @returns {String} The URI for the icon for the target slot.
*/
PropertyPathMgrColumn.prototype.getColumnIcon = function () {
var prop = this.getPropertyName();
if (this.$type) {
try {
var slotType = getSlotType(this.$type, prop);
return slotType ? slotType.getIcon().getImageUris()[0] : null;
} catch (ignore) {}
}
return null;
};
/**
* Get the property name for this column.
* @since Niagara 4.8
*
* @returns String
*/
PropertyPathMgrColumn.prototype.getPropertyName = function () {
return last(this.$path);
};
/**
* Get the complex this property is on.
* @since Niagara 4.8
*
* @param {module:nmodule/webEditors/rc/wb/table/model/Row} row
* @returns {baja.Complex}
*/
PropertyPathMgrColumn.prototype.getComplexFromPath = function (row) {
return PropertyPathMgrColumn.getPropValueFromPath(row.getSubject(),
initial(this.$path)); // use everything but the last entry of the $path array
};
/**
* Take a subject component (e.g. a control point) and an array of slot
* names representing a slot path (e.g leading to a property on its point ext),
* and resolve the descendants of the subject using the property names.
*
* @param {baja.Complex} complex
* @param {Array.<baja.Slot|string>} path
* @returns {baja.Value}
* @since Niagara 4.13
*/
PropertyPathMgrColumn.getPropValueFromPath = function (complex, path) {
return reduce(path, function (comp, name) {
return isComplex(comp) ? comp.get(name) : null;
}, complex);
};
return (PropertyPathMgrColumn);
});