Добрый день. Необходимо написать функцию, которая подсчитывает количество значений в записи. Проблема в том, что из-за того что esq выполняется асинхронно, функция всегда возвращает undefined. Нужно как-то вернуть из нее значение, так как функция будет использоваться повторно. Как это пофиксить? Спасибо.

 

             /**

             * Подсчет количества продуктов в детали.

             */

            checkProductsCount: function() {

                var select = this.Ext.create("Terrasoft.EntitySchemaQuery", {

                    rootSchemaName: "OpportunityProductInterest"

                });

                    

                var esqRestaurantFilter = Terrasoft.createColumnFilterWithParameter(

                Terrasoft.ComparisonType.EQUAL, "Opportunity.Id", this.get("Id"));

                select.filters.add("selectFilter", esqRestaurantFilter);

                

                var selectedProductsCount = select.getEntityCollection(function(result) {

                    try {

                        if (!result.success) {

                            throw new Error("Ошибка запроса данных");

                        }

                    

                        return result.collection.getCount();

                    }

                    catch (e) {

                        Terrasoft.showErrorMessage(e.message);

                    }

                }, this);

                return selectedProductsCount;

            }

Нравится

1 комментарий
Лучший ответ

Результат функции вы можете записать в атрибут либо сразу в поле через this.set

this.set("Filed", value)

Если после вычитки значений нужные еще какие-то действия, можно на атрибут повесить метод-обработчик по изменению атрибута (свойство dependencies). Подробнее про вычисляемые поля тут:  https://academy.terrasoft.ru/documents/technic-sdk/7-16/dobavlenie-vych…

Так же вы можете столкнуться с тем, что в callback'е от вызова функции getEntityCollection не будет доступа к полям (на самом деле просто другой контекст в this будет), в этом случае следует перед выполнением запроса записать this в другую переменную:

var self = this.

 

var self = this;
 
//ну и далее
var selectedProductsCount = select.getEntityCollection(function(result) {
                   if (result && result.success) {
                    self.set("Filed", value);
                        }
                }, this);

 

 

Результат функции вы можете записать в атрибут либо сразу в поле через this.set

this.set("Filed", value)

Если после вычитки значений нужные еще какие-то действия, можно на атрибут повесить метод-обработчик по изменению атрибута (свойство dependencies). Подробнее про вычисляемые поля тут:  https://academy.terrasoft.ru/documents/technic-sdk/7-16/dobavlenie-vych…

Так же вы можете столкнуться с тем, что в callback'е от вызова функции getEntityCollection не будет доступа к полям (на самом деле просто другой контекст в this будет), в этом случае следует перед выполнением запроса записать this в другую переменную:

var self = this.

 

var self = this;
 
//ну и далее
var selectedProductsCount = select.getEntityCollection(function(result) {
                   if (result && result.success) {
                    self.set("Filed", value);
                        }
                }, this);

 

 

Показать все комментарии

Подскажите как динамически делать пункт меню в "Действие" карточки в совмещенном режиме.

В обычном режиме все работает нормально.

