/**
* @copyright 2020 Tridium, Inc. All Rights Reserved.
* @author Logan Byam
*/
/**
* API Status: **Development**
* @module nmodule/bajaui/rc/baja/binding/Binding
*/
define([
'baja!',
'Promise',
'nmodule/bajaui/rc/binding/impl/widgetEvents',
'log!nmodule.bajaui.rc.baja.binding.Binding' ], function (
baja,
Promise,
widgetEvents,
log) {
'use strict';
const logWarning = log.warning.bind(log);
/**
* BajaScript representation of a `bajaui:Binding`. An instance of this class
* knows how to propagate data values from an `OrdTarget` to a `Widget`.
*
* @abstract
* @class
* @alias module:nmodule/bajaui/rc/baja/binding/Binding
* @extends baja.Component
* @implements module:nmodule/bajaui/rc/binding/IValueProvider
* @implements module:nmodule/bajaui/rc/binding/IWidgetEventListener
*/
return class Binding extends baja.Component {
/**
* @returns {module:baja/ord/OrdTarget} the OrdTarget this Binding is bound to
*/
getOrdTarget() {
return this.$ordTarget;
}
/**
* @param {module:baja/ord/OrdTarget} ordTarget the OrdTarget this Binding is bound to
*/
setOrdTarget(ordTarget) {
this.$ordTarget = ordTarget;
}
/**
* Given a certain property name, typically a Widget property, provide the
* corresponding value for that name. This is roughly analogous to
* `BBinding#getOnWidget`.
*
* @param {string} name the name of the property to retrieve a value for
* @param {object} cx user context
* @returns {*|null|Promise.<*|null>} by default, returns `null` which
* indicates there is no value to be provided by this name. Override in
* subclasses.
*/
provide(name, cx) {
return null;
}
/**
* Every Binding belongs to a BindingList which contains all the other
* Bindings that are also bound to its target.
*
* @returns {module:nmodule/bajaui/rc/model/BindingList}
*/
getBindingList() {
return this.$bindingList;
}
/**
* This is a callback that will be run whenever the Binding is updated to
* point to a new OrdTarget. This may happen when the binding's ORD changes,
* or when it is re-resolved.
*
* @returns {Promise}
*/
targetChanged() {
return null;
}
/**
* @returns {module:bajaux/Widget} the Widget this Binding is bound to
*/
getWidget() {
return this.$widget;
}
/**
* @param {module:bajaux/Widget} widget the Widget this Binding is bound to
*/
setWidget(widget) {
this.$widget = widget;
}
/**
* When the Binding is bound to a Widget, it can start listening for events
* from that Widget. Override this method as needed.
*
* @param {module:bajaux/Widget} widget
* @see module:nmodule/bajaui/rc/binding/impl/widgetEvents
*/
addListeners(widget) {}
/**
* Registers the provided events on the widget and keeps track of these
* events which are disarmed with the default implementation of
* Binding#removeListeners.
*
* The added listeners will fire in the order they were added to the widget.
* The handler for a listener may optionally return false which will cause
* the later event handlers to no longer fire. The provided handler for an
* event also executes in order, asynchronously via promises. This means
* it will wait for the async operation to finish prior to moving on to the
* next event of the same type. Valid event types include both JQuery and
* bajaux events.
*
* @example
* binding.addWidgetEvents(widget, {
* click: () => {
* // do something when click happens
* }
* });
*
* @example
* binding.addWidgetEvents(widget, {
* click: () => {
* // This promise will resolve prior to executing the second click event
* return this.fetchDataAsync()
* .then(() => {
* // do something with the fetched data and continue to next click
* });
* }
* });
*
* @example
* binding.addWidgetEvents(widget, {
* loaded: () => {
* return false; // prevent firing events that were armed later.
* }
* });
*
* @param {module:bajaux/Widget} widget
* @param {object} events
*/
addWidgetEvents(widget, events) {
this.$widgetEventDisarms = this.$widgetEventDisarms || [];
this.$widgetEventDisarms.push(widgetEvents(widget, events).disarm);
}
/**
* Any event handlers that would not automatically be cleaned up by
* `widget.destroy()` can be explicitly cleaned up here.
*
* By default, this will cleanup the events that were added via
* addWidgetEvents
*
* @param {module:bajaux/Widget} widget
*/
removeListeners(widget) {
if (this.$widgetEventDisarms) {
this.$widgetEventDisarms.forEach((disarm) => disarm());
this.$widgetEventDisarms = [];
}
}
/**
* Hide or disable the widget based on whether this binding is currently
* degraded.
* @returns {Promise}
*/
applyDegradeBehavior() {
const widget = this.$widget;
const degraded = this.isDegraded();
switch (this.get('degradeBehavior').getTag()) {
case 'disable':
return widget.setEnabled(!degraded);
case 'hide':
widget.properties().add('visible', !degraded);
return Promise.resolve();
default:
return Promise.resolve();
}
}
/**
* @returns {boolean} if the binding is successfully bound to an
* `OrdTarget`.
*/
isBound() {
return !!this.$ordTarget;
}
/**
* @returns {boolean} if the binding is unusable for any reason. The default
* implementation returns `!this.isBound()`.
*/
isDegraded() {
return !this.isBound();
}
/**
* Callback that will be called when the Px page containing this Binding is
* saved. Each Binding will have a chance to save any user-entered data (it
* is the Binding's responsibility to check if the user has made any changes
* or not).
*
* @returns {*|Promise} to be resolved when the binding has saved any
* user-entered data
*/
save() {}
/**
* Fires a event to signal to the framework that the binding is now bound to
* a different target ORD. The framework should resolve the Binding's new
* ORD, set a brand-new OrdTarget, and refresh its bound Widget so the UI is
* brought up to date.
*
* Should not be overridden without calling `super()`.
*/
requestRebind() {
this.fireHandlers('rebind', logWarning, this);
}
/**
* Fires an event to signal that the Binding has new values to provide. The
* framework should propagate all the Binding's properties to its bound
* Widget so the UI is brought up to date.
*
* Should not be overridden without calling `super()`.
*/
requestRefresh() {
this.fireHandlers('refresh', logWarning, this);
}
};
});