Биндиг (bindTo) свойства поля "visible" на метод.

Версия приложения 7.9.1
Юзкес: привязать свойство справочного поля "visible" некой карточки, к некой бизнес-логике.
Пердпринятые действия
При помощи мастера раздела, и редактора страницы на страницу контакта было добавлено новое справочное поле, н/п в карточке контакта добавлено поле "Страна" (ContactCountry)
см.вложение country.png
Далее в сгенерированной замещающей схеме с группе свойств конфигурационного объекта для поля был добавлен биндинг свойства "visible" к методу "ContactCountryVisibilityController"
и объявлен сам метод.

...
        {
                "operation": "insert",
                "name": "ContactCountrye97c8637-0ade-42fc-95e1-a37e1971b25e",
                "values": {
                        "layout": {
                                "colSpan": 12,
                                "rowSpan": 1,
                                "column": 12,
                                "row": 2,
                                "layoutName": "ContactGeneralInfoBlock"
                        },
                        "labelConfig": {},
                        "enabled": true,
                        "contentType": 5,
                        "bindTo": "ContactCountry",
                        "visible": {"bindTo": "ContactCountryVisibilityController"}
                },
                "parentName": "ContactGeneralInfoBlock",
                "propertyName": "items",
                "index": 4
        },
...

                methods: {
                        ContactCountryVisibilityController: function() {
                                if (this.get("ContactCountry") !== undefined) {
                                        return true;
                                } else {
                                        return false;
                                }
                        }
                }

Проблема:
во-первых - если поставить точку останова внутри объявленного выше метода, то код метода при загрузке страницы выполняется 6-10 раз !!!
во-вторых - во время отладки, даже если метод возвращает true (в случае когда целевое поле заполнено), то свойство всё равно не принимает необходимого значения.
(поле в любом случае остается скрытым)
Т.е. биндинг не приносит желаемого результата.

Протестировано как в on-site версии, так и в облачной демо-версии.

Нравится

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

Здравствуйте!

Задача решается использованием бизнес-правил. Подробнее:
https://academy.terrasoft.ru/documents/technic-sdk/7-9/pravilo-bindpara…

Дело не том как можно.
Я изначально понимаю что есть три пути:

  1. 1. биндинг на атрибут
    (И дальнейшее управление значением атрибута любой бизнес-логикой имеющей к нему прямой или опосредованный, через события, доступ)
  2. 2. биндинг на метод
    (Идеальный вариант если логика нужна лишь на этапе инициализации, или если используется функционал, который надо выполнить автоматически но он так-же является полезной нагрузкой какого либо другого элемента)
  3. 3. использование конфигурационного объекта бизнес-правил
  4. (Идеальный вариант для организации сложной логики по валидации и зависимости значений и полей друг от друга на этапе заполнения данных пользователем)

У каждого из подходов есть своя ниша - где его применение наиболее эффективно, просто и т.д.

Дело в том, что один из этих способов - ведет себя не предсказуемо, и не очевидно некорректно!
Вопрос в конечном итоге сводится к плоскости: "Или я дурак, или лыжи не едут ?"
Ну и собственно ежели первое - то хочется учиться у сведующих уму разуму, если второе то чтобы специалисты ТП обратили на нее внимание.

Уточните, пожалуйста, метод ContactCountryVisibilityController вызывается в методе onEntityInitialized?

он "забинжен" на свойство "visible" поля
т.е. должен вызываться при его инициализации

Бог с ней с моей самописной логикой, давайте возьмем базовую поставку

Возьмем к примеру схему BaseProfileSchema (пакет NUI - фактически ядро системы)
Где у нас имеется объявленный метод "getVisibleContent"

Возьмем карточку контрагента и воспользуемся поиском по исходным кодам, чтобы получить представление о количестве биндингов на данный метод или количество его прямых вызовов, возможного абстрагирования в других переменных и т.д. (см.вложение getVisibleContent_UsagesInContragentPage.png).
Оба биндинга принадлежат к той-же схеме "BaseProfileSchema" - это объекты "ProfileContentContainer" и "ProfileModuleActions", их свойство "visible"