attributes: {
	"IsCanVisaStatus": {
		dataValueType: Terrasoft.DataValueType.BOOLEAN,
		value: true,  // Значение по умолчанию которое передаеться при загрузке страницы на нашу кнопку
		dependencies: [{
			columns: ["KtState"], // Отслеживаем изменение поля состояния 
			methodName: "setCanVisaStatusContract" // и запускаем нашу функция если состояние изменчется
		}]
	}
},
methods: {
	onEntityInitialized: function() {
		this.callParent(arguments);
		this.setCanVisaStatusContract(); // Запускаем функцию при входе на страницу когда поля уже заполненны
	},
	getActions: function() {
		var actionMenuItems = this.callParent(arguments);
		actionMenuItems.addItem(this.getButtonMenuItem({
			Type: "Terrasoft.MenuSeparator",
			Caption: ""
		}));
		actionMenuItems.addItem(this.getButtonMenuItem({
			"Caption": VisaHelper.resources.localizableStrings.SendToVisaCaption,
			"Tag": VisaHelper.SendToVisaMenuItem.methodName,
			"Enabled": {"bindTo": "IsCanVisaStatus"} // мониторим через привязку состояние значения атрибута
		}));
		return actionMenuItems;
	},
	setCanVisaStatusContract: function(){
		var stateId = this.get("KtState").value; // Текущая стадия
		var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {  // Запрос к базе данных о том нужна ли кнопка отправки на визирования
			rootSchemaName: "KtContractKarTelState"
		});
		esq.addColumn("KtCanVisa", "KtCanVisa"); // Сама колонка указывающая что нужна кнопка
		esq.getEntity(stateId, function(result) {
			if (result.success) {
				if (result.entity.get("KtCanVisa") && this.canEntityBeOperated()) {   // this.canEntityBeOperated() - стандартный класс отвечает за то что если карточка на в режиме редактирования то false.
					this.set("IsCanVisaStatus", true);
				} else {									// Присваивает атрибуту значение true или false в зависимости от результата запроса
					this.set("IsCanVisaStatus", false);
				}
 
			}
		}, this);
	}
}

В отоге весь код работает хорошо но только в обычном режиме карточки, а совмещенном кнопка не видет атрибут хоть по девагу все запускается так же и значения поля в атрибуте меняется по кнопка просто его не видит. Пробовал атрибут вписать в Section - не помогло, атрибут видит кнопка но изменить его не получается.

Нравится

2 комментария
Лучший ответ

Нужно использовать механизм сообщений. Вот пример. Моем случае бинд был на аттрибут (в странице редакирования) "Enabled": {"bindTo": "enabledPlanPaymentDate"}

В секции пишем

		messages: {
			"GetEnabledPlanPaymentDate": {
				mode: Terrasoft.MessageMode.PTP,
				direction: Terrasoft.MessageDirectionType.SUBSCRIBE
			}
		},
		attributes: {
			"enabledPlanPaymentDate": {
				"type": this.Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
				"dataValueType": this.Terrasoft.DataValueType.BOOLEAN,
				"value": false
			}
		},
		methods: {
			init:function() {
				this.callParent(arguments);
				this.sandbox.subscribe("GetEnabledPlanPaymentDate",  function(args){this.$enabledPlanPaymentDate = args}, this, ["SectionModuleV2_InvoiceSectionV2"]);
			},
		}

В странице реадактирования:

		messages: {
			"GetEnabledPlanPaymentDate": {
				mode: Terrasoft.MessageMode.PTP,
				direction: Terrasoft.MessageDirectionType.PUBLISH
			}
		},
 
		methods: {
			onEntityInitialized: function() {
				this.callParent(arguments);
				this.sandbox.publish("GetEnabledPlanPaymentDate", this.checkEnabledChangePlanPaymentDate(), ["SectionModuleV2_InvoiceSectionV2"]);
			},
			checkEnabledChangePlanPaymentDate: function() {
				return this.$enabledPlanPaymentDate;
			}
}

 

Нужно использовать механизм сообщений. Вот пример. Моем случае бинд был на аттрибут (в странице редакирования) "Enabled": {"bindTo": "enabledPlanPaymentDate"}

В секции пишем

		messages: {
			"GetEnabledPlanPaymentDate": {
				mode: Terrasoft.MessageMode.PTP,
				direction: Terrasoft.MessageDirectionType.SUBSCRIBE
			}
		},
		attributes: {
			"enabledPlanPaymentDate": {
				"type": this.Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
				"dataValueType": this.Terrasoft.DataValueType.BOOLEAN,
				"value": false
			}
		},
		methods: {
			init:function() {
				this.callParent(arguments);
				this.sandbox.subscribe("GetEnabledPlanPaymentDate",  function(args){this.$enabledPlanPaymentDate = args}, this, ["SectionModuleV2_InvoiceSectionV2"]);
			},
		}

