Я создал более сложную тему user-card-contents, добавив несколько пользовательских полей пользователя. Я хотел бы сделать карточку «липкой» с кнопкой закрытия — аналогично компоненту «Создать новую тему».
Правильно ли я понимаю, что мне нужно будет переопределить user-card-contents.js, чтобы предотвратить вызов закрытия элемента? Смогу ли я упаковать это внутри темы?
Короткий ответ — да. Если ваши изменения затрагивают только фронтенд, то их можно реализовать в теме или компоненте.
Не могли бы вы подробнее объяснить, что вы имеете в виду под «липким» (sticky)? Если вы хотите, чтобы элемент прокручивался вместе с контентом, оставаясь на месте, то это изменение в CSS. Также, планируется ли внедрение этого изменения как для десктопа, так и для мобильных устройств?
Не могли бы вы уточнить, какой именно вызов вы имеете в виду? (например, по клику, при прокрутке и т. д.)
Разметку кнопки закрытия нужно добавить в шаблон Handlebars для карточки пользователя. Логика обработки действия при клике на кнопку должна быть добавлена в файл компонента .js.
Полное переопределение может и не потребоваться: существуют специальные хуки, с помощью которых можно переопределять конкретные методы в классе. Я могу рассказать об этом подробнее, если вы чуть подробнее опишете, что именно хотите сделать.
Я хочу предотвратить закрытие карточки по клику вне её области в десктопной версии, используя обработчик событий clickOutsideEventName из миксина card-contents-base.js. Вместо этого я хотел бы заставить пользователей закрывать карточку, нажимая на кнопку. Для мобильных устройств, скорее всего, потребуется другое решение.
Мне удалось заставить работать этот шаблон Handlebars, теперь нужно разобраться с .js
Скорее всего, вы уже знаете большую часть этого, раз уже работали над своей темой, но я постараюсь изложить всё чуть подробнее для более широкой аудитории.
Первое, что я бы сделал, — это поискать либо локально, либо на Github. На Github вы получите что-то вроде этого. В большинстве случаев поисковый запрос даст более одного результата, и вам либо придётся уточнить запрос, либо вручную просмотреть результаты, чтобы найти что-то близкое к тому, что нужно.
Этот файл представляет собой миксин. Почему я об этом упоминаю? Потому что нужно помнить, что миксины могут использоваться в самых разных местах. В данном случае он применяется как для карточек пользователей, так и для карточек групп. Следовательно, внесённые вами изменения затронут оба типа.
Если вы поищете в файле, то обнаружите, что clickOutsideEventName впервые определяется здесь:
Ember гарантирует, что к моменту вызова didInsertElement():
Элемент компонента был создан и добавлен в DOM.
Элемент компонента доступен через свойство this.element компонента.
Зачем нам это нужно? Потому что нам нужен отдельный обработчик события mousedown для карточек пользователей и карточек групп. Если вернуться чуть назад, вы заметите, что в clickOutsideEventName используется идентификатор элемента:
Что, как мы обсуждали выше, затем передаётся как свойство.
Теперь давайте перейдём к тому, как всё это связано с тем, что вы делаете.
Вы пытаетесь предотвратить закрытие карточек при клике пользователя вне их области. Давайте попробуем найти способ сделать это. Если вы помните, мы обсуждали, что clickOutsideEventName в конечном итоге используется здесь:
Короче говоря, это добавляет обработчик события mousedown к элементу HTML при вставке карточки (при первом просмотре страницы). Затем мы проверяем цель события mousedown. Если цель находится внутри карточки, мы прерываем действие. Если она вне карточки, мы закрываем её, вызывая this._close().
И это то, что вам нужно будет вызывать при добавлении кнопки закрытия — но мы вернёмся к этому позже.
Теперь цель состоит в том, чтобы удалить этот обработчик mousedown, если вы хотите, чтобы клики вне карточки не закрывали её. Как это сделать? Нам нужно будет модифицировать didInsertElement(), и вот как это можно сделать.
Темы Discourse имеют доступ к API плагинов, который содержит множество методов, которые вы можете использовать. Более подробная информация о наиболее часто используемых из них доступна здесь.
Если вы помните, файл, с которым мы работаем, — это миксин. Миксин — это класс Ember. Следовательно, метод, который мы будем использовать, — это modifyClass:
При использовании modifyClass вы можете добавить, изменить или полностью переопределить метод класса. Мы сосредоточимся на изменении метода, так как именно это вам нужно сделать.
Мы хотим изменить didInsertElement(), поэтому можем сделать что-то вроде этого:
Почему так? Оказывается, метод modifyClass в настоящее время не поддерживает изменение миксинов (насколько я проверял). Я отмечу это, чтобы разобраться, почему так происходит, и проверить, можно ли это исправить. Но пока давайте вернёмся к тому, что вы хотите сделать.
Что ж, мы не можем изменять миксины, значит, мы застряли, да? Нет. Давайте копнём чуть глубже.
Как я уже упоминал, этот миксин используется как для карточки пользователя user-card-contents, так и для карточки группы group-card-contents — компонентов Ember (снова потому, что миксины созданы для переиспользования кода).
Итак, давайте посмотрим на компонент user-card-contents здесь:
Если внимательно прочитать, можно заметить, что мы сначала импортируем обсуждаемый выше миксин здесь,
а затем создаём новый компонент Ember и передаём ему миксин.
Что это значит? Это означает, что didInsertElement() для user-card-contents фактически наследуется из миксина. То же самое можно сказать и о group-card-contents.
Куда это нас приводит? Ну, есть хорошие и плохие новости. Хорошая новость в том, что если вы хотите внести изменения в user-card-contents, не затрагивая group-card-contents, то вы можете это сделать! Плохая новость в том, что если вы хотите, чтобы ваши изменения применялись к обоим, вам придётся продублировать часть кода.
Вернёмся к modifyClass и попробуем снова с user-card-contents. Что-то вроде этого:
Изменение зарегистрировано, и мы видим его в консоли, но мы ещё не совсем там.
Итак, теперь, когда мы можем изменять didInsertElement(), давайте вернёмся к тому, что вы пытаетесь сделать. Если вы помните, обработчик mousedown определяется в didInsertElement здесь:
вы получите что-то сломанное. Почему? Потому что это не изменение метода. Это полное переопределение основного метода didInsertElement() для этого компонента. Следовательно, ни один из кода ядра фактически не применяется, если вы делаете так.
Как это исправить? Оказывается, в Ember есть такая вещь, как this._super(...arguments).
Что это делает? Это позволяет добавлять код до или после того, что уже есть в методе класса. Например, если вы сделаете это:
api.modifyClass('component:user-card-contents', {
didInsertElement() {
// код, который вы хотите добавить
$("html").off(clickOutsideEventName);
// код из ядра
this._super(...arguments);
}
});
то код, который вы хотите добавить, выполнится раньше всего остального здесь:
api.modifyClass('component:user-card-contents', {
didInsertElement() {
// код из ядра
this._super(...arguments);
// код, который вы хотите добавить
$("html").off(clickOutsideEventName);
}
});
Это отличный способ сделать вашу тему устойчивой к изменениям в ядре, поскольку весь код ядра выполняется первым, а затем уже ваш код. Итак, давайте попробуем это снова и посмотрим, что произойдёт.
api.modifyClass('component:user-card-contents', {
didInsertElement() {
// код из ядра
this._super(...arguments);
// код, который вы хотите добавить
$("html").off(clickOutsideEventName);
}
});
Почему это происходит? Из-за разного контекста кода. В файле компонента Ember clickOutsideEventName уже определён к моменту его использования. Ваша тема находится в другом файле, поэтому clickOutsideEventName там не определён.
Как это исправить? Помните это?
clickOutsideEventName — это свойство компонента, поэтому, если вы используете this.clickOutsideEventName, это должно сработать. Давайте попробуем.
api.modifyClass('component:user-card-contents', {
didInsertElement() {
// код из ядра
this._super(...arguments);
// код, который вы хотите добавить
$("html").off(this.clickOutsideEventName);
},
});
И действительно, это работает
Карточки пользователей теперь открываются без ошибок, и клик в любом месте вне их не вызывает никаких действий.
Вы можете сделать то же самое для карточек групп, вот так:
api.modifyClass('component:group-card-contents', {
didInsertElement() {
// код из ядра
this._super(...arguments);
// код, который вы хотите добавить
$("html").off(this.clickOutsideEventName);
},
});
Осталось только подключить эту кнопку закрытия к методу _close(), о котором мы говорили ранее, и для этого есть три шага:
добавить действие closeCard (вы можете назвать его как угодно);
добавить кнопку в шаблон карточки пользователя;
вызывать это действие при клике на кнопку.
Я остановлюсь на карточках пользователей для простоты. Итак, мы добавляем это (вместе с тем, что мы уже обсуждали):
И, конечно же, вы можете сделать что-то подобное для карточек групп, как я упоминал выше.
Я оставлю реализацию для карточек групп и мобильных устройств в качестве упражнения для вас, поскольку вы фактически будете использовать те же концепции, которые мы обсуждали выше, если захотите внести изменения в них, но, пожалуйста, дайте мне знать, если возникнут какие-либо проблемы.
Просто из любопытства: имеет ли значение номер версии в теге? И всегда ли лучше размещать их в head_tag.html, а не в header.html, или это не так важно?