Итого
во-первых: установив точку останова внутри метода getVisibleContent - во время загрузки страницы получаем 4-6 вызовов вместо положенных двух.
во-вторых: если на первом вхождении точки останова подменить вызываемую функцию, скорректировав возвращаемое ею значение
т.к. исходный код метода:
getVisibleContent: function() {
return !this.getVisibleBlankSlate();
},
Во время штатного выполнения кода getVisibleContent - возвращает "false" (т.е. забинженное на метод свойство "visible" должно устанавливаться в значение false, тем не менее элементы страницы "Добавить контакт" и "Выбрать" как раз таки отображаются см.вложение BasicFunctionality_ReturnFalse.png)
Если же мы подменим функцию this.getVisibleBlankSlate() таким образом чтобы метод getVisibleContent возвращал "true"
н/п this.getVisibleBlankSlate = function(){return false};
То несмотря на то, что метод начинает возвращать "true" - элементы как раз таки скрываются. см.вложение BasicFunctionality_ReturnTrue.png
Т.е. все с точностью да наоборот от ожидаемого поведения !
Свойство visible принимающее значение true (элемент показывается) значение false (элемент скрывается), по крайней мере если не биндить поле а указывать значение прим в описании схемы - работает по такому принципу.

Никакой разработки НОВОЙ функциональности - Это все штатные системные коды входящие в базовую поставку и их поведение в облачной демо версии!
Прошу Вас объяснить что в данном случае происходит:
1) Почему метод вызывается большее количество раз чем к нему идет обращений/биндингов ?
2) Почему реальное значение получаемое параметром - инвертировано от того что возвращается методом ?

Добрый день!
Вы немного недосмотрели.
Итак, базовая логика. Есть 2 элемента , у которых visible забайнден на getVisibleContent:

{
      "operation": "insert",
      "name": "ProfileModuleActions",
      "parentName": "ProfileModuleContainer",
      "propertyName": "items",
      "values": {
		...
		"visible": {"bindTo": "getVisibleContent"},
		...
}
{
        "operation": "insert",
	"name": "ProfileContentContainer",
	"parentName": "ProfileModuleContainer",
	"propertyName": "items",
	"values": {
		...
		"visible": {"bindTo": "getVisibleContent"},
                ...
}

Обратим внимание на свойтво name элементов: ProfileModuleActions и ProfileContentContainer
Сам метод работает так как вы и описывали. Теперь если посмотреть на клиенте в HTML коде эти элементы, то увидим, что стиль dispaly: ... соответствует значению visible элементов

То что вы на скрине показали, что элементы видны, то мне кажется, что вы обратили внимание не на свойство "name", а на свойство "parentName"

Теперь что касается нескольких входов в функцию. Всегда будет 2 входа: 1 - это при создании элемента управления происходит инициализация начального значения. 2-й - это при изменении зависимого значения, в данном случае это значение аттрибута модели, который учавствует в функции (ях), на которые произведен bind

см. https://004531-sales-enterprise.bpmonline.com
Админ
Админ
пакет Custom замещающая схема ContactPageV2 - поле UsrCity

Очевидная проблема функционала.
Функция (метод) "забинженная" на свойство поля - вызывается на исполнение 6-ть и более раз,
после чего значение свойства все равно не устанавливается в соответствии с возвращаемым ею значением.

PS: Это как раз тема моего изначального обращения (меня потом начали заворачивать по SLA пришлось искать примеры в базовых кодах)

Добрый день.
На вашей демо-версии всегдя возвращается false. Если изменить код и вставить прямо "return true", то все работает. В вашей схеме вообще нет аттрибута ContactCity, поэтому false всегда. Также, после того как вы изменили код необходимо убедится, что он обновился на клиенте, а не вычитался из кэша. Данный работает корректно, т.к. по всей системе были бы ошибки

Касательно 6 раз - это нужно исследовать, и это уже отдельная история. Именно с темой "метод вызывается 6 раз" вы можете обратится в службу поддержки

Артем, демку разворачивал в "попыхах" действительно допустил опечатку в методе.
Но сути дела это не меняет, я исправил ошибку.


Метод проверяет не атрибут, а поле - его заполненность.
Вот пример 2 контакта у одного поле "Город" (UsrCity) заполнено у другого - нет.

Установим точку останова в методе CityVisibleController
Заходим в карточку контакта - поле которого заполнено:

Мало того что метод вызывается 6-ть раз, смотрим на логику, метод возвращает "true", поле "Город"
в карточке показывается - пока что кажется что все идет нормально, но давайте зайдем в карточку контакта у которого это поле не заполнено:
И ТУТ ВНИМАНИЕ !!!
Браузер Mozilla Firefox

this.get("UsrCity") почему-то возвращает не undefined а пустую строку ""
Браузер Google Chrome

В том же самом месте - возвращает UNDEFINED! (Как и ожидалось)
Какого лешего ? извините за выражение.
И даже в Google Chrome где this.get("UsrCity") возвращает undefined и моя логика срабатывает, т.е. методо возвращает FALSE
ВСЕ РАВНО ПОЛЕ UsrCity остается видимым !

Итак.
Поле из БД приходит нам в схему и ложится в аттрибут. Таким образом чтение/запись аттрибутов и полей одинаковая (this.get() / this.set())

Теперь о undefined и ''.
Мы знаем, что в БД не может хранится значение undefined, поэтому это сравнение некорректное. Оно может быть undefined только тогда, когда вы создаете новую запись, у вас создается модель, которая не содержит значений аттрибутов. Т.е., если для новой записи написать this.get('поле'), то значение будет undefined (если конечно же кто-то в фоне не установил этот аттрибут). После значения всех атррибутов, которые являются "колонками/полями" записывается в БД.
В нашем приложении для полей типа "текст" нельзя записывать в БД значения null (not null) и записывается значение по умолчанию - пустая строка. Поэтому, во время редактирования какой-то записи вам вычитается пустая строка и это значение ляжет в аттрибут. Отсюда у вас и разногласия в значениях

Хорошо, мы пробовали изменить проверку на более корректную, как нам казалось
Т.к. в примерах с сайта академии мы видели проверку поля на пустоту специальным методом this.Ext.isEmpty
т.е. буквально переписали метод следующим образом:

CityVisibleController: function() {
	var UserCity = this.get("UsrCity");
		if (this.Ext.isEmpty("UsrCity")) {
			return false;
		} else {
			return true;
		}
	}

Опять же во время отладки, чтобы не содержалось в this.get("UsrCity") - undefined или "" или поле заполнено и там возвращается объект - вердикт метода всегда FALSE (т.е. на undefined и на "" тоже, это как-бы получается "не пустое" поле а какое-же оно ?)

PS: В конечном итоге добились требуемой функциональности вот так:

CityVisibleController: function() {
	var UserCity = this.get("UsrCity");
	if (UserCity !== undefined && UserCity !== "" && UserCity !== 0) {
		return true;
	} else {
		return false;
	}
}

Т.е. проверяя на все подряд, и даже на всякий случай на 0
А так же выяснили что во время 12-ти вызовом метода, в первых 6-ти - this.get("UsrCity") - undefined
А в последних 4-х уже возвращает пустую строку "" (и вот видимо во время одного из таких вызовов и происходит биндинг)

Так что вопрос ПОЧЕМУ метод вызывается 12-ть раз остается открытым.
Вот еще вопрос, юзкейс: свойство биндится на какую-то тяжелую логику включающую в себя ESQ запросы в БД - получается они будут выполнены 12-ть раз ?!
Это баг или фича ? :)

В поддержку даже нет смысла обращаться - будет отклонен по SLA как "пить дать".

Добрый день!
Певрое. Так как это JS, то вы можете использовать следующее выражание - return !!!this.get("UsrCity"),
так как !! от undefined и пустой строки вернет false.
Второе. Касательно 12 раз. Еще раз, каждый аттрибут инициализируется с каким-то значением, потом начинается цепочка зависимостей, в ходе которых происходят еще вызовы этого метода. Вы это можете увидеть в call stack'е.
Третье. Касательно тяжелого ESQ. Вы немного не разобрались. Составление ESQ - синхронная "операция", а получение данных - асинхронная. У вас просто не получится внутрь bind'а "впихнуть" асинхронность. Обычно все байнды завязываются на значения аттрибутов, их комбинаций, клиентской логики, а значение самиъх этих аттрибутов может быть установленно в callback'е любой логики

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