В странице реадактирования:

		messages: {
			"GetEnabledPlanPaymentDate": {
				mode: Terrasoft.MessageMode.PTP,
				direction: Terrasoft.MessageDirectionType.PUBLISH
			}
		},
 
		methods: {
			onEntityInitialized: function() {
				this.callParent(arguments);
				this.sandbox.publish("GetEnabledPlanPaymentDate", this.checkEnabledChangePlanPaymentDate(), ["SectionModuleV2_InvoiceSectionV2"]);
			},
			checkEnabledChangePlanPaymentDate: function() {
				return this.$enabledPlanPaymentDate;
			}
}

 

Трефилов Павел Сергеевич,

Спасибо, отлично работает. Не много изменил на двустороннюю подписку что бы атрибут менять через сообщение и тот что в секции и тот что на странице

Показать все комментарии

Добрый день. Подскажите пожалуйста, не как не могу решить данный кейс (причем когда-то такой решал давно но не могу не как вспомнить как).

Есть кнопка в группе кнопок "Действие" - "Отправить на визирование", она запускает процесс визирования. Данная кнопка должна быть активна если в таблице визирования уже есть как минимум одна положительная запись (автор согласовал). Для этого сделал запрос к базе данных и получил результат. Через свойство "Enabled" нужно реализовать доступность кнопки.

Код кнопки:

actionMenuItems.addItem(this.getButtonMenuItem({
	"Caption": VisaHelper.resources.localizableStrings.SendToVisaCaption,
	"Tag": VisaHelper.SendToVisaMenuItem.methodName,
	"Enabled": {"bindTo": "canEntityBeOperated"}
}));

"Enabled": {"bindTo": "canEntityBeOperated"} - сюда и надо отправить true и false.

Код запроса к базе данных:

var opportunityId = this.get("Id");
var ownerContact = "";
if (this.get("Owner")) {
	ownerContact = this.get("Owner").value;
}
var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {
	rootSchemaName: "KtOpportunityVisa"
});
esq.addColumn("VisaOwner.Contact", "VisaOwnerContact");
esq.addColumn("KtOpportunity", "KtOpportunity");
esq.addColumn("Status", "Status");
var esqOwnerContactFilter = esq.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL,
	"VisaOwner.Contact", ownerContact);
var esqOpportunityFilter = esq.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL,
	"KtOpportunity", opportunityId);
var esqVisaСanceledFilter = esq.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL,
	"Status", KtConfigurationConstantsKarTel.VisaStatus.Positive);
esq.filters.add("esqOwnerContactFilter", esqOwnerContactFilter);
esq.filters.add("esqOpportunityFilter", esqOpportunityFilter);
esq.filters.add("esqVisaСanceledFilter", esqVisaСanceledFilter);
esq.getEntityCollection(function (result) {
	if (result.success && result.collection.getCount() === 0) {
		return false;
	} else {
		return true;
	}
}, this);

Так как запрос асинхронный то на сколько помню решается это через Terrasoft.chain с Callback, но как это сделать хоть убейте не помню.

Нравится

2 комментария
Лучший ответ

По поводу упомянутого механизма Вы недавно задавали вопрос в этой теме. Также см. публикацию.

По поводу упомянутого механизма Вы недавно задавали вопрос в этой теме. Также см. публикацию.

Зверев Александр,

 решил вопрос через создание в таблице процесса что срабатывал при изменении в ней и отправлял сообщение на фронт, а на фронте подписался на него и запускаю функцию что меняет атрибут который и дает ответ в каком состоянии должна быть кнопка. Могу написать код реализации тут если кому нужно. Но в итоге бизнес решил что им это не надо потому далее не оптимизировал код. Сейчас проблема в активности этой кнопки в совмещенном режиме, создал отдельный вопрос

Показать все комментарии

Всем доброго времени суток.

