При добавлении зависимости следующим образом:

"RoundPropertyPriceWithDiscount": {
    dependencies: [
        {
             columns: ["PropertyPriceWithDiscount"],
             methodName: "roundPropertyPriceWithDiscount"
        }
    ]
}

и обработки методом:

roundPropertyPriceWithDiscount: function() {
	this.roundDoubleValue("PropertyPriceWithDiscount");
},
roundDoubleValue: function(propertyName) {
	var value = this.get(propertyName);
	var rounded = Math.round(value);
 
	if (rounded != value) {
		this.set(propertyName, rounded);
	}
},

происходит зацикливание. Кейс:

вводим в поле 5,56 - запускается метод, записывающий в поле 6,00, далее запускается еще раз (очевидно), проверяет что значение в поле равно его округленному значению (6,00 == 6,00) и значение в поле НЕ ПЕРЕЗАПИСЫВАЕТ. И вот далее идет ТРЕТИЙ запуск, в котором this.get() возвращает старое значение поля - 5,56. Ну и, естественно, это бесконечный цикл. Каким образом работает сей механизм? Если мне не изменяет память - в прошлых версиях такого не было.

7.13.1.769_SalesEnterprise_Softkey_MSSQL_ENU

 

UPD: Подписка на изменение поля вручную решает вопрос

this.on("change:PropertyPriceWithDiscount", this.roundPropertyPriceWithDiscount, this);

 

Нравится

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

Добрый день!

Если я правильно понял Ваш бизнес-кейс, то вы его неправильно реализовали. Если при изменении одного поля нужно взять значение этого поля, округлить его и записать в другое поле, то вам нужно делать this.set("RoundPropertyPriceWithDiscount", value);

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

Нет, я все правильно написал. Округление значения в том же самом поле. 

RoundPropertyPriceWithDiscount это просто название атрибута

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

Коллеги, добрый день. Столкнулся со следующей проблемой. Как можно в данном коде передать параметр в вызываемом метод?

 "UsrField": {

                dependencies: [

                    {

                        columns: ["UsrField2"],

                        methodName: "method"

                    }

                ]

            },

Нравится

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

Если "повесить" зависимость некоторого атрибута на колонку, в методе-обработчике зависимости изменять значение колонки-зависимости - вполне предсказуемо получить рекурсивный вызов.
Но обычно цепочка рекурсивных вызовов пресекается установкой флага, или внедрением проверок, которые гарантированно не пройдут при повторном вызове, что в свою очередь обеспечит выполнение рекурсивного вызова без изменения целевого значения.
В данном случае, не зависимо от механики предусмотренной в методе-обработчике, его вызов уходит в рекурсию в том случае если во время его исполнения было изменено значение зависимой колонки.
н/п

