@openui5/sap.ui.core
Version:
OpenUI5 Core Library sap.ui.core
870 lines (796 loc) • 28.8 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2009-2023 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
/*eslint-disable max-len */
// Provides an abstract property binding.
sap.ui.define([
"./BindingMode",
"./ChangeReason",
"./CompositeDataState",
"./CompositeType",
"./Context",
"./PropertyBinding",
"sap/base/assert",
"sap/base/Log",
"sap/base/util/deepEqual",
"sap/ui/base/DataType",
"sap/ui/base/SyncPromise"
], function(BindingMode, ChangeReason, CompositeDataState, CompositeType, Context, PropertyBinding,
assert, Log, deepEqual, DataType, SyncPromise) {
"use strict";
/**
* Constructor for CompositeBinding.
*
* @class
* Combines multiple property bindings (called 'parts') into a single one.
*
* A <code>CompositeBinding</code> combines the values from all its binding parts (each an
* instance of <code>PropertyBinding</code>), either by calling a formatter function or by
* involving a {@link sap.ui.model.CompositeType composite type}. When a formatter function is
* used, the composite binding is automatically limited to <code>OneWay</code> mode. When a
* type is used, the binding can also operate in <code>TwoWay</code> mode.
*
* Higher layers of the framework derive composite bindings from easy-to-write string
* representations (the following features require complex binding syntax, e.g.
* <code>data-sap-ui-bindingSyntax="complex"</code>):
*
* XML views, for example, convert attribute values with nested curly braces like
* <pre>
* text="{fullname} &lt;{email}&gt;"
* </pre>
* into a composite binding with two parts (one property binding for property "fullname" and one
* for property "email") and with a generic formatter function that injects the values of the
* parts into the string literal "{0} <{1}>" accordingly.
*
* Similarly, {@link topic:daf6852a04b44d118963968a1239d2c0 expression bindings} are parsed and
* converted into composite bindings, too. The formatter function is created by the framework
* and executes the calculations as defined by the expression string, taking the values from the
* binding parts as input.
*
* <b>Note:</b> A nesting of composite bindings is currently not supported (albeit being
* helpful).
*
* @see {@link topic:a2fe8e763014477e87990ff50657a0d0}
* @public
* @alias sap.ui.model.CompositeBinding
* @extends sap.ui.model.PropertyBinding
*/
var CompositeBinding = PropertyBinding.extend("sap.ui.model.CompositeBinding", /** @lends sap.ui.model.CompositeBinding.prototype */ {
constructor : function (aBindings, bRawValues, bInternalValues) {
var oModel;
PropertyBinding.apply(this, [null,""]);
this.aBindings = aBindings;
this.aValues = null;
this.bRawValues = bRawValues;
this.bPreventUpdate = false;
this.bInternalValues = bInternalValues;
this.bMultipleModels = this.aBindings.some(function (oBinding) {
var oCurrentModel = oBinding.getModel();
oModel = oModel || oCurrentModel;
return oModel && oCurrentModel && oCurrentModel !== oModel;
});
},
metadata : {
publicMethods : [
"getBindings", "attachChange", "detachChange"
]
}
});
CompositeBinding.prototype.destroy = function() {
this.aBindings.forEach(function(oBinding) {
oBinding.destroy();
});
PropertyBinding.prototype.destroy.apply(this);
};
CompositeBinding.prototype.getPath = function() {
assert(null, "Composite Binding has no path!");
return null;
};
CompositeBinding.prototype.getModel = function() {
assert(null, "Composite Binding has no model!");
return null;
};
CompositeBinding.prototype.getContext = function() {
assert(null, "Composite Binding has no context!");
return null;
};
CompositeBinding.prototype.isResolved = function() {
return this.aBindings.every(function(oBinding) {
return oBinding.isResolved();
});
};
/**
* Sets the optional type and internal type for the binding. The type and internal type are used
* to do the parsing/formatting correctly. The internal type is the property type of the element
* which the value is formatted to.
*
* @param {sap.ui.model.CompositeType} oType
* The type for the binding
* @param {string} sInternalType
* The internal type of the element property which this binding is bound against.
*
* @public
*/
CompositeBinding.prototype.setType = function(oType, sInternalType) {
var that = this;
if (oType && !(oType instanceof CompositeType)) {
throw new Error("Only CompositeType can be used as type for composite bindings!");
}
PropertyBinding.prototype.setType.apply(this, arguments);
// If a composite type is used, the type decides whether to use raw values or not
if (this.oType) {
oType.getPartsIgnoringMessages().forEach(function (i) {
var oBinding = that.aBindings[i];
if (oBinding && oBinding.supportsIgnoreMessages()
&& oBinding.getIgnoreMessages() === undefined) {
oBinding.setIgnoreMessages(true);
}
});
this.bRawValues = this.oType.getUseRawValues();
this.bInternalValues = this.oType.getUseInternalValues();
this.oType.processPartTypes(this.aBindings.map(function (oBinding) {
return oBinding.getType();
}));
if (this.bRawValues && this.bInternalValues) {
throw new Error(this.oType + " has both 'bUseRawValues' & 'bUseInternalValues' set to true. Only one of them is allowed to be true");
}
}
};
/**
* Sets the context for the property bindings of this composite binding which are either checked
* thruthy via <code>mParameters.fnIsBindingRelevant</code> or whose model is the given
* context's model.
*
* @param {sap.ui.model.Context} oContext
* The new context for the bindings
* @param {Object<string,any>} [mParameters]
* Additional map of binding specific parameters
* @param {function} [mParameters.fnIsBindingRelevant]
* A callback function that checks whether the given context needs to be propagated to a
* property binding of this composite binding. It gets the index of a property binding as
* parameter and returns whether the given context needs to be propagated to that property
* binding.
*/
CompositeBinding.prototype.setContext = function (oContext, mParameters) {
var bCheckUpdate, bForceUpdate,
aBindings = this.aBindings,
oModel = oContext && oContext.getModel(),
fnIsBindingRelevant = mParameters && mParameters.fnIsBindingRelevant
? mParameters.fnIsBindingRelevant
: function (i) {
// check if oModel is given since a context's model may have been destroyed
return !oContext || oModel && oModel === aBindings[i].getModel();
};
aBindings.forEach(function (oBinding, i) {
var oBindingContext;
if (fnIsBindingRelevant(i)) {
oBindingContext = oBinding.getContext();
bCheckUpdate = bCheckUpdate
|| oBinding.isRelative() && Context.hasChanged(oBindingContext, oContext);
bForceUpdate = bForceUpdate || (bCheckUpdate && oBindingContext !== oContext);
oBinding.setContext(oContext);
}
});
if (bCheckUpdate) {
this.checkUpdate(bForceUpdate && this.getDataState().getControlMessages().length);
}
};
/**
* Sets the values. This will cause the setValue to be called for each nested binding, except
* for undefined values in the array.
*
* @param {array} aValues The values to set for this binding
*
* @public
*/
CompositeBinding.prototype.setValue = function(aValues) {
if (this.bSuspended) {
return;
}
this.aBindings.forEach(function(oBinding, i) {
var oValue = aValues[i],
sBindingMode = oBinding.getBindingMode();
if (oValue !== undefined && sBindingMode !== BindingMode.OneWay && sBindingMode !== BindingMode.OneTime) {
oBinding.setValue(oValue);
}
});
this.getDataState().setValue(this.getValue());
};
/**
* Returns the raw values of the property bindings in an array.
*
* @return {object} The values of the internal property bindings in an array
*
* @public
*/
CompositeBinding.prototype.getValue = function() {
return this.aBindings.map(function(oBinding) {
return oBinding.getValue();
});
};
CompositeBinding.prototype.getOriginalValue = function() {
return this.aBindings.map(function(oBinding) {
return oBinding.getDataState().getOriginalValue();
});
};
/**
* Returns the current external value of the bound target which is formatted via a type or
* formatter function.
*
* @return {object} The current value of the bound target
*
* @throws {sap.ui.model.FormatException}
*
* @public
*/
CompositeBinding.prototype.getExternalValue = function() {
var aValues = [],
oInternalType,
oValue;
switch (this.sInternalType) {
case "raw":
return this.getRawValue();
case "internal":
return this.getInternalValue();
default:
oInternalType = this.sInternalType && DataType.getType(this.sInternalType);
aValues = this.getCurrentValues();
if (this.fnFormatter) {
oValue = this.fnFormatter.apply(this, aValues);
} else if (this.oType) {
oValue = this.oType.formatValue(aValues, this.sInternalType);
} else if (oInternalType instanceof DataType && oInternalType.isArrayType()) {
oValue = aValues;
} else if (aValues.length > 1) {
// default: multiple values are joined together as space separated list if no formatter or type specified
oValue = aValues.join(" ");
} else {
oValue = aValues[0];
}
return oValue;
}
};
/**
* Sets the external value of a composite binding. If no CompositeType is assigned to the
* binding, the default implementation assumes a space-separated list of values. This will cause
* the setValue to be called for each nested binding, except for undefined values in the array.
*
* @param {object} oValue The value to set for this binding
* @return {undefined|Promise} A promise in case of asynchronous type parsing or validation
*
* @throws sap.ui.model.ParseException
* @throws sap.ui.model.ValidateException
*
* @public
*/
CompositeBinding.prototype.setExternalValue = function(oValue) {
var oInternalType, oDataState, vResult, pValues,
that = this;
if (this.sInternalType === "raw") {
this.setRawValue(oValue);
return undefined;
} else if (this.sInternalType === "internal") {
this.setInternalValue(oValue);
return undefined;
}
oInternalType = this.sInternalType && DataType.getType(this.sInternalType);
// No twoway binding when using formatters
if (this.fnFormatter) {
Log.warning("Tried to use twoway binding, but a formatter function is used");
return undefined;
}
oDataState = this.getDataState();
if (this.oType) {
pValues = SyncPromise.resolve().then(function() {
var aCurrentValues;
if (that.oType.getParseWithValues()) {
aCurrentValues = that.getCurrentValues();
}
return that.oType.parseValue(oValue, that.sInternalType, aCurrentValues);
}).then(function(aValues) {
var aValidateValues = that.getValidateValues(aValues);
return SyncPromise.all([aValues, that.oType.validateValue(aValidateValues)]);
}).then(function(aResult) {
return aResult[0];
}).catch(function(oException) {
oDataState.setInvalidValue(oValue);
that.checkDataState(); //data ui state is dirty inform the control
throw oException;
});
} else if (Array.isArray(oValue) && oInternalType instanceof DataType && oInternalType.isArrayType()) {
pValues = SyncPromise.resolve(oValue);
} else if (typeof oValue == "string") {
// default: multiple values are split by space character together if no formatter or type specified
pValues = SyncPromise.resolve(oValue.split(" "));
} else {
pValues = SyncPromise.resolve([oValue]);
}
vResult = pValues.then(function(aValues) {
that.aBindings.forEach(function(oBinding, iIndex) {
var sBindingMode = oBinding.getBindingMode();
oValue = aValues[iIndex];
// if a value is undefined skip the update of the nestend binding - this allows partial updates
if (oValue !== undefined && sBindingMode !== BindingMode.OneWay && sBindingMode !== BindingMode.OneTime) {
if (that.bRawValues) {
oBinding.setRawValue(oValue);
} else if (that.bInternalValues) {
oBinding.setInternalValue(oValue);
} else {
oBinding.setExternalValue(oValue);
}
}
});
oDataState.setValue(that.getValue());
oDataState.setInvalidValue(undefined);
});
vResult.catch(function () {/*avoid "Uncaught (in promise)"*/});
return vResult.unwrap();
};
/**
* Returns the current internal value of the bound target which is an array of the internal (JS
* native) values of nested bindings.
*
* @return {array} The current values of the nested bindings
*
* @public
*/
CompositeBinding.prototype.getInternalValue = function() {
return this.aBindings.map(function(oBinding) {
return oBinding.getInternalValue();
});
};
/**
* Sets the internal value of the bound target. Parameter must be an array of values matching
* the internal (JS native) types of nested bindings.
*
* @param {array} aValues the new values of the nested bindings
* @return {undefined|Promise} A promise in case of asynchronous type parsing or validation
*
* @public
*/
CompositeBinding.prototype.setInternalValue = function(aValues) {
var oDataState = this.getDataState(), pValues,
that = this;
if (this.oType) {
pValues = SyncPromise.resolve(aValues).then(function(aValidateValues){
if (!that.bInternalValues) {
aValidateValues = that.aBindings.map(function(oBinding, i) {
return oBinding._internalToRaw(aValidateValues[i]);
});
if (!that.bRawValues) {
aValidateValues = that.aBindings.map(function(oBinding, i) {
return oBinding._rawToExternal(aValidateValues[i]);
});
}
}
return that.oType.validateValue(aValidateValues);
}).then(function() {
return aValues;
}).catch(function(oException) {
oDataState.setInvalidValue(aValues);
that.checkDataState(); //data ui state is dirty inform the control
throw oException;
});
} else {
pValues = SyncPromise.resolve(aValues);
}
return pValues.then(function() {
that.aBindings.forEach(function(oBinding, iIndex) {
var vValue = aValues[iIndex],
sBindingMode = oBinding.getBindingMode();
if (vValue !== undefined && sBindingMode !== BindingMode.OneWay && sBindingMode !== BindingMode.OneTime) {
oBinding.setInternalValue(vValue);
}
});
oDataState.setValue(that.getValue());
oDataState.setInvalidValue(undefined);
}).unwrap();
};
/**
* Returns the current raw value of the bound target which is an array of the raw (model)
* values of nested bindings.
*
* @return {array} The current values of the nested bindings
*
* @public
*/
CompositeBinding.prototype.getRawValue = function() {
return this.aBindings.map(function(oBinding) {
return oBinding.getRawValue();
});
};
/**
* Sets the raw value of the bound target. Parameter must be an array of values matching the raw
* (model) types of nested bindings.
*
* @param {array} aValues the new values of the nested bindings
* @return {undefined|Promise} A promise in case of asynchronous type parsing or validation
*
* @public
*/
CompositeBinding.prototype.setRawValue = function(aValues) {
var oDataState = this.getDataState(), pValues,
that = this;
if (this.oType) {
pValues = SyncPromise.resolve(aValues).then(function(aValidateValues){
if (!that.bRawValues) {
if (that.bInternalValues) {
aValidateValues = that.aBindings.map(function(oBinding, i) {
return oBinding._rawToInternal(aValidateValues[i]);
});
} else {
aValidateValues = that.aBindings.map(function(oBinding, i) {
return oBinding._rawToExternal(aValidateValues[i]);
});
}
}
return that.oType.validateValue(aValidateValues);
}).then(function() {
return aValues;
}).catch(function(oException) {
oDataState.setInvalidValue(aValues);
that.checkDataState(); //data ui state is dirty inform the control
throw oException;
});
} else {
pValues = SyncPromise.resolve(aValues);
}
return pValues.then(function() {
that.aBindings.forEach(function(oBinding, iIndex) {
var vValue = aValues[iIndex],
sBindingMode = oBinding.getBindingMode();
if (vValue !== undefined && sBindingMode !== BindingMode.OneWay && sBindingMode !== BindingMode.OneTime) {
oBinding.setRawValue(vValue);
}
});
oDataState.setValue(that.getValue());
oDataState.setInvalidValue(undefined);
}).unwrap();
};
/**
* Returns an array with the current values as available in the bindings.
* Depending on the raw/internal value flags, this may return raw/internal values.
*
* @return {array} the values of all bindings
*
* @private
*/
CompositeBinding.prototype.getCurrentValues = function() {
if (this.bRawValues) {
return this.getRawValue();
} else if (this.bInternalValues) {
return this.getInternalValue();
} else {
return this.aBindings.map(function(oBinding) {
return oBinding.getExternalValue();
});
}
};
/**
* Returns values to validate. In case the value array does contain undefined values
* they will be filled with actual data of nested bindings. This ensures the validate
* method always gets the full set of values to validate, even if partial updates are
* used.
*
* @param {any[]} aValues Values to fill empty current values with
* @return {array} Array of values used for validation
*
* @private
*/
CompositeBinding.prototype.getValidateValues = function(aValues) {
var aCurrentValues, bPartialUpdate,
aValidateValues = aValues;
bPartialUpdate = this.aBindings.some(function(vPart, i) {
return aValues[i] === undefined;
});
if (bPartialUpdate) {
aCurrentValues = this.getCurrentValues();
aValidateValues = aCurrentValues.map(function(vValue, i) {
return aValues[i] === undefined ? vValue : aValues[i];
});
}
return aValidateValues;
};
/**
* Returns the property bindings contained in this composite binding.
*
* @return {array} The property bindings in this composite binding
*
* @public
*/
CompositeBinding.prototype.getBindings = function() {
return this.aBindings;
};
/**
* {@see sap.ui.model.Binding#hasValidation}
*
* @returns {boolean}
* Whether the binding throws a validation exception when an invalid value is set on it
* @private
*/
CompositeBinding.prototype.hasValidation = function() {
if (this.getType()) {
// If the CompositeBinding has a type of its own, it always validates
return true;
}
// If the one of the inner bindings has a type of its own the CompositeBinding validates
var aBindings = this.getBindings();
for (var i = 0; i < aBindings.length; ++i) {
if (aBindings[i].hasValidation()) {
return true;
}
}
return false;
};
//Eventing and related
/**
* Attaches event handler <code>fnFunction</code> to the <code>change</code> event of this
* <code>sap.ui.model.CompositeBinding</code>.
*
* When called, the context of the event handler (its <code>this</code>) will be bound to
* <code>oListener</code> if specified, otherwise it will be bound to this
* <code>sap.ui.model.CompositeBinding</code> itself.
*
* @param {function} fnFunction The function to be called, when the event occurs
* @param {object} [oListener] Object on which to call the given function
* @protected
*/
CompositeBinding.prototype.attachChange = function(fnFunction, oListener) {
var that = this;
this.fChangeHandler = function(oEvent) {
if (that.bSuspended) {
return;
}
var oBinding = oEvent.getSource();
if (oBinding.getBindingMode() == BindingMode.OneTime) {
oBinding.detachChange(that.fChangeHandler);
}
/*bForceUpdate true gets lost (e.g. checkUpdate(true) on model); But if an embedded binding fires a change we could
* call checkUpdate(true) so we handle both cases: a value change of the binding and a checkUpdate(true)
*/
that.checkUpdate(true);
};
this.attachEvent("change", fnFunction, oListener);
if (this.aBindings) {
this.aBindings.forEach(function(oBinding) {
oBinding.attachChange(that.fChangeHandler);
});
}
};
/**
* Detaches event handler <code>fnFunction</code> from the <code>change</code> event of this
* <code>sap.ui.model.CompositeBinding</code>.
*
* @param {function} fnFunction The function to be called, when the event occurs
* @param {object} [oListener] Object on which to call the given function
* @protected
*/
CompositeBinding.prototype.detachChange = function(fnFunction, oListener) {
var that = this;
this.detachEvent("change", fnFunction, oListener);
if (this.aBindings) {
this.aBindings.forEach(function(oBinding) {
oBinding.detachChange(that.fChangeHandler);
});
}
};
/**
* Attaches event handler <code>fnFunction</code> to the <code>DataStateChange</code> event of
* this <code>sap.ui.model.CompositeBinding</code>.
*
* When called, the context of the event handler (its <code>this</code>) will be bound to
* <code>oListener</code> if specified, otherwise it will be bound to this
* <code>sap.ui.model.CompositeBinding</code> itself.
*
* @param {function} fnFunction The function to be called, when the event occurs
* @param {object} [oListener] Object on which to call the given function
* @protected
*/
CompositeBinding.prototype.attachDataStateChange = function(fnFunction, oListener) {
var that = this;
this.fDataStateChangeHandler = function(oEvent) {
var oBinding = oEvent.getSource();
if (oBinding.getBindingMode() == BindingMode.OneTime) {
oBinding.detachDataStateChange(that.fChangeHandler);
}
that.checkDataState();
};
this.attachEvent("DataStateChange", fnFunction, oListener);
if (this.aBindings) {
this.aBindings.forEach(function(oBinding) {
oBinding.attachEvent("DataStateChange", that.fDataStateChangeHandler);
});
}
};
/**
* Detaches event handler <code>fnFunction</code> from the <code>DataStateChange</code> event of
* this <code>sap.ui.model.CompositeBinding</code>.
*
* @param {function} fnFunction The function to be called, when the event occurs
* @param {object} [oListener] Object on which to call the given function
* @protected
*/
CompositeBinding.prototype.detachDataStateChange = function(fnFunction, oListener) {
var that = this;
this.detachEvent("DataStateChange", fnFunction, oListener);
if (this.aBindings) {
this.aBindings.forEach(function(oBinding) {
oBinding.detachEvent("DataStateChange", that.fDataStateChangeHandler);
});
}
};
/**
* Attaches event handler <code>fnFunction</code> to the <code>AggregatedDataStateChange</code>
* event of this <code>sap.ui.model.CompositeBinding</code>.
*
* The <code>AggregatedDataStateChange</code> event is fired asynchronously, meaning that the
* <code>DataState</code> object given as parameter of the event contains all changes that were
* applied to the <code>DataState</code> in the running thread.
*
* @param {function} fnFunction The function to be called, when the event occurs
* @param {object} [oListener] Object on which to call the given function
* @protected
*/
CompositeBinding.prototype.attachAggregatedDataStateChange = function(fnFunction, oListener) {
var that = this;
if (!this.fDataStateChangeHandler) {
this.fDataStateChangeHandler = function(oEvent) {
var oBinding = oEvent.getSource();
if (oBinding.getBindingMode() == BindingMode.OneTime) {
oBinding.detachDataStateChange(that.fChangeHandler);
}
that.checkDataState();
};
}
this.attachEvent("AggregatedDataStateChange", fnFunction, oListener);
if (this.aBindings) {
this.aBindings.forEach(function(oBinding) {
oBinding.attachEvent("DataStateChange", that.fDataStateChangeHandler);
});
}
};
/**
* Detaches event handler <code>fnFunction</code> from the
* <code>AggregatedDataStateChange</code> event of this
* <code>sap.ui.model.CompositeBinding</code>.
*
* @param {function} fnFunction The function to be called, when the event occurs
* @param {object} [oListener] Object on which to call the given function
* @protected
*/
CompositeBinding.prototype.detachAggregatedDataStateChange = function(fnFunction, oListener) {
var that = this;
this.detachEvent("AggregatedDataStateChange", fnFunction, oListener);
if (this.aBindings) {
this.aBindings.forEach(function(oBinding) {
oBinding.detachEvent("DataStateChange", that.fDataStateChangeHandler);
});
}
};
/**
* Determines if the property bindings in the composite binding should be updated by calling
* updateRequired on all property bindings with the specified model.
*
* @param {object} oModel The model instance to compare against
* @returns {boolean} Whether this binding should be updated
* @protected
*/
CompositeBinding.prototype.updateRequired = function(oModel) {
var bUpdateRequired = false;
this.aBindings.forEach(function(oBinding) {
bUpdateRequired = bUpdateRequired || oBinding.updateRequired(oModel);
});
return bUpdateRequired;
};
/**
* Initialize the binding. The method should be called when creating a binding.
* The default implementation calls checkUpdate(true).
* Prevent checkUpdate to be triggered while initializing nested bindings, it is
* sufficient to call checkUpdate when all nested bindings are initialized.
*
* @returns {this} A reference to itself
*
* @protected
*/
CompositeBinding.prototype.initialize = function() {
this.bPreventUpdate = true;
if (this.aBindings) {
this.aBindings.forEach(function(oBinding) {
oBinding.initialize();
});
}
this.bPreventUpdate = false;
if (!this.bSuspended) {
this.checkUpdate(true);
}
return this;
};
/**
* Returns the data state for this binding.
*
* @return {sap.ui.model.CompositeDataState} The data state
*/
CompositeBinding.prototype.getDataState = function() {
if (!this.oDataState) {
this.oDataState = new CompositeDataState(this.aBindings.map(function(oBinding) {
return oBinding.getDataState();
}));
}
return this.oDataState;
};
/**
* Suspends the binding update. No change events will be fired.
*
* A refresh call with bForceUpdate set to true will also update the binding and fire a change
* in suspended mode. Special operations on bindings, which require updates to work properly
* (as paging or filtering in list bindings) will also update and cause a change event although
* the binding is suspended.
*
* @public
*/
CompositeBinding.prototype.suspend = function() {
this.bSuspended = true;
this.aBindings.forEach(function(oBinding) {
oBinding.suspend();
});
};
/**
* Suspends the binding update. No change events will be fired.
*
* A refresh call with bForceUpdate set to true will also update the binding and fire a change
* in suspended mode. Special operations on bindings, which require updates to work properly
* (as paging or filtering in list bindings) will also update and cause a change event although
* the binding is suspended.
*
* @public
*/
CompositeBinding.prototype.resume = function() {
this.aBindings.forEach(function(oBinding) {
oBinding.resume();
});
this.bSuspended = false;
this.checkUpdate(true);
};
/**
* Check whether this Binding would provide new values and in case it changed,
* inform interested parties about this.
*
* @param {boolean} [bForceUpdate] Whether an update should be forced
*
*/
CompositeBinding.prototype.checkUpdate = function(bForceUpdate){
var bChanged = false;
if (this.bPreventUpdate || (this.bSuspended && !bForceUpdate)) {
return;
}
// do not fire change event in case the destruction of the model for one part leads to the
// update of a model for another part of this binding
if (this.bMultipleModels
&& this.aBindings.some(function (oBinding) {
var oModel = oBinding.getModel();
return oModel && oModel.bDestroyed;
})) {
return;
}
var oDataState = this.getDataState();
var aOriginalValues = this.getOriginalValue();
if (bForceUpdate || !deepEqual(aOriginalValues, this.aOriginalValues)) {
this.aOriginalValues = aOriginalValues;
oDataState.setOriginalValue(aOriginalValues);
bChanged = true;
}
var aValues = this.getValue();
if (!deepEqual(aValues, this.aValues) || bForceUpdate) {// optimize for not firing the events when unneeded
this.aValues = aValues;
oDataState.setValue(aValues);
this._fireChange({reason: ChangeReason.Change});
bChanged = true;
}
if (bChanged) {
this.checkDataState();
}
};
return CompositeBinding;
});