/**
* @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';
var toProperties = facetsUtils.toProperties,
getSlotDisplayName = typeUtils.getSlotDisplayName,
getSlotFacets = typeUtils.getSlotFacets,
getSlotType = typeUtils.getSlotType,
isComplex = typeUtils.isComplex;
/**
* 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.
*/
function getPropValueFromPath(subject, path) {
return _.reduce(path, function (comp, name) {
return isComplex(comp) ? comp.get(name) : null;
}, subject);
}
/**
* 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.
*/
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 && params.type) {
this.$type = params.type;
}
// 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) {
var comp = row.getSubject(),
path = this.$path,
prop;
if (!isComplex(comp)) {
throw new Error('Complex required');
}
prop = getPropValueFromPath(comp, path);
if (prop !== null) { return prop; }
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; }
if (cx) { return cx; }
const container = this.getComplexFromPath(row);
const prop = this.getPropertyName();
return container.has(prop) ? container.getFacets(prop).toObject() : {};
};
/**
* 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) };
};
/**
* 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} rows
* @returns String
*/
PropertyPathMgrColumn.prototype.getComplexFromPath = function (row) {
return getPropValueFromPath(
row.getSubject(),
_.initial(this.$path) // use everything but the last entry of the $path array
);
};
return (PropertyPathMgrColumn);
});