attributes: {
...
        "SameHandler": {
                dependencies: [
                        {
                                //поле с типом "Дата-время"
                                columns: ["TargetColumn"],
                                methodName: "SameHandlerControl"
                        }
                ]
        },
...

methods: {
...
                "SameHandlerControl": function() {
                        if (window.foo) {
                                window.foo = false;
                        } else {
                                window.foo = true;
                                this.set("TargetColumn", new Date());
                        }
                },

так вот несмотря на то, что при повторном вызове обработчика, выполнение уходит в ветку
...
                        if (window.foo) {
                                window.foo = false;
...

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

Как этого избежать ?
Почему так происходит ?

PS: классический кейс "самоконтроля поля ввода" - поле реагирует на изменение своего значения вызовом обработчика, который производит проверки и вычисления с введенным значением, по необходимости корректируя/восстанавливая свое собственное значение, предотвращая рекурсивный вызов обработчика.

Нравится

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

Здравствуйте
Интересный случай. Глубоко не копал, но причина вроде бы в последовательности вызова
меняя this.set("TargetColumn", new Date());, вы "дергаете" SameHandlerControl, т.к. атрибут связан с колонкой. И поскольку перед "самовызовом" метода вы меняете флаг выхода, то получается замкнутый цикл

попробуйте вызвать window.foo = true; ПОСЛЕ this.set("TargetColumn", new Date());

мой тестовый код, на котором нет вечного цикла

define("UsrSection1Page", [], function() {
	return {
		entitySchemaName: "UsrSection",
		attributes: {
			"SameHandler": {
				dependencies: [
					{
						columns: ["UsrName"],
						methodName: "SameHandlerControl"
					}
				]
			}
		},
		methods: {
			"SameHandlerControl": function() {
				var n = this.get("UsrName");
				if (window.foo) {
					window.foo = false;
				} else {
					//window.foo = true;
					this.set("UsrName", n + window.foo);
					window.foo = true;
				}
			}
		},
...

Я решил, проблему "отложенным вызовом на 100 мс." смены атрибута который приводит к вызову обработчика в рекурсии

...
setTimeout(function(){
     this.set("TargetColumn", new Date())
}.bind(this), 100);
...

т.е. как только выполнение обработчика завершается корректно - повторный вызов из отложенной функции уже корректно ловит "флаг" и его обрабатывает.

PS:
создается такое впечатление что срабатывание обработчиков - псевдосинхронное, т.е. this.set банально "стакает" текущий контекст, и функция вызывается повторно, не завершившись в предыдущий раз.

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

7.10
Для примера возьмем Lead
Создаем в схеме карточки (LeadPageV2)

                attributes: {
                        "firstAttr": {
                                type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
                                dataValueType: Terrasoft.DataValueType.BOOLEAN,
                                value: false
                        },
                        "secondAttr": {
                                type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
                                dataValueType: Terrasoft.DataValueType.BOOLEAN,
                                value: false
                        },
                        "depAttr": {
                                "dependencies": [
                                        {
                                                "columns": ["secondAttr", "firstAttr"],
                                                "methodName": "depMethod"
                                        }
                                ],
                                type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
                                dataValueType: Terrasoft.DataValueType.BOOLEAN,
                                value: false
                        }
                },
                methods: {
                        "onEntityInitialized": function() {
                                document.LeadPageScope = this;
                                this.callParent(arguments);
                         },
                        "depMethod": function() {
                               alert("Dep Trigged!!!")
                         }
                }

Создаем в схеме секции (LeadSectionV2) почти идентично
                attributes: {
                        "firstAttr": {
                                type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
                                dataValueType: Terrasoft.DataValueType.BOOLEAN,
                                value: false
                        },
                        "secondAttr": {
                                type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
                                dataValueType: Terrasoft.DataValueType.BOOLEAN,
                                value: false
                        },
                        "depAttr": {
                                "dependencies": [
                                        {
                                                "columns": ["secondAttr", "firstAttr"],
                                                "methodName": "depMethod"
                                        }
                                ],
                                type: Terrasoft.ViewModelColumnType.VIRTUAL_COLUMN,
                                dataValueType: Terrasoft.DataValueType.BOOLEAN,
                                value: false
                        }
                },
                methods: {
                        "init": function() {
                                document.LeadSectionScope = this;
                                this.callParent(arguments);
                         },
                        "depMethod": function() {
                               alert("Dep Trigged!!!")
                         }
                }

Открываем карточку Лида в Combined режиме,
Устанавливаем точки останова внутри метода depMethod в карточке и секции соответственно,
Открываем консоль, поехали

document.LeadSectionScope.set("firstAttr", true);
//НИЧЕГО НЕ ПРОИСХОДИТ !
//Исполнение в метод depMethod секции не переходит.
document.LeadPageScope.set("firstAttr", true);
//ОТЛАДЧИК ВСТАЕТ ВНУТРИ МЕТОДА depMethod карточки !

И как это понимать ?
Двойные стандарты для функциональности атрибутов в отношении зависимостей ?

Нравится

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

Здравствуйте, Илья.

Зависимости атрибутов представляют собой зависимости между колонками конкретной записи. При изменении значения колонки в карточке редактирования срабатывает зависимость.
Раздел в свою очередь работает с коллекцией записей. Нет конкретной колонки, изменения которой должно вызвать срабатываение зависимости.

Ну в рамках карточки мы не обязательно привязаны к колонкам,
мы можем просто создать атрибуты (свои, пользовательские не связанные с колонками - что собственно и показано в примере) и на их изменение - зависимости выраженные в вызове методов текущего контекста.
Почему то же самое с атрибутами не доступно в рамках карточки секции ?
Возможно есть иной способ привязки логики к изменению атрибута ?
Например биндинг value на метод:
Что в таком случае будет происходить -
При установке значения в аттрибут - каждый раз будет вызываться забинженный метод - в аргументы которого будет поступать устанавливаемое значение, а его return значение - будет установлено в качестве текущего value ?

Потому что applyDependencies из BusinessRulesApplierV2 работает только для BasePageV2 и её наследников. По сути это сахар над обычным бекбоновским this.on("change: ...
так что в секции можете в ините просто написать:
this.on("change:MyAttrColumn", this.myMethod, this);
где MyAttrColumn имя атрибута\колонки для которой будет тригерится по изменению метод myMethod описанный в блоке методов.

"Максим Шевченко" написал:По сути это сахар над обычным бекбоновским this.on("change

ну вот именно это я и подразумевал :)
Спасибо что уточнили.
А можно подключить applyDependencies из BusinessRulesApplierV2 в качестве зависимостей схемы секции - в таком случае тоже будет работать ?
Просто интересует однообразие.

Прям её не подключить, все же в базовой реализации этого метода анализируется атрибут IsEntityInitialized которого нет в секции. Но, вы можете попробовать написать свой миксин который будет пробегать по атрибутам, искать у них свойство "зависимости" и делать в этом форе this.on на указанные методы.

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

Очень много оверхеда по коду можно было бы избежать имея возможность передавать аргументы в методы обработчики зависимостей аттрибута.
Например у вас есть 10-ть полей отражающих сущность стоимости "без НДС" и соответвующие им поля "с НДС", вместо того чтобы писать 10-ть методов обработчиков, можно было бы написать один, но передавать в него целевые поля или их имена.
Я пролез по исходникам и нашел занятное поле конфигурационного объекта "argument

                        "SomeFiledwithVAT": {
                                "dependencies": [
                                        {
                                                "columns": ["SomeFiledwithoutVAT"],
                                                "methodName": "VAT_calculate",
                                                "argument": "SomeValue"
                                        }
                                ]
                        }

Таким образом в первый аргумент при вызове метода обработчика попадает "SomeValue"

Далее вопроса два:

1) Как передавать множественные аргументы ? (предусмотрено ли нечто такое ?)
Я естественно проверил "arguments"

                        "SomeFiledwithVAT": {
                                "dependencies": [
                                        {
                                                "columns": ["SomeFiledwithoutVAT"],
                                                "methodName": "VAT_calculate",
                                                "arguments": [null, 1, "SomeText"]
                                        }
                                ]
                        }

но меня ждало фиаско...
тем не менее можно в сам "argument" передавать массив, просто обогатив логику метода сплитом - добиться необходимого.
Хотя кончено это не так эстетично, как если бы можно было
"arguments": [null, 1, "SomeText"]

но тут возникает второй вопрос:

2) Уж больно редко сей аттрибут используется в исходных кодах, есть предположение, что это "костыль" и его могут в любой момент убрать.
Т.е. использовать его при разработке своей логики - авантюризм,
кто ни будь знает это часть нормального долгоживущего функционала или это "костыль" ?

Нравится

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

в метод-обработчик передается
1) то, что в кофинге в свойстве argument
2) название измененной колонки




эта штука там уже с незапамятных времен и вряд ли куда-то денется

"Андросов Дмитрий" написал:

https://learn.javascript.ru/arguments-pseudoarray


Зачем вы меня отправляете читать про псевдомассив arguments ? :biggrin:
В данном случае arguments, я по аналогии с argument - это атрибут анонимного объекта - элемента массива dependencies
атрибуты объекта вольны называться как хочется, хоть ключевыми словами.

var x = {
	var: "var keyword",
	arguments: "pseudo array",
	function: "function keyword"
}
//любуемся
console.log(x)

Псевдомассив аргументов при вызове метода обработчика собирается следующим образом:
первый аргумент: значение атрибута "argument" установленное в dependencies объекте.
второй аргумент: Имя аттрибута текущей модели (поля) по которому сработала зависимость.

Вот вопрос касался как раз "argument"

"Севостьянов Илья Сергеевич" написал:Вот вопрос касался как раз "argument"

arguments нет, как вы могли уже увидеть по исходникам.
Используйте argument, передавая туда массив или объект, и учите ваш метод работать с этим первым аргументом, разбирая его на нужные вам значения.
Логика зависимостей вряд ли поменяется в ближайшее время.

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

Добрый день коллеги!

Глобально, задача состояла в следующем - сделать редактируемый грид, и я руководствовался примером из этой темы
https://community.terrasoft.ua/forum/topic/13667

Но на этапе добаления модулей в dependencies столкнулся с проблемой добавления ConfigurationGridUtilities. При добавлении модулей из списка поиск происходит по заголовку, а в модуле ConfigurationGridUtilities заголовок отсутствует.

Подскажите пожалуйста, как можно обойти эту проблему?
версия конфигурации - 7.7

Нравится

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

Здравствуйте, Влад.

Для создания редактированного грида достаточно описать зависимости в define. Добавить ConfigurationGridUtilities в dependencies Вы не сможете (да это и не нужно). Так же рекомендую Вам создавать деталь через "Мастер деталей", а затем вносить изменения, поскольку добавлять фильтрацию, а так же бизнес правила для детали с редактируемым реестром возможно только на странице детали.

Спасибо. Подскажите пожалуйста еще на один вопрос:
Можно ли сделать редакрируемым грид, в котором в качестве значения колонки могут быть данные разного типа, как например у детали "Деталь характеристики продукта"?

В детали «характеристики продукта», в значение колонки не заносится разные типы значений. Там на самом деле в объекте есть колонки разных типов, и в зависимости от условий, показывается та или иная колонка, то числовая, то строковая, то лукапная. А для отображения в гриде всегда выводится ее текстовое значение. Которое программно высчитывается при изменении фактических колонок хранения.
В редактируемом же гриде вам придётся вывести все эти типы колонок, иначе вы их просто не заполните. Можно разве что в зависимости от «условия» влиять на их блокировку на изменение. Посредством написания бизнес правил в «странице редактирования» детали.
Т.к. редактируемый реестр детали, часть логики, ответственную за конкретную запись, может обрабатывать только при наличии страницы редактирования. Даже если она не открыта, бизнес правила из страницы редактирования будут отрабатывать для строк редактируемого реестра.

В общем, если вам нужна логика как в «характеристике продукта», стоит все же использовать классическую деталь, а не с редактируемым реестром.

Спасибо!

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