Возможно ли настроить конфигурацию мобильного приложения следующим образом, что бы при срабатывании бизнес правила и обработки модели текущей записи возможно было доставать значения данных из связанных записей? В данном случае меня интересует Контакт - http://prntscr.com/o5yxe7. Без написания запроса. или запрос нужно писать в любом случае? Стоит задача при изменении Контакта необходимо в заказе перезаписывать контактный номер телефона, как лучше реализовать?

Нравится

3 комментария

Стоит задача при изменении Контакта необходимо в заказе перезаписывать контактный номер телефона, как лучше реализовать?

Мне кажется, такое нужно делать не в мобильной версии, а на стороне сервера при синхронизации. Ведь новые данные контакта могут прийти с разных сторон, например, их может исправить пользователь, работающий в веб-версии, интеграция с сайтом, 1С или ещё чем-то. Если менять будут при помощи класса EntitySchemaQuery, можно реализовать логику во встроенном или обычном процессе.

Зверев Александр,

 да это понятно на стороне сервера это само собой я предлагал и говорил заказчику, но требуют что бы на моб. устройстве все происходило, они будут работать в режиме оффлайн

В таком случае теряется смысл всего движка бизнес-процессов, который работает на серверной стороне. 

Показать все комментарии

Cделан запрос в базу данных из клиентского модуля для получения системной настройки: 

this.Terrasoft.SysSettings.querySysSettingsItem("PsMaximumProgramsCount", function(value) {
	countSettings = value;
}, this);

Получены данные из БД отфильтровав их и сравнив с полученным значением из системной настройки ранее, и если значение выпадающего списка periodicity  равно "Ежедневно" и количество записей в базе больше чем системной настройке - срабатывает валидация на поле и всплывающее уведомление при попытке сохранения:

message - сообщение для поля валидации;

periodicity  - выпадающий список (словарь) в котором есть значение "Ежедневно";

countSettings  - ранее полученное значение системной настройки;

concertProgramsCount - количество записей в таблице;

var periodicity = "";
if (this.get("PsPeriodicity")) {
	periodicity = this.get("PsPeriodicity").displayValue;
}
var esq = Ext.create("Terrasoft.EntitySchemaQuery", {
	rootSchemaName: "PsConcertPrograms"
});
esq.addColumn("PsPeriodicity.Name", "PsPeriodicityName");
var esq1Filter = esq.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL,
	"PsPeriodicity.Name", "Ежедневно");
esq.filters.add("esq1Filter", esq1Filter);
esq.getEntityCollection(function(result) {
	var message = "";
	if (result.success) {
		var concertProgramsCount = result.collection.collection.length;
		if (periodicity === "Ежедневно" && concertProgramsCount <= countSettings ) {
			message = this.get("Resources.Strings.PsFewFreeConcertHalls").replace("NNN", countSettings);
		}
	}
	return message;
}, this);

Вывод валидации для поля и при сохранении страницы:

concertHallsValidator: function(message) {
	var invalidMessage = message;
	return {
		fullInvalidMessage: invalidMessage,
		invalidMessage: invalidMessage
	};
}

Проблема в том что по отдельности все работает, но когда вместе - вступает в дело асинхронность и все идет не по очереди, а если использую через callback тогда застреваю на замыкании.

Спасибо всем кто окажет помощь в решении проблемы.

Нравится

7 комментариев
Лучший ответ

Prime Source,

нет. стоп. я думал вы прогоняете esq в onEntityInitialized, а потом используете this.$PeriodValidationMessage для проверки. тогда такие варианты:

1) на изменение поля PsPeriodicity запускаете concertHallsValidator. Изменяете this.addColumnValidator("CreatedOn", this.MessageValidator);

2) Используете asyncValidate (вроде так называется метод)

И уберите return. Ну не используется он в асинхронных функциях)

немного не понимаю смысла return из async функции, поэтому вариант такой:

в attributes добавляете 

"PeriodValidationMessage": {
	dataValueType: 1,
	value: ""
}

esq:

var periodicity = "";
if (this.get("PsPeriodicity")) {
	periodicity = this.get("PsPeriodicity").displayValue;
}
var esq = Ext.create("Terrasoft.EntitySchemaQuery", { rootSchemaName: "PsConcertPrograms" });
esq.addColumn("PsPeriodicity.Name", "PsPeriodicityName");
esq.filters.addItem(esq.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL, "PsPeriodicity.Name", "Ежедневно"));
esq.getEntityCollection(function(result) {
	if (result.success) {
		var concertProgramsCount = result.collection.collection.length;
		//считывание сист. настройки
		Terrasoft.SysSettings.querySysSettingsItem("PsMaximumProgramsCount", function(countSettings) {
			//сравнение
			if (periodicity === "Ежедневно" &amp;&amp; concertProgramsCount &lt;= countSettings ) {
				this.$PeriodValidationMessage = this.get("Resources.Strings.PsFewFreeConcertHalls").replace("NNN", countSettings);
			}
 
		}, this);
	}
}, this);

валидация

concertHallsValidator: function(message) {
	var invalidMessage = this.$PeriodValidationMessage;
	return {
		fullInvalidMessage: invalidMessage,
		invalidMessage: invalidMessage
	};
}

 

Добрый день,

Каждый борется с асинхронностью своими средствами :)

Как вариант, можно вложить одно в другое: выполнять запросы по очереди, по мере получения ответа из БД. Например перенести вашу логику с esq запросом в колбэк системной настройки.

Тёскин Дмитрий Валерьевич,

 

Пробовал, тогда return возвращает поздно ответ

concertHallsValidator:  function(){
	var periodicity = "";
	if (this.get("PsPeriodicity")) {
		periodicity = this.get("PsPeriodicity").displayValue;
	}
	this.Terrasoft.SysSettings.querySysSettingsItem("PsMaximumProgramsCount", function(countSettings) {
		var esq = Ext.create("Terrasoft.EntitySchemaQuery", {
			rootSchemaName: "PsConcertPrograms"
		});
		esq.addColumn("PsPeriodicity.Name", "PsPeriodicityName");
		var esq1Filter = esq.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL,
			"PsPeriodicity.Name", "Ежедневно");
		esq.filters.add("esq1Filter", esq1Filter);
		esq.getEntityCollection(function(result) {
			var invalidMessage = "";
			if (result.success) {
				var concertProgramsCount = result.collection.collection.length;
				if (periodicity === "Ежедневно" &amp;&amp; concertProgramsCount &gt; countSettings ) {
					invalidMessage = this.get("Resources.Strings.PsFewFreeConcertHalls").replace("NNN", countSettings);
				}
			}
			return invalidMessage;
		}, this);
	}, this);
},
setValidationConfig: function() {
	this.callParent(arguments);
	this.addColumnValidator("PsPeriodicity", this.concertHallsValidator);
}

 

Варфоломеев Данила,

Асинхронность не дает

methods: {
	concertHallsValidator:  function(callback){
		var periodicity = "";
		if (this.get("PsPeriodicity")) {
			periodicity = this.get("PsPeriodicity").displayValue;
		}
		var esq = Ext.create("Terrasoft.EntitySchemaQuery", { rootSchemaName: "PsConcertPrograms" });
		esq.addColumn("PsPeriodicity.Name", "PsPeriodicityName");
		esq.filters.addItem(esq.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL, "PsPeriodicity.Name", "Ежедневно"));
		esq.getEntityCollection(function(result) {
			if (result.success) {
				var concertProgramsCount = result.collection.collection.length;
				//считывание сист. настройки
				Terrasoft.SysSettings.querySysSettingsItem("PsMaximumProgramsCount", function(countSettings) {
					//сравнение
					if (periodicity === "Ежедневно" &amp;&amp; concertProgramsCount &lt;= countSettings ) {
						this.$PeriodValidationMessage = this.get("Resources.Strings.PsFewFreeConcertHalls").replace("NNN", countSettings);
					}
					return callback.call(this);
				}, this);
			}
		}, this);
	},
	MessageValidator: function() {
		var invalidMessage = this.$PeriodValidationMessage;
		return {
			fullInvalidMessage: invalidMessage,
			invalidMessage: invalidMessage
		};
	},
	setValidationConfig: function() {
		// Вызывает инициализацию валидаторов родительской модели представления.
		this.callParent(arguments);
		this.addColumnValidator("CreatedOn", this.concertHallsValidator(this.MessageValidator));
	}
},
			}

 

