Раcширить логику save, после валидации. Контроль Chain выполнения

Необходимо расширить логику для некоей карточки использующей базовый save метод, но расширить логику необходимо уже после встроенной валидации обязательных полей.
Как необходимый кандидат подходит функция saveEntityInChain из BasePageV2
Она вызывается до непосредственного сохранения, но после проверки обязательных полей, и все замечательно пока логика - синхронная.
А вот как быть если расширение логики подразумевает вызов асинхронный ?
Сам вызов saveEntityInChain в рамках цепочки вызовов, т.е. заворачивая в коллбек this.callParent() мы фактически выходим из чейна в связи с чем имеем ряд предсказуемых ошибок.

this.Terrasoft.chain(
        this.saveCheckCanEditRight,
        this.saveAsyncValidate,
        this.saveEntityInChain,
        function() {
                this.Ext.callback(callback, scope || this);
        }, this);

Каким образом можно управлять текущим состоянием chain - и расширить его необходимым вызовом, или приостановить его исполнение до выполнения собственной асинхронной логики ?

Нравится

6 комментариев

Здравствуйте, вы можете переопределить данный метод, полным копированием на уровне вашей карточки, без вызова родительской реализации, и в цепочку методов, добавить свой асинхронный метод, написанный в стиле того же saveAsyncValidate.

Либо, вот еще обходной путь которым решали подобные задачи:

1. Создаем атрибут булевного типа, который будет описывать выполнена ли esq\асинхронная функция или еще нет. К примеру: IsEsqCompleted, с значением по умолчанию false.
2. Замещаем метод save, в нем проверяем если this.get(“IsEsqCompleted”) === false то вызываем наш esq\асинхронную функцию, если истина, то this.callParent(arguments);
3. В самом esq\асинхронной функции, в колбеке, устанавливаем IsEsqCompleted в true, и еще раз вызываем scope.save(); где scope, это this сохраненный в переменную scope вне колбека, что бы с помощью замыкания в колбеке был доступ к скоупу карточки.

Таким образом, по событию сохранения, вначале выполнится ваш метод, установится IsEsqCompleted в истину, сработает callParent и будет произведено сохранение карточки.

Спасибо, описанный Вами метод, довольно "старый прием" :) И в данном случае он наверное единственный наиболее лаконичный.
PS: К моменту написания вопроса, я забыл что в JS SDK документации есть ссылка на исходник функции, к счастью метод chain, хоть и не удостоился какого либо исчерпывающего описания, но его исходный код все таки представлен, что поставило все на свои места.

PPS: По началу я думал что это некая имплементация Ext.Promise или обертка над его использованием.
Собственно говоря, конструктив идентичный:

var promise = new Ext.Promise(function(resolve, reject){
    if(expression fulfilled){
         resolve(data);
     }
     else{
         reject(error reason);
     }
 });
 promise.then(
     function(value){ ... }, //success callback
     function(error){ ... }, //reject callback
     function(value){ ... }, //progress callback
     Scope
 );

Если бы это было так, то внутри любой функции chain присутствовал бы self.then / this.then метод, который позволил бы расширить цепочку вызова своей функцией, или даже своим чейном/промисом

 promise.then(
     function(value){ ... }, 
     function(error){
         self.then(function(){
                //добавляемый в chain код
          })}, 
     function(value){ ... }, 
 );

Но попытки найти что либо подобное среди доступных методов в отладчике - ничего не дала.

Держите пример использования террасофт чейн:

obj = {a: 2, b: 3}
 
var func1 = function(next) {
	console.log(this.a);
	next();
}
 
var func2 = function(next) {
	console.log(this.b);
	next();
}
 
var func3 = function(next) {
	console.log("end");
}
 
Terrasoft.chain(
	func1,
	func2,
	func3,
	obj
);

Да уже понятно все по исходному коду самой функции.
В случае если это замещающая схема - то там в любом случае использовать callParent,
что по сути тоже chain - но реализован он иначе, и вот где-то в изначальном объектв происходит вызов next()
Хотя в приведенном выше примере, я только что посмотрел в исходном коде - прямой вызов next() все равно не используется везде идет вызов this.Ext.callback (предполагаю что это или альтернатива или туда в callback и подставляется следующий аргумент чейна автоматически).

В любом случае сам по себе аргумент чейна не обладает свойством расширяющим свой вызов, в чем собственно изначально и был вопрос.

Здравствуйте последняя функция в базовой реализации в BaseEntityPage,

function() {
this.Ext.callback(callback, scope || this);
}, this);

Сделана для того, чтобы на уровень выше, можно было вызвать callParent передав свой метод\цепочку, которая(й) выполнится после базовой.
К сожалению, на уровень выше, в BasePageV2 уже такой возможности не предоставили, так что на уровне вашей карточки, отвечая на ваш вопрос, вмешаться в центр цепочки, до сохранения, но после валидации, можно полным переопределением, и вызывать свою функцию с асинхронной частью, в колбеке которой продолжать цепочку. Пример:

save: function(config) {
				this.showBodyMask({
					selector: this.get("ContainerSelector") || null,
					timeout: 0
				});
				this.Terrasoft.chain(
					this.saveCheckCanEditRight,
					this.saveAsyncValidate,
					this.myMethod,
					this.saveEntityInChain,
					this.saveDetailsInChain,
					function() {
						this.onSaved(this.cardSaveResponse, config);
						this.cardSaveResponse = null;
						delete this.cardSaveResponse;
					},
				this);
			},
 
			myMethod: function(callback, scope) {
				window.console.log("myMethod.");
 
				var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {
					rootSchemaName: "Contact"
				});
				esq.addColumn("Name");
				var paramDataType = this.Terrasoft.DataValueType.GUID;
				var lookupFilter = this.Terrasoft.createColumnFilterWithParameter(Terrasoft.ComparisonType.EQUAL,
					"Id", "c4ed336c-3e9b-40fe-8b82-5632476472b4", paramDataType);
				esq.filters.addItem(lookupFilter);
 
				// вызов асинхронной части
				esq.getEntityCollection(function(result) {
					// ... любые синхронные действия
					// продолжение цепочки
					this.Ext.callback(callback, scope || this);
				}, this);
			}
Показать все комментарии