@openui5/sap.ui.core
Version:
OpenUI5 Core Library sap.ui.core
280 lines (258 loc) • 10.7 kB
JavaScript
/*!
* OpenUI5
* (c) Copyright 2009-2021 SAP SE or an SAP affiliate company.
* Licensed under the Apache License, Version 2.0 - see LICENSE.txt.
*/
// Provides control sap.ui.core.mvc.JSONView.
sap.ui.define([
'./View',
'./JSONViewRenderer',
'./EventHandlerResolver',
'sap/base/util/merge',
'sap/ui/base/ManagedObject',
'sap/ui/core/library',
'sap/ui/model/resource/ResourceModel',
'sap/base/Log',
'sap/base/util/LoaderExtensions'
],
function(
View,
JSONViewRenderer,
EventHandlerResolver,
merge,
ManagedObject,
library,
ResourceModel,
Log,
LoaderExtensions
) {
"use strict";
// shortcut for enum(s)
var ViewType = library.mvc.ViewType;
/**
* Constructor for a new mvc/JSONView.
*
* <strong>Note:</strong> Application code shouldn't call the constructor directly, but rather use the factory
* {@link sap.ui.core.mvc.JSONView.create JSONView.create} or {@link sap.ui.core.mvc.View.create View.create}
* with type {@link sap.ui.core.mvc.ViewType.JSON JSON}. The factory simplifies asynchronous loading of a view
* and future features might be added to the factory only.
*
* @param {string} [sId] id for the new control, generated automatically if no id is given
* @param {object} [mSettings] initial settings for the new control
*
* @class
* A View defined using JSON.
* @extends sap.ui.core.mvc.View
* @version 1.87.1
*
* @public
* @alias sap.ui.core.mvc.JSONView
* @ui5-metamodel This control/element also will be described in the UI5 (legacy) designtime metamodel
*/
var JSONView = View.extend("sap.ui.core.mvc.JSONView", /** @lends sap.ui.core.mvc.JSONView.prototype */ {
metadata : {
library : "sap.ui.core"
},
renderer: JSONViewRenderer
});
/**
* Creates a JSON view of the given configuration.
*
* @param {object} oOptions An object containing the view configuration options.
* @param {string} [oOptions.id] Specifies an ID for the view instance. If no ID is given, an ID will be generated.
* @param {string} [oOptions.viewName] The view name (in dot-notation) that corresponds to a JSON resource that can
* be loaded via the module system (viewName + suffix ".view.json").
* @param {string|object} [oOptions.definition] view definition as a JSON string or an object literal
* @param {sap.ui.core.mvc.Controller} [oOptions.controller] Controller instance to be used for this view.
* The given controller instance overrides the controller defined in the view definition. Sharing a controller instance
* between multiple views is not supported.
* @public
* @static
* @returns {Promise<sap.ui.core.mvc.JSONView>} A promise which resolves with the created <code>JSONView</code> instance.
*/
JSONView.create = function(oOptions) {
var mParameters = merge({}, oOptions);
//remove unsupported options:
for (var sOption in mParameters) {
if (sOption === 'preprocessors') {
delete mParameters['preprocessors'];
Log.warning("JSView.create does not support the option preprocessors!");
}
}
mParameters.type = ViewType.JSON;
return View.create(mParameters);
};
/**
* Creates a JSON view of the given name and id.
*
* The <code>viewName</code> must either correspond to a JSON module that can be loaded
* via the module system (viewName + suffix ".view.json") and which defines the view or it must
* be a configuration object for a view.
* The configuration object can have a viewName, viewContent and a controller property. The viewName
* behaves as described above, viewContent can hold the view description as JSON string or as object literal.
*
* <strong>Note</strong>: when an object literal is given, it might be modified during view construction.
*
* The controller property can hold a controller instance. If a controller instance is given,
* it overrides the controller defined in the view.
*
* When property <code>async</code> is set to true, the view definition and the controller class (and its
* dependencies) will be loaded asynchronously. Any controls used in the view might be loaded sync or
* async, depending on the view configuration. Even when the view definition is provided as string or object tree,
* controller or controls might be loaded
* asynchronously. In any case, a view instance will be returned synchronously by this factory API, but its
* content (control tree) might appear only later. Also see {@link sap.ui.core.mvc.View#loaded}.
*
* Like with any other control, an id is optional and will be created when missing.
*
* @param {string} [sId] id of the newly created view
* @param {string | object} vView name of a view resource or view configuration as described above.
* @param {string} [vView.viewName] name of a view resource in module name notation (without suffix)
* @param {string|object} [vView.viewContent] view definition as a JSON string or an object literal
* @param {boolean} [vView.async] defines how the view source is loaded and rendered later on
* @param {sap.ui.core.mvc.Controller} [vView.controller] controller to be used for this view instance
* @public
* @static
* @deprecated since 1.56: Use {@link sap.ui.core.mvc.JSONView.create JSONView.create} instead.
* @return {sap.ui.core.mvc.JSONView} the created JSONView instance
* @ui5-global-only
*/
sap.ui.jsonview = function(sId, vView) {
return sap.ui.view(sId, vView, ViewType.JSON);
};
/**
* The type of the view used for the <code>sap.ui.view</code> factory
* function. This property is used by the parsers to define the specific
* view type.
* @private
*/
JSONView._sType = ViewType.JSON;
/**
* Flag for feature detection of asynchronous loading/rendering
* @public
* @since 1.30
*/
JSONView.asyncSupport = true;
JSONView.prototype.initViewSettings = function(mSettings) {
if (!mSettings) {
throw new Error("mSettings must be given");
}
// View template handling - no JSON template given
if (mSettings.viewName && mSettings.viewContent) {
throw new Error("View name and view content are given. There is no point in doing this, so please decide.");
} else if (!mSettings.viewName && !mSettings.viewContent) {
throw new Error("Neither view name nor view content is given. One of them is required.");
}
var that = this;
var fnInitModel = function() {
if ((that._oJSONView.resourceBundleName || that._oJSONView.resourceBundleUrl) && (!mSettings.models || !mSettings.models[that._oJSONView.resourceBundleAlias])) {
var oModel = new ResourceModel({
bundleName: that._oJSONView.resourceBundleName,
bundleUrl: that._oJSONView.resourceBundleUrl,
async: mSettings.async
});
var vBundle = oModel.getResourceBundle();
// if ResourceBundle was created with async flag vBundle will be a Promise
if (vBundle instanceof Promise) {
return vBundle.then(function() {
that.setModel(oModel, that._oJSONView.resourceBundleAlias);
});
}
that.setModel(oModel, that._oJSONView.resourceBundleAlias);
}
};
if (mSettings.viewName) {
if (mSettings.async) {
return this._loadTemplate(mSettings.viewName, {async: true}).then(fnInitModel);
} else {
this._loadTemplate(mSettings.viewName);
fnInitModel();
}
} else if (mSettings.viewContent) {
// keep the content as a pseudo property to make cloning work but without supporting mutation
// TODO model this as a property as soon as write-once-during-init properties become available
this.mProperties["viewContent"] = mSettings.viewContent;
if (typeof mSettings.viewContent === "string") {
this._oJSONView = JSON.parse(mSettings.viewContent);
if (!this._oJSONView) { // would lead to errors later on
throw new Error("error when parsing viewContent: " + mSettings.viewContent);
}
} else if (typeof mSettings.viewContent === "object") {
this._oJSONView = mSettings.viewContent;
} else {
throw new Error("viewContent must be a JSON string or object, but is a " + (typeof mSettings.viewContent));
}
if (mSettings.async) {
return Promise.resolve().then(fnInitModel);
} else {
fnInitModel();
}
} // else does not happen, already checked
};
JSONView.prototype.onControllerConnected = function(oController) {
var that = this;
// use preprocessors to fix IDs, associations and event handler references
ManagedObject.runWithPreprocessors(function() {
// parse
that.applySettings({ content : that._oJSONView.content}, oController);
},
{
// preprocessors
id : function(sId) {
// prefix only if prefix doesn't exist already. Avoids double prefixes
// for composite components (now done in createId)
return that.createId(sId);
},
// preprocess 'mSettings' for setting the controller as Listener for defined events
// => make sure to store old preprocessor in case of nested views
settings : function(oSettings) {
var oMetadata = this.getMetadata(),
aValidKeys = oMetadata.getJSONKeys(), // UID names required, they're part of the documented contract
sKey, oValue, oKeyInfo;
for (sKey in oSettings) {
// get info object for the key
if ( (oKeyInfo = aValidKeys[sKey]) !== undefined ) {
oValue = oSettings[sKey];
switch (oKeyInfo._iKind) {
case 3: // SINGLE ASSOCIATIONS
// prefix the association ids with the view id
if ( typeof oValue === "string" ) {
oSettings[sKey] = that.createId(oValue);
}
break;
case 5: // EVENTS
if ( typeof oValue === "string" ) {
oSettings[sKey] = EventHandlerResolver.resolveEventHandler(oValue, oController);
}
break;
}
}
}
}
});
};
/**
* Loads and returns the template from a given URL.
*
* @param {string} sTemplateName The name of the template
* @param {object} [mOptions] with view settings
* @param {boolean} [mOptions.async=false] whether the action should be performed asynchronously
* @return {string|Promise} the template data, or a Promise resolving with it when async
* @private
*/
JSONView.prototype._loadTemplate = function(sTemplateName, mOptions) {
var sResourceName = sTemplateName.replace(/\./g, "/") + ".view.json";
if (!mOptions || !mOptions.async) {
this._oJSONView = LoaderExtensions.loadResource(sResourceName);
} else {
var that = this;
return LoaderExtensions.loadResource(sResourceName, mOptions).then(function(oJSONView) {
that._oJSONView = oJSONView;
});
}
};
JSONView.prototype.getControllerName = function() {
return this._oJSONView.controllerName;
};
return JSONView;
});