Prime Source,

нет. стоп. я думал вы прогоняете esq в onEntityInitialized, а потом используете this.$PeriodValidationMessage для проверки. тогда такие варианты:

1) на изменение поля PsPeriodicity запускаете concertHallsValidator. Изменяете this.addColumnValidator("CreatedOn", this.MessageValidator);

2) Используете asyncValidate (вроде так называется метод)

И уберите return. Ну не используется он в асинхронных функциях)

Варфоломеев Данила,

 

Спасибо большее, помогло отлично asyncValidate. Раньше не знал что этот метод. Вод такой код заработал отлично:

asyncValidate: function(callback, scope) {
	this.callParent([function(response) {
		if (!this.validateResponse(response)) {
			return;
		}
		Terrasoft.chain(
			function(next) {
				this.validateConcertHalls(function(response) {
					if (this.validateResponse(response)) {
						next();
					}
				}, this);
			},
			function(next) {
				callback.call(scope, response);
				next();
			}, this);
	}, this]);
},
validateConcertHalls: function(callback, scope) {
	Terrasoft.SysSettings.querySysSettingsItem("PsMaximumProgramsCount", function(countSettings) {
		var periodicity = "";
		var result = {success: true};
		if (this.get("PsPeriodicity")) {
			periodicity = this.get("PsPeriodicity").displayValue;
		}
		var esq = Ext.create("Terrasoft.EntitySchemaQuery", { rootSchemaName: "PsConcertPrograms" });
		esq.addColumn("PsPeriodicity.Name", "PsPeriodicityName");
		esq.filters.addItem(esq.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL, 
			"PsPeriodicity.Name", "Ежедневно"));
		esq.getEntityCollection(function(response) {
			if (response.success &amp;&amp; periodicity === "Ежедневно" &amp;&amp; (!this.isAddMode() &amp;&amp; 
				response.collection.getCount() &gt; countSettings) || (this.isAddMode() &amp;&amp; 
				response.collection.getCount() &gt;= countSettings)) {
					result.message = this.get("Resources.Strings.PsFewFreeConcertHalls").replace("NNN", countSettings);
					result.success = false;
			}
			callback.call(scope || this, result);
		}, this);
	}, this);
}

 

Prime Source,

Если ваша функция должна возвращать какие-то значение, то из коллбэка это сделать не получится. В этом случае у вас уже должны быть где-то готовые значения для того, что бы функция валидации могла синхронно выполниться и вернуть значения. Например значение системной настройки можно получить при инициализации страницы и сохранить в атрибуте страницы, т.к. системная настройка скорее всего не будет часто меняться. Значение из esq запроса можно так же получить раньше и сохранить в атрибут, а потом уже при валидации оперировать существующими значениями. Ну или сделать так, как посоветовал Варфоломеев Данила.

Показать все комментарии

В статье Асинхронные запросы в bpm’online 5.X был приведен пример отправки Get/Post запросов со страницы с использование языка JavaScript.
В этой статье хотелось бы рассказать про подводные камни, которые встретились при реализации и как их можно обойти.
Предположим, имеется некий абонент интернет провайдера, у которого есть такие поля, как баланс, MAC-адрес, IP-адрес, состояние, список последних сессий абонента, получение доступных для смены тарифов, получение текущего тарифа абонента. При открытии карточки необходимо получать обновленную информацию от WCF сервиса, отображать её на странице и сохранять в базе данных.
При наличии сложной логики на странице, а именно:

  • применение нескольких асинхронных запросов;
  • обновление значений в контроллах на странице;
  • обновление значений столбцов в DataSource.ActiveRow;
  • обновление записей в базе с помощью класса Update (для того, чтобы при получении обновленной информации от сервиса, например, баланса, сразу записывать его значение в базу, и при последующем открытии карточки, даже если пользователь закрыл страницу не сохранив запись нажав "Отмена", пользователь увидит последние обновленные сведения, полученные от биллинговой системы, до того момента, пока от сервиса не придет новая, обновленная информация о балансе, в противном случае, при отсутствии доступа к данному сервису (пропало интернет соединение), будет отображаться последняя загруженная информация о балансе).

