devexpress-reporting
Version:
DevExpress Reporting provides the capability to develop a reporting application to create and customize reports.
201 lines (200 loc) • 10.3 kB
JavaScript
/**
* DevExpress HTML/JS Reporting (designer\jsReportDesignerBinding.js)
* Version: 25.1.3
* Build date: Jun 26, 2025
* Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED
* License: https://www.devexpress.com/Support/EULAs/universal.xml
*/
import { addCultureInfo, ajaxSetup, fetchSetup, requestManager } from '@devexpress/analytics-core/analytics-utils';
import { DxAnalyticsComponentCommon, getLocalization, JSDesignerBindingCommon, resolveFromPromises, ShowMessage, _wrapModelInObservable, troubleshootingPageWrapper, assignTroubleshootingPage, useKoIntegration, DxDeferred, $dx } from '@devexpress/analytics-core/analytics-internal';
import { registerBaseBinding } from '@devexpress/analytics-core/analytics-widgets-internal';
import * as ko from 'knockout';
import { _setChartLimitation } from '../chart/_initializer';
import { EventGenerator } from '../common/binding/eventGenerator';
import { createReportDesignerFromModel } from './internal/_initializer';
import { limitation } from './internal/_settings';
import { JSReportDesigner } from './jsReportDesigner';
useKoIntegration();
export class JSReportDesignerBinding extends JSDesignerBindingCommon {
_applyBindings(model, $element) {
troubleshootingPageWrapper(() => {
this.sender.designerModel = model;
this._disposables.push(model);
const childTemplate = !model ? document.createElement('div') : this._templateHtml;
$element.empty();
const child = $element.append(childTemplate).children()[0];
if (!child)
return;
ko.cleanNode(child);
this._callbacks && this._callbacks.designer.beforeRender && this._callbacks.designer.beforeRender(model);
ko.applyBindings(model, child);
model.afterRender();
this._fireEvent('Init');
this._updateSurfaceSizeTimeout && clearTimeout(this._updateSurfaceSizeTimeout);
this._updateSurfaceSizeTimeout = setTimeout(() => {
model?.updateSurfaceSize && model?.updateSurfaceSize();
}, 1);
}, this.developmentMode, $element);
}
_initializeCallbacks() {
if (this._options.callbacks) {
const previewEvents = EventGenerator.generatePreviewEvents((eventName, args) => { this._fireEvent(eventName, args); }, 'Preview');
const designerEvents = EventGenerator.generateDesignerEvents((eventName, args) => { this._fireEvent(eventName, args); });
this._checkCallbackName(previewEvents);
this._checkCallbackName(designerEvents);
const availablePreviewEvents = this._generateCallbackDictionary(previewEvents, 'preview');
const availableDesignerEvents = this._generateCallbackDictionary(designerEvents, 'designer');
const availableEvents = {
preview: availablePreviewEvents,
designer: availableDesignerEvents
};
return availableEvents;
}
}
_createModel(initData, element) {
return createReportDesignerFromModel(initData, element, this._callbacks, false);
}
_showErrorInfo(jqXHR, getRequestDetails, errorThrown, element) {
const messages = [];
if (jqXHR && jqXHR.status)
messages.push(jqXHR.status);
if (errorThrown)
messages.push(errorThrown);
const helpLink = 'https://go.devexpress.com/Web_Reporting_Diagnostics_Tips.aspx';
const consoleMessage = `Review the following help topic to diagnose a problem: '${helpLink}'.`;
const clientMessage = getLocalization(`The page is blank because the Report Designer failed to load the report. Consult the developer for assistance.
Use development mode for detailed information.`, 'ASPxReportsStringId.ReportDesigner_GetReportDesignerModel_Error');
if (this.developmentMode) {
assignTroubleshootingPage(element);
console.log(consoleMessage);
if (jqXHR && jqXHR.responseText)
console.log(jqXHR.responseText);
}
else {
ShowMessage(clientMessage);
}
}
_getDesignerModelRequest(reportUrl, element) {
const requestOptions = this._options.requestOptions;
const getDesignerModelActionUrl = this._getServerActionUrl(requestOptions.host, requestOptions.getDesignerModelAction);
const onError = (data, textStatus, jqXHR, getRequestDetails, errorThrown) => {
this._showErrorInfo(jqXHR, getRequestDetails, errorThrown, element);
if (this._callbacks && this._callbacks.designer && this._callbacks.designer.onServerError)
this._callbacks.designer.onServerError({ jqXHR: jqXHR, textStatus: textStatus, data: data, getRequestDetails: getRequestDetails });
if (errorThrown)
throw errorThrown;
};
const requestManagerSetup = {
ajaxSetup,
fetchSetup
};
const getModel = requestManager.getInstance(requestManagerSetup).sendRequest({
url: getDesignerModelActionUrl,
type: 'POST',
data: {
reportUrl,
designerModelSettings: this._options.designerModelSettings
}
});
const _deferredModel = new DxDeferred();
this._deferreds.push(_deferredModel);
getModel.done(_deferredModel.resolve.bind(_deferredModel)).fail(_deferredModel.reject.bind(_deferredModel));
_deferredModel.done((result, textStatus, jqXHR) => {
if (result.error) {
return onError(result, textStatus, jqXHR, () => ({ url: getDesignerModelActionUrl, data: { reportUrl } }), result.error);
}
result.handlerUri = this._getServerActionUrl(requestOptions.host, result.handlerUri);
result.viewerHandlerUri = this._getServerActionUrl(requestOptions.host, result.viewerHandlerUri);
result.queryBuilderHandlerUri = this._getServerActionUrl(requestOptions.host, result.queryBuilderHandlerUri);
this._initializationData(result);
}).fail((jqXHR, textStatus, errorThrown) => {
if (!jqXHR && !errorThrown)
return;
const localizationPromises = [];
this._callbacks && this._callbacks.designer && this._callbacks.designer.customizeLocalization && this._callbacks.designer.customizeLocalization(localizationPromises);
resolveFromPromises(localizationPromises, () => {
onError({ error: errorThrown }, textStatus, jqXHR, () => ({ url: getDesignerModelActionUrl, data: { reportUrl } }), errorThrown);
});
});
}
constructor(_options, customEventRaiser) {
super(_options, customEventRaiser);
this._model = null;
this._deferreds = [];
limitation(_options.limitation);
_setChartLimitation(limitation());
_options.designerModel = _wrapModelInObservable(_options.designerModel);
this.sender = new JSReportDesigner(_options.designerModel);
this._initializationData = ko.isObservable(_options.initializationData)
? _options.initializationData
: ko.observable(_options.initializationData);
_options.callbacks && _options.callbacks._eventSenderCreated && _options.callbacks._eventSenderCreated(this.sender);
this._callbacks = this._initializeCallbacks();
this._callbacks && this._callbacks.designer.onInitializing && this._callbacks.designer.onInitializing();
}
dispose() {
(this._deferreds || []).forEach((deferred) => {
deferred.reject();
});
this._updateSurfaceSizeTimeout && clearTimeout(this._updateSurfaceSizeTimeout);
super.dispose();
}
applyBindings(element) {
const _$element = $dx(element);
_$element.addClass('dx-designer');
this._createDisposeFunction(element);
if (this._options.undoEngine) {
this._applyBindings(this._options, _$element);
return;
}
const applyBindingsFunc = (newData) => {
this.developmentMode = this.developmentMode || newData.developmentMode;
newData.developmentMode = this.developmentMode;
troubleshootingPageWrapper(() => {
if (this._model) {
this._disposables.splice(this._disposables.indexOf(this._model), 1);
ko.cleanNode(element.firstChild);
this._model.dispose();
}
this._createModel(newData, element).done((model) => {
this._model = model;
this._applyBindings(model, _$element);
});
}, this.developmentMode, _$element);
};
this._disposables.push(this._initializationData.subscribe((newVal) => {
applyBindingsFunc(newVal);
}));
if (this._options.requestOptions) {
this._getLocalizationRequest().done((localization) => {
localization && addCultureInfo(localization);
}).always(() => {
if (this._options.requestOptions.getDesignerModelAction) {
if (ko.isSubscribable(this._options.reportUrl)) {
this._disposables.push(this._options.reportUrl.subscribe((newVal) => this._getDesignerModelRequest(newVal, _$element)));
}
this._getDesignerModelRequest(ko.unwrap(this._options.reportUrl), _$element);
}
else {
applyBindingsFunc(this._initializationData());
}
});
}
else {
applyBindingsFunc(this._initializationData());
}
}
}
const dxReportDesignerBindingName = 'dxReportDesigner';
export class DxReportDesigner extends DxAnalyticsComponentCommon {
getBindingName() {
return dxReportDesignerBindingName;
}
}
registerBaseBinding(dxReportDesignerBindingName, '$data');
ko.bindingHandlers[dxReportDesignerBindingName] = {
init: function (element, valueAccessor) {
new JSReportDesignerBinding(ko.unwrap(valueAccessor()) || {}).applyBindings(element);
return { controlsDescendantBindings: true };
}
};