Javascript-форум (https://javascript.ru/forum/)
-   ExtJS (https://javascript.ru/forum/extjs/)
-   -   Маска для элементов TabPanel (https://javascript.ru/forum/extjs/66401-maska-dlya-ehlementov-tabpanel.html)

Sogl 13.12.2016 08:01

Маска для элементов TabPanel
 
Всем привет!

Есть панель с вкладками, в каждой вкладке находится по таблице. Пытаюсь сделать маску на каждый item-таблицу, получилось только через событие afterrender, т к у GridPanel нет в конфигах маски:
defaults: {
    listeners: {
        afterrender: function(grid) {
            grid.mask();
        }
    }
},


Однако есть интересная проблема. Т к у нас activeTab может быть только один, то и срабатывает событие лишь на активной вкладке. Переключаем вкладку — опять срабатывает на новой вкладке.
Мне же нужно, чтобы маска применилась на всех элементах разом и при загрузке.

У самой TabPanel нет события afterrender, а с added и перебором в цикле элементов-табов маска не применяется и вываливается с ошибкой:
TypeError: target is undefined


Так как же правильно применить маску?

Infarch 13.12.2016 13:54

Правильно применить маску - это не применять маску. Да, именно так. Когда стор грузится, грид автоматически маскируется. Ну и не понятно насчет маскировки одновременно во всех табах. Зачем? Ведь активен, и соответственно виден только один таб за раз. Что происходит в остальных и так никто не узнает. А когда таб активизировался то и делайте в нем все что надо.
Или я что-то недопонял?

Sogl 14.12.2016 02:59

Объясняю. У меня две таблицы с ассоцияциями (FK), при нажатии на строку 1й таблицы грузятся данные с фильтром по id этой записи во 2й таблице.
Однако по умолчанию 2я таблица при загрузке активна и если мы нажмем на кнопку обновления в pagingtoolbar'е, то получим ВСЕ записи, связанные и не связанные. К тому же, кнопки toptoolbar'а также все активны...

Чтобы избежать ненужных нажатий пользователем и несвязанных данных, я маскирую таблицы. Можно, конечно, отключить саму TabPanel, но это выглядит менее эстетично, чем маска на Grid'ах. Да и вкладки остаются видимы и доступны с маской.

Infarch 14.12.2016 10:58

Стало понятнее ) Мне кажется что маска тут неправильное решение. Поставьте подчиненному гриду disabled. Будут заблокированы тулбары и пейджер.

Sogl 15.12.2016 01:38

Как ни странно, решение с отключением самой таблицы делает неактивными и сами вкладки TabPanel. В итоге имеем идентичный вид, как если бы мы просто отключили сам TabPanel (монолитно серая отключенная панель).

Infarch 15.12.2016 11:01

Вот же хитрая бестия ) Ну попробуйте панель в панели: одна панель как сам таб, а в ней - гридпанель. Думаю должно помочь.

nohuhu 15.12.2016 22:14

Честно сказать, глубоко не вчитывался, многабукаф. Fiddle было бы существенно нагляднее.

Однако имею сказать вот что:

а) Не используйте afterrender в Classic toolkit ни за что и никогда. От слова "вообще". Это событие стреляет сразу после добавления свежесозданных элементов в DOM, до раскладки. Делать что-либо с DOM в этот момент - самый лучший путь к "тяжёлым и тормозным приложениям Ext JS". Используйте событие boxready, оно стреляет после раскладки.*

б) Есть мнение, что конфиг maskElement может оказаться интересным.

ц) По умолчанию карты в Tab panel рендерятся поодиночке после активации, сделано это экономии ради и производительности для. Конфиг deferredRender позволяет управлять этим поведением.

* Кроме компонентов, использующих браузерную раскладку (liquidLayout: true). Таких компонентов в Classic Toolkit немного: кнопки и поля ввода, кроме текстовых полей с grow: true. Если liquidLayout == true, то событие boxready не стреляет и придётся использовать afterrender. Но лучше не надо, потому что принудительные браузерные раскладки убьют всю производительность на корню.

Sogl 16.12.2016 08:00

nohuhu, Спасибо за разъяснения.

В общем, я заморочился и сделал Fiddle (1й раз в нем работал):
https://fiddle.sencha.com/#fiddle/1mls

По умолчанию оба зависимых Grid'а маскируются по событию boxready, но вкладки при этом доступны. По событию rowclick главного Grid маска снимается, но работает это только для активной вкладки!

defferedRender: false мне не помог (в фидле есть эта строчка), а с maskElement не понял как работать.

nohuhu 16.12.2016 21:37

Fiddle вижу, спасибо. А как должно работать-то? При решении любой задачи всегда полезно знать ответ. :)

Sogl 20.12.2016 06:13

Ну вообще желательно такое поведение:
1. Маска накладывается на ВСЕ элементы TabPanel один раз.
2. При выборе строки 1го Grid все маски снимаются.

Как сейчас происходит:
1. По событию boxready каждый раз накладывается маска на АКТИВНЫЙ элемент TabPanel.
2. При выборе строки 1го Grid снимается маска ТОЛЬКО У АКТИВНОГО элемента (событие rowclick), а при переключении на 2-ю вкладку маска остается, т к срабатывает boxready (п. 1).

Sogl 26.12.2016 07:10

nohuhu, Посмотрите?

nohuhu 09.01.2017 23:29

Sogl,

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