Столь сложная логика на странице может стать причиной ошибок на странице, как, например, ошибка "Сбой при загрузке состояния отображения. Дерево элементов управления, в которое загружается это состояние отображения, должно соответствовать дереву элементов управления, использованному для сохранения состояния отображения при предыдущем запросе. Например, при динамическом добавлении элементов управления те элементы управления, которые были добавлены при обратной передаче, должны по типу и положению соответствовать элементам управления, добавленным при исходном запросе." (скриншот окна с ошибкой ниже).

Данная ошибка возникает при переходе на деталь "Активности", и другие детали, в которые загружается информация, связанная с данной записью. Причем ошибка возникает только во время выполнения асинхронных запросов к сервису, если быть точнее, при получении информации от сервиса, выполняется серверная логика, во время которой и возникает ошибка.
Чтобы избежать данной ошибки, необходимо запретить пользователю переходить на детали, которые вызывают ошибку. Сделать это можно следующим образом.
В БП страницы на событие PageLoadComplete необходимо добавить элемент "Задание сценарий", который добавляет на страницу следующий JavaScript код (данный скрипт должен выполняться перед добавлением скрипта создания самого запроса, т.е. объекта XMLHttpRequest, как было описано в статье):

// объявление глобальных переменных для объектов-запросов
requestUpdateBalance = new XMLHttpRequest();
requestUpdateMacAndSerialNumber = new XMLHttpRequest();
requestUpdateAbonentState = new XMLHttpRequest();
requestGetSessionDetalisation = new XMLHttpRequest();
requestGetAvaliableTariffs = new XMLHttpRequest();
requestGetCurrentTariff = new XMLHttpRequest();

// Функция, позволяющая скрыть/отобразить все детали, следующие за деталью
// "Активности", включая саму эту деталь
function changeVisibleOfTabs(isVisible) {
  for (var i = 0; i PageContainer_DataTabPanel.items.items.length; i++) {
    if (PageContainer_DataTabPanel.items.items[i].caption == 'Активности') {
      for (var j = i; j PageContainer_DataTabPanel.items.items.length; j++) {
        PageContainer_DataTabPanel.items.items[j].tabHeader.setVisible(isVisible);
      }
      break;
    }
  }
}

// скрываем детали перед выполнением запросов
changeVisibleOfTabs(false);

// создаем таймер, который каждые 500мс проверяет, завершились ли все запросы,
// после того, как все запросы завершены, создаем таймер на 2000мс,
// чтобы отработала вся логика на странице, после чего отображаем скрытые детали
var timerCheckIsDone = setInterval(function() {
  if ((requestUpdateBalance != undefined && requestUpdateBalance != null && requestUpdateBalance.readyState == 4) &&
    (requestUpdateMacAndSerialNumber != undefined && requestUpdateMacAndSerialNumber != null && requestUpdateMacAndSerialNumber.readyState == 4) &&
    (requestUpdateAbonentState != undefined && requestUpdateAbonentState != null && requestUpdateAbonentState.readyState == 4) &&
    (requestGetSessionDetalisation != undefined && requestGetSessionDetalisation != null && requestGetSessionDetalisation.readyState == 4) &&
    (requestGetAvaliableTariffs != undefined && requestGetAvaliableTariffs != null && requestGetAvaliableTariffs.readyState == 4) &&
    (requestGetCurrentTariff != undefined && requestGetCurrentTariff != null && requestGetCurrentTariff.readyState == 4)) {
    clearInterval(timerCheckIsDone);
  var timerChangeVisibleOfTabs = setTimeout(function() {
      changeVisibleOfTabs(true); // отображаем детали
    }, 2000);
  }
}, 500);

