Если вы чем-то похожи на меня, вы слышали о TemplateRef в Angular, но не знали, что это значит. И это, вероятно, отпугнуло вас от изучения того, что они собой представляют или как они работают. Но учиться на самом деле не так уж и страшно. Просто нужно немного поработать и немного попрактиковаться, и польза может быть огромной.

Впервые я узнал о TemplateRefs из этой статьи Исаака Манна. Это привлекло мое внимание, потому что я много слышал о свойствах рендеринга в React, но не знал, как это применимо к Angular. В итоге я прочитал несколько статей Исаака о ng-templates и TemplateRefs. Это отличные статьи, и вы можете найти их все здесь.

Хорошо, давайте приступим к делу. Я узнал следующее, добавляя параметр компонента TemplateRef в пакет angular-tag-select, которым я управляю. До сих пор использование компонента означало, что вы должны были использовать стили, которые я определил. Был объект настроек, который вы могли передать, который изменил некоторые стили. Эти параметры включали, например, классы значков и были очень ограничены. И перезаписать любой из стилей по умолчанию было сложно. Поэтому я хотел предоставить людям возможность воспользоваться преимуществами компонента выбора тегов, не привязываясь к стилям по умолчанию.

При создании компонента, использующего TemplateRefs, есть две основные части. Первый находится в шаблоне. Шаблон должен содержать элемент для вывода шаблона, который увидит пользователь. Это может быть div или какой-либо другой элемент HTML, или это может быть <ng-container>. Если вы используете элемент HTML, этот элемент будет отображаться на странице. Если использовать <ng-container>, то на странице не будет лишних элементов. Вот пример шаблона с использованием <ng-container>:

<ng-container *ngTemplateOutlet="layoutTemplate; context: ctx"></ng-container>

*ngTemplateOutlet состоит из двух частей: layoutTemplateи context. layoutTemplate — это переменная класса, украшенная декоратором @ContentChild() из Angular. Это то, что позволяет вам передать <ng-template> в этот компонент из родительского компонента. context позволяет сделать переменные класса доступными в родительском компоненте. Это объект с переменными и функциями, к которым вы хотели бы иметь доступ.

public ctx: any = {
    tagsSelectedAtStart: this.tagsSelectedAtStart,
    possibleTags: this.possibleTags,
    selectedTags: this.selectedTags,
    fns: {
        toggleTag: this.toggleTag,
    }
};

Другая важная часть реализации компонента, использующего TemplateRef, находится в родительском компоненте. Предположим, что наш компонент TemplateRef является компонентом <tag-select>, и мы реализуем этот компонент в нашем приложении в компоненте <submit-form>. Это самые основы того, как должен выглядеть шаблон компонента <submit-form>:

<tag-select>
    <ng-template>
        <!-- HTML to display parts of the tag-select component -->
    </ng-template>
</tag-select>

Чтобы использовать шаблон, вы помещаете компонент <tag-select> в свой шаблон. Внутри открывающего и закрывающего тега этого компонента вам нужен тег <ng-template>. Этот тег <ng-template> жизненно важен, потому что контент, который вы размещаете внутри него, будет отображаться как пользовательский интерфейс для компонента <tag-select>. В этом случае с этим компонентом вам нужно отобразить две основные части: выбранные теги и теги, которые возможны для выбора. Как бы вы ни хотели их показать, HTML-код для этого должен находиться между тегом <ng-template>.

Помните, в теге <ng-container> мы объявили переменную context? Мы получаем доступ к этим переменным в родительском объекте следующим образом:

<tag-select>
    <ng-template let-selected="selected" let-possible="possible">
        <!-- HTML to display parts of the tag-select component -->
    </ng-template>
</tag-select>

После этого мы можем получить доступ к внутренним переменным класса. В этом случае это дает нам доступ к списку выбранных тегов, а также к списку возможных тегов, поэтому мы можем отображать эти два списка так, как мы хотим. Скорее всего, у вас также будут функции, открытые в контексте.

Есть несколько вещей, которые нужно помнить о context, если вы объявляете его с помощью переменной класса, как показано выше, и функций, которые вы предоставляете. Во-первых, каждый раз, когда обновляется одна из внутренних переменных класса, контекст также необходимо явно обновлять. Если вы объявите context следующим образом, вам не нужно будет явно обновлять контекст каждый раз, когда что-то меняется; Angular сделает это за вас:

<ng-container *ngTemplateOutlet="layoutTemplate; context: { selected: selected, possible: possible }">
</ng-container>

Что касается функций, если вы просто используете обычный синтаксис для написания функции в компоненте, вы столкнетесь с проблемами с undefined переменными, потому что this больше не привязано к TagSelectComponent. Чтобы избежать этой проблемы, используйте стрелочные функции; это автоматически привяжет this к экземпляру компонента. Ознакомьтесь с этим вопросом StackOverflow для получения дополнительной информации. Следующие блоки кода продемонстрируют это.

toggleTag = (tag: Tag) => {
    // Do Stuff
    this.updateContext('selected', this.selected)
}
updateContext(key: string, value: any) {
    this.ctx[key] = value;
}

Я знаю, что все это кажется очень сложным, но как только я вскочил и начал пробовать, все оказалось не так уж плохо. У меня еще много практики и долгий путь, но я в восторге от возможностей, которые это откроет для меня как для Angular-разработчика. Возможность использовать или предоставлять пакеты, которые выполняют сложную логику для конечного пользователя, но позволяют им определять внешний вид, действительно эффективна. Вот почему этот шаблон стал таким популярным в React.

EnterpriseNG выйдет 4 и 5 ноября 2021 года.

Приходите послушать ведущих спикеров сообщества, экспертов, лидеров и команду Angular, которые в течение двух дней будут рассказывать обо всем, что вам нужно, чтобы максимально использовать Angular в ваших корпоративных приложениях.
Темы будут сосредоточены на следующих четырех областях:
• Монорепозитории
• Микроинтерфейсы
• Производительность и масштабируемость
• Удобство обслуживания и качество
Узнайте больше здесь ›› https://enterprise.ng-conf.org/