Если я правильно понял ваше последнее сообщение, то вам нужно закрыть маской всю TabPanel изначально, до момента, пока не выбрана запись в Grid с левой стороны. В таком случае, почему бы не использовать привязку (binding) к выбранной записи в 1-м Grid, точно так же, как вы делаете во втором Grid? У каждого компонента есть метод setLoading, так что можно сделать вот такую привязку:

bind: {
    loading: '{!customerGrid.selection.orders}'
}


Это не решит вопрос с изначальным маскированием TabPanel, т.к. bindings стреляют только в момент обновления. Для этого как раз подойдёт событие boxready на самой TabPanel:

listeners: {
    boxready: function(panel) {
        panel.setLoading({ useMsg: false });
    }
}


Я подправил и сохранил ваш fiddle, можете посмотреть вживую. Если что-то работает не так, как надо, уточните задачу.

Sogl 10.01.2017 01:32

Не поздно =)

Цитата:

Сообщение от nohuhu
Если я правильно понял ваше последнее сообщение, то вам нужно закрыть маской всю TabPanel изначально, до момента, пока не выбрана запись в Grid с левой стороны.

Не так. Должны маскироваться элементы items этой TabPanel, а не вся панель. Таким образом остается возможность пользоваться вкладками, но поменять в них ничего нельзя, пока не нажмешь на строку 1-й Grid.

Попробовал перенести ваш код в секцию defaults, но маска с элементов так и не снимается.

Еще так и не понял как работает вот такая запись и почему в ней стоит оператор логического НЕ:
bind: {
    loading: '{!customerGrid.selection.orders}'
}


p.s. Кстати, Sencha Fiddle не показывает внесенные сторонние изменения в созданных fiddle? А то так сложно понять что было изменено.

nohuhu 12.01.2017 22:25

Цитата:

Сообщение от Sogl (Сообщение 440178)
Не так. Должны маскироваться элементы items этой TabPanel, а не вся панель. Таким образом остается возможность пользоваться вкладками, но поменять в них ничего нельзя, пока не нажмешь на строку 1-й Grid.

В таком случае надо использовать maskElement: 'body', чтобы маска накладывалась на элемент body, а не основной el. См. fiddle.

Цитата:

Еще так и не понял как работает вот такая запись и почему в ней стоит оператор логического НЕ:
bind: {
    loading: '{!customerGrid.selection.orders}'
}

Это частично ошибочная строка, я второпях зачем-то использовал ассоциацию не на выбранную запись в customerGrid, а на данные в этой записи. Прошу прощения за конфуз. :) Должно выглядеть вот так:

bind: {
    loading: '{!customerGrid.selection}'
}


Ассоциация customerGrid.selection публикуется компонентом customerGrid и содержит выделенную запись в customerGrid. С левой стороны у нас название "config", который нужно изменить, когда меняется ассоциация. "Config" в кавычках, потому что на самом деле у компонентов нет опции loading, которую можно было бы сконфигурировать, но есть метод setLoading, который можно вызвать. Binding всё равно, конфиг это или нет, оно дёрнет метод и скормит ему данные из ассоциации, когда эти данные изменяются.

Дальше к оператору. Когда выделенных записей нет, нужна маска. Когда запись есть, маска не нужна - обратная логическая связь. Когда выделение есть, свойство customerGrid.selection содержит объект с выделенной записью. Когда выделения нет, это свойство равно null. Использованием логического НЕ мы убиваем двух зайцев: во-первых, инвертируем логическую связь в нужную нам сторону (есть запись - нет маски, и наоборот); во-вторых, приводим truthy объект "запись" к булевому значению true, или null к булевому false. Если бы нам нужна была прямая связь, то я бы использовал '{!!customerGrid.selection}', просто чтобы в метод setLoading передавалось всегда булевое значение, а не запись. Меньше потенциальных поводов для головной боли.

Цитата:

p.s. Кстати, Sencha Fiddle не показывает внесенные сторонние изменения в созданных fiddle? А то так сложно понять что было изменено.
Нет, такое Fiddle пока не умеет.

Sogl 13.01.2017 03:36

nohuhu, Огромное спасибо! :thanks:

Теперь все работает как надо. Изначально я пытался маскировать каждый item в TabPanel и снимать маску со всех них по rowclick, а оказалось, что проще всего выставить maskElement: 'body' и не нужны никакие лишние телодвижения. Красота.

Есть один непонятный момент.

Как я понял из вашего описания и доки, вот эта запись:
listeners: {
    boxready: function(panel) {
        panel.setLoading({ useMsg: false });
    }
},

выставляет маску без сообщения на ней (т к useMsg: false) перед загрузкой контейнера и после раскладки элементов DOM. Вы выше писали, что bind срабатывает только на обновление.

По идее, без данного кода маска примениться не должна. Однако, без него вечно отображаться "Loading..." пока не кликнем на строку Grid, т е каким-то образом стандартная маска появляется. Почему?

Фидл с этим моментом:
https://fiddle.sencha.com/#fiddle/1mls

Комментирую код c bind, возвращая boxready на место — все ОК, маска есть. Без этих 2х кусков кода никаких масок, что и должно быть.

nohuhu 13.01.2017 22:34

"Loading..." это сообщение по умолчанию, если вызвать просто setLoading(true). В какой-то момент стреляет binding и, поскольку выделенной записи нет, накладывает маску с сообщением. Мне показалось, что это выглядит довольно странно, поэтому я добавил обработчик boxready, чтобы снова установить маску, но уже без сообщения.

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

А если подумать, такая конфигурация была бы хорошей идеей. EXTJS-23472. :)


Часовой пояс GMT +3, время: 12:38.