Важно! После выполнения данного скрипта, необходимо создать объекты запросов, как было показано в статье. Причем объекты запросов должны ссылаться на глобальные переменные, которые мы объявляли в самом начале, т.е. необходимо писать вместо
var request = new XMLHttpRequest();

вот так:
requestUpdateBalance = new XMLHttpRequest();

Иначе доступ к данным переменным будет невозможен из функции, где выполняется проверка завершения запросов.

Нравится

Поделиться

0 комментариев
Показать все комментарии

Есть ситуации, когда нужно, чтобы при открытии карточки редактирования, в поля карточки подгружалась информация, которую нужно выводить в актуальном (обновленном) состоянии. К примеру, когда клиент звонит в тех поддержку, сотрудник техподдержки открывает карточку клиента в bpm'online и должен видеть текущий баланс данного клиента.
Предположим, есть WCF сервис, работающий на некотором хостинге, созданный для интеграции с биллинговой системой, с методом получения баланса абонента GetBalance, ответ от которого приходит в виде строки в формате JSON, который мы можем обработать и вывести в нужном формате на страницу редактирования.
Если выполнять запрос из бизнес процесса карточки редактирования (на PageLoadComplete, к примеру), то при выполнении запроса возникает зависание карточки редактирования, и она не отвиснет пока либо не придет ответ, либо пока запрос не будет завершен по таймауту (если сервер долго не отвечает).
Чтобы выполнить запрос асинхронно, нужно добавить на страницу редактирования JavaScript код, который будет обращаться к сервису, получать ответ от него и выводить результат в контролы страницы. Чтобы отправлять запрос сразу после загрузки страницы, нужно добавить элемент бизнес процесса «Задание-сценарий» на событие «PageLoadComplete», либо если нужно отправлять запрос по нажатию на кнопку, то на событие нажатия на неё.

StringBuilder sb = new StringBuilder();
sb.Append("var request = new XMLHttpRequest();");
sb.Append("var url = 'http://someserveradress/SomeWCFService.svc/GetBalance';");
sb.Append(
"request.open('GET',url,true);" +
"request.onreadystatechange = function() " +
"{  if (request.readyState == 4 && request.status == 200) {" +
Page.BalanceEdit.ClientID + ".setValue(request.responseText); } }; ");
sb.Append("request.send();");
Page.AddScript(sb.ToString());

При выполнении данного кода Java Script кода на странице, мы получим ошибку в консоли браузера:
XMLHttpRequest cannot load http://someserveradress/SomeWCFService.svc/GetBalance. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.
Ошибка говорит о том, что запрещено обращаться к другому домену (попытка выполнить кросс-доменный запрос). Данное ограничение введено из соображений безопасности, но его можно обойти используя технологию CORS.
Подробную информацию об этой технологии можно посмотреть тут:
1) https://ru.wikipedia.org/wiki/Cross-origin_resource_sharing
2) http://enable-cors.org/
3) http://habrahabr.ru/post/120917/
Для того, чтобы разрешить кросс-доменные запросу, нужно включить поддержку на сервере, к которому приходит запрос, в моём случае это WCF служба. Для включения поддержки CORS у WCF сервиса, нужно выполнить действия, описанные тут: http://enable-cors.org/server_wcf.html.
Информацию по включению поддержки CORS для других платформ/серверов можно найти тут: http://enable-cors.org/server.html.
После этого, запрос должен выполняться асинхронно, не вызывая зависание карточки, благодаря чему можно просматривать/редактировать карточку в момент выполнения запроса.

Нравится

Поделиться

0 комментариев
Показать все комментарии