Всплывающие подсказки могут показаться незначительными, застенчивыми и иногда игнорируемыми. Но они являются настоящими супергероями многих пользовательских интерфейсов. И как супергерои, они появляются из ниоткуда, именно тогда, когда они вам нужны, именно там, где требуется помощь. Но в чем же их суперсила?
Всплывающие подсказки — это действительно мощный инструмент для предоставления информации. Это идеальный способ предоставить контекстную помощь, не перегружая пользователей слишком большим количеством информации с первого взгляда. Всплывающие подсказки появляются там, где пользователям требуются дополнительные данные или пояснения. Не раньше и не позже. Это позволяет сохранить пользовательский интерфейс красивым, чистым и минималистичным.
Всплывающие подсказки обычно используются для объяснения значения различных инструментов, кнопок и значков.
Предоставление помощи в полях формы, объяснение сложных функций или инструментов, отображение дополнительной информации над графиками… Существует множество способов, с помощью которых всплывающие подсказки могут улучшить пользовательский опыт вашего приложения. Поэтому если вы являетесь front-end разработчиком, создающим приложение на Angular, есть большая вероятность, что рано или поздно вы будете добавлять всплывающие подсказки то тут, то там.
- Как добавить всплывающие подсказки в приложение Angular?
- Почему же мне следует создать собственную директиву Tooltip в Angular?
- Tooltip как директива, компонент или как сервис?
- Создание директивы всплывающих подсказок в Angular — пошаговый рецепт
- Шаг 0 — создайте проект Angular или используйте существующий проект
- Шаг 1 — создание компонента всплывающей подсказки
- Шаг 2 — определение фона всплывающей подсказки и других стилей
- Шаг 3 — создание директивы всплывающей подсказки
- Шаг 4 — добавление базовой логики всплывающей подсказки
- Шаг 5 — обернуть это в модуль всплывающих подсказок
- Шаг 6 — попробуйте!
- Как мы можем улучшить наши всплывающие подсказки Angular?
Как добавить всплывающие подсказки в приложение Angular?
Прежде чем мы погрузимся в создание собственной директивы Angular Tooltip Directive, давайте рассмотрим некоторые другие способы добавления всплывающих подсказок в приложение Angular. Одним из самых популярных способов является использование библиотеки компонентов Angular Material. Она предоставляет большой набор компонентов, которые следуют системе Google Material Design. Другая распространенная библиотека пользовательского интерфейса, которую вы можете рассмотреть, это ng-bootstrap — это Angular-версия ранее самой популярной библиотеки HTML/CSS/JS в мире — Bootstrap. Если вы ищете что-то более легкое, но мощное, вы также можете рассмотреть директиву ng2 tooltip.
Примеры всплывающих подсказок Angular Material & NG Bootstrap
Почему же мне следует создать собственную директиву Tooltip в Angular?
С таким количеством доступных решений для всплывающих подсказок, создание собственной директивы всплывающих подсказок может показаться изобретением колеса. Но вот несколько причин, по которым вам все же стоит подумать о том, чтобы создать ее с нуля самостоятельно:
- Потому что вы не хотите полагаться на еще один пакет npm или просто хотите, чтобы ваше приложение было легким — без добавления всего материала Angular для создания всплывающих подсказок;
- Потому что вам может понадобиться дополнительная настройка стиля всплывающих подсказок (например, светлая тема против темной) или поведения (например, динамические всплывающие подсказки, которые следуют за вашей мышью — я расскажу об этом во второй части этого руководства). Вы также можете добавить некоторые параметры директивы, такие как тип содержимого, пользовательские опции наведения (анимация, задержка скрытия, задержка показа) или поддержка сенсорных устройств / мобильных устройств;
- Потому что вы, возможно, создаете целый компонентный фреймворк для своего приложения и хотите, чтобы всплывающие подсказки были его частью;
- И последнее, но не менее важное — потому что вы, возможно, хотите научиться создавать структурные директивы angular, и хотя для начала можно найти несколько более простых директив, всплывающие подсказки послужат вам хорошим уроком.
Итак, если вы уже решили создать свои собственные всплывающие подсказки, давайте посмотрим, как мы можем подойти к этой задаче и какие строительные блоки Angular мы будем использовать.
Tooltip как директива, компонент или как сервис?
Конечно, зависит 😉 Пользовательская кнопка в Angular может быть реализована как отдельный тег компонента или как директива, наложенная на стандартный HTML-тег. Сегодня я покажу вам, как реализовать всплывающую подсказку в Angular в виде директивы, потому что я нахожу это наиболее распространенным вариантом использования. Обычно всплывающие подсказки не живут сами по себе (как компоненты) и их не нужно вызывать программно (через сервисы). Обычно они являются дополнениями к существующему HTML-контенту, и именно здесь структурные директивы работают лучше всего.
Но поскольку ваш случай использования может быть иным, вы можете рассмотреть возможность реализации всплывающих подсказок в качестве директивы:
- Angular Directive — простая и понятная, просто дополнение к существующему HTML-коду, обычно содержащее просто строку или короткий текст;
- Angular Component — когда вам нужно разместить внутри всплывающей подсказки что-то более сложное, чем текст или изображение, например, некоторые дополнительные данные над интерактивной диаграммой, или даже сделать универсальные всплывающие подсказки, которые могут отображать любой заданный контент внутри тега шаблона ng;
- Angular Service — в основном, если вам нужно программно добавить всплывающие подсказки или манипулировать ими из кода TypeScript, например, из функций других компонентов или сервисов.
Всплывающие подсказки над графиками — потенциальное место, где можно рассмотреть возможность реализации всплывающих подсказок в качестве компонентов
Создание директивы всплывающих подсказок в Angular — пошаговый рецепт
Итак, давайте начнем с короткого todo list с шагов, необходимых для создания наших многоразовых всплывающих подсказок:
- Создайте компонент всплывающей подсказки с шаблоном;
- Определите фон всплывающей подсказки и другие стили;
- Создайте директиву всплывающей подсказки;
- Добавить базовую логику всплывающей подсказки;
- Заверните все это в модуль всплывающих подсказок;
- Проведите тест!
Вот список необходимых ингредиентов для нашего рецепта директивы tooltip в Angular:
- 1 компонент — для определения шаблона и стиля нашей всплывающей подсказки;
- 1 директива — для прикрепления всплывающих подсказок к HTML-элементам;
- 1 модуль — чтобы обернуть все это и сделать многоразовым.
Компонент, директива и модуль?! До этого вы упоминали только директиву! Ага, вы меня раскусили! Я обманул вас, но совсем чуть-чуть. На самом деле всего этого можно добиться с помощью одной лишь директивы Angular. Но наличие компонента внутри директивы (вроде как) дает нам более удобный способ стилизации, а модуль поможет нам обернуть его и сохранить чистоту кода. Так что прошу прощения, но у меня были благие намерения 🙈.
Примечание: весь код из этого руководства вы найдете в нашем репозитории: https://github.com/accesto/angular-tooltip-directive.
Шаг 0 — создайте проект Angular или используйте существующий проект
Если вы хотите использовать существующий проект, просто перейдите к шагу 1. Если же вы хотите начать с нуля, откройте вашу любимую IDE, и в терминале введите:
ng new angular-tooltips-tutorial
И следуйте стандартному процессу установки нового проекта Angular. После запуска проекта замените содержимое файла app.component.html на одну кнопку:
<button>Do something</button>
Или иконкой кошки.
Шаг 1 — создание компонента всплывающей подсказки
Давайте начнем с создания компонента всплывающей подсказки с помощью Angular CLI:
ng generate component common/ui/tooltip
Мне нравится хранить все общие строительные блоки пользовательского интерфейса (такие как кнопки, всплывающие подсказки, ярлыки и т.д.) в каталоге app/common/ui/directory, но, конечно, не стесняйтесь подстраивать их под свои предпочтения.
Наш компонент всплывающей подсказки будет просто красивой оберткой для шаблона и некоторых CSS, никакой причудливой логики не требуется, поэтому tooltip.component.ts может быть простым:
import {Component, OnInit} from '@angular/core';
@Component({
selector: 'tooltip',
templateUrl: './tooltip.component.html',
styleUrls: ['./tooltip.component.scss']
})
export class TooltipComponent implements OnInit {
tooltip: string = '';
left: number = 0;
top: number = 0;
constructor() {}
ngOnInit(): void {}
}
Позже мы добавим некоторые настройки, но пока единственным отличием от пустого шаблона компонента являются три свойства — tooltip, left и top. Они будут хранить текст всплывающей подсказки и ее положение (всплывающие подсказки будут размещаться глобально в окне браузера, и мы будем использовать position:fixed для их размещения).
В шаблоне нам нужно только отобразить текст всплывающей подсказки и привязать ее расположение к свойствам left и top компонента, поэтому HTML-содержимое tooltip.component.html будет выглядеть следующим образом:
<div class="tooltip"
[style.left]="left + 'px'" [style.top]="top + 'px'">
{{tooltip}}
</div>
Достаточно просто, не так ли? Теперь давайте добавим несколько основных стилей, чтобы содержимое шаблона выглядело как настоящая всплывающая подсказка.
Шаг 2 — определение фона всплывающей подсказки и других стилей
Здесь приведен пример стиля с черным фоном, белым шрифтом и закругленными углами, но вы можете сделать его по-своему:
.tooltip {
position: fixed;
background-color: black;
border-radius: 4px;
color: #ffffff;
font-family: Arial;
padding: 3px 6px;
font-size: 13px;
margin-top: 5px;
transform:translateX(-50%);
}
Как уже упоминалось, всплывающие подсказки будут работать глобально, поэтому position: fixed. Последняя строка с transform:translateX(-50%) обеспечит самопозиционирование всплывающей подсказки по горизонтали.
Возможно, вы также захотите определить максимальную ширину или z-индекс, но я оставлю это на ваше усмотрение. Сейчас же давайте просто добавим красивый треугольник, обозначающий точку привязки:
.tooltip {
(...)
&::before {
content: '';
width: 0;
height: 0;
border-left: 5px solid transparent;
border-right: 5px solid transparent;
border-bottom: 5px solid black;
position: absolute;
left: calc(50% - 5px);
top: -5px;
}
}
Таким образом, общая всплывающая подсказка будет выглядеть следующим образом:
Пока мы стремимся отображать всплывающую подсказку под HTML-элементом, но во второй части этого руководства я расскажу, как добавить некоторые дополнительные опции использования — включая расположение/направление всплывающей подсказки, задержку показа или время закрытия. Но сначала давайте, наконец, создадим Angular Tooltip Directive.
Шаг 3 — создание директивы всплывающей подсказки
Директивы могут быть созданы через Angular CLI так же, как и компоненты:
ng generate component common/ui/tooltip/tooltip
Обратите внимание на дополнительный /tooltip в пути, так как мы хотим, чтобы наша директива была сгенерирована в уже существующей папке с компонентом.
Теперь давайте объявим, внедрим и импортируем несколько объектов, которые понадобятся нам внутри нашей директивы:
import {TooltipComponent} from "./tooltip.component";
@Directive({
selector: '[tooltip]'
})
export class TooltipDirective {
@Input() tooltip = '';
private componentRef: ComponentRef<any> = null;
constructor(
private elementRef: ElementRef,
private appRef: ApplicationRef,
private componentFactoryResolver: ComponentFactoryResolver,
private injector: Injector) {
}
}
Начнем с импорта класса нашего компонента tooltip, поскольку мы будем создавать его экземпляр (через фабрику компонентов) каждый раз, когда захотим показать всплывающую подсказку пользователям. Нам также нужно несколько объектов, которые будут введены в наш конструктор:
- elementRef — мы будем использовать его для доступа к HTML-элементу, на котором мы разместим тег директивы, например, кнопку или ссылку, чтобы проверить его положение в окне браузера;
- appRef — ссылка на целое приложение, что позволит нам глобально внедрить нашу подсказку в HTML-контент приложения;
- componentFactoryResolver — предоставит нам фабрику для создания нашего TooltipComponent программно внутри кода TooltipDirective;
- injector — который обеспечит инъекцию зависимостей Angular для нашего TooltipComponent.
Затем мы объявляем свойство componentRef, чтобы хранить ссылку на TooltipComponent, который мы будем создавать с помощью фабрики.
Не забудьте также указать значимое имя селектора для директивы. Я просто использовал [tooltip] для удобства, но вы можете назвать его, например, [appTooltip].
Затем давайте определим атрибут, который мы будем использовать для передачи в качестве @Input() нашей всплывающей подсказки. Сейчас нам нужно только содержимое всплывающей подсказки, поэтому вы можете назвать ее просто tooltip или что-то вроде tooltipText.
Опять же, для удобства я остановился на tooltip, так как это позволяет упростить синтаксис и легче использовать директиву. Поэтому вместо:
<div appTooltip [tooltip]="...
я могу пропустить часть с селектором и просто написать:
<div [tooltip]="...
Это, конечно, удобно, но иногда может вызвать проблемы, если свойство всплывающей подсказки конфликтует с другими директивами, которые у вас есть, так что будьте осторожны!
Шаг 4 — добавление базовой логики всплывающей подсказки
После того, как все готово, давайте реализуем логику всплывающих подсказок. Мы начнем с создания фактической всплывающей подсказки при наведении, или, точнее, при нажатии на кнопку мыши.
@HostListener('mouseenter')
onMouseEnter(): void {
if (this.componentRef === null) {
const componentFactory =
this.componentFactoryResolver.resolveComponentFactory(
TooltipComponent);
this.componentRef = componentFactory.create(this.injector);
this.appRef.attachView(this.componentRef.hostView);
const domElem =
(this.componentRef.hostView as EmbeddedViewRef<any>)
.rootNodes[0] as HTMLElement;
document.body.appendChild(domElem);
this.setTooltipComponentProperties();
}
}
Этот метод будет слушать (благодаря @HostListener) события указателя мыши относительно элемента DOM, на котором мы применили нашу директиву tooltip. Событие mouseenter испускается каждый раз, когда курсор пользователя начинает нависать над нашим DOM-элементом. Когда это происходит, мы сначала проверяем, не существует ли еще наша всплывающая подсказка (используя ее ссылку this.componentRef), и если нет, то переходим к ее созданию:
- Прежде всего, нам нужно использовать resolveComponentFactory, чтобы получить фабрику, которая может производить наш компонент всплывающей подсказки;
- Затем мы используем ее для создания компонента, передавая инжектор зависимостей;
- Далее мы присоединяем наш компонент к виртуальному DOM всего приложения через appRef;
- Мы также присоединяем его к реальному DOM с помощью стандартного document.body.appendChild;
- И последнее, но не менее важное, мы определяем и вызываем методы для установки положения и заголовка нашей всплывающей подсказки.
Этот метод для настройки свойств выглядит следующим образом:
private setTooltipComponentProperties() {
if (this.componentRef !== null) {
this.componentRef.instance.tooltip = this.tooltip;
const {left, right, bottom} =
this.elementRef.nativeElement.getBoundingClientRect();
this.componentRef.instance.left = (right - left) / 2 + left;
this.componentRef.instance.top = bottom;
}
}
Описание метода довольно простое:
- Доступ к this.componentRef.instance позволяет использовать реальный экземпляр нашего компонента, чтобы мы могли настроить его свойства, например, текст всплывающей подсказки (я использовал прямой доступ к свойствам компонента, но я рекомендую вам вместо этого определить некоторые сеттеры);
- getBoundingClientRect() — позволяет нам получить расположение HTML-элемента и на основе этого вычислить координаты всплывающей подсказки относительно окна браузера.
Вот как мы работаем с наведением, теперь пришло время позаботиться о скрытии всплывающей подсказки:
@HostListener('mouseleave')
onMouseLeave(): void {
this.destroy();
}
ngOnDestroy(): void {
this.destroy();
}
destroy(): void {
if (this.componentRef !== null) {
this.appRef.detachView(this.componentRef.hostView);
this.componentRef.destroy();
this.componentRef = null;
}
}
Есть две ситуации, в которых мы должны убедиться, что наша всплывающая подсказка скрывается: когда мы наводим курсор (событие mouseleave), и когда элемент, на котором мы применили нашу директиву angular tooltip, уничтожается (ngOnDestroy).
И… вот и все! Осталось только завернуть его, и все готово.
Шаг 5 — обернуть это в модуль всплывающих подсказок
И снова мы воспользуемся Angular CLI:
ng generate module common/ui/tooltip
Внутри модуля мы объявим наш компонент и директиву, а также экспортируем нашу финальную директиву Angular tooltip:
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {TooltipComponent} from './tooltip.component';
import {TooltipDirective} from './tooltip.directive';
@NgModule({
declarations: [
TooltipComponent,
TooltipDirective
],
imports: [
CommonModule
],
exports: [TooltipDirective]
})
export class TooltipModule { }
И… готово. Теперь давайте испытаем его в тестовом режиме.
Шаг 6 — попробуйте!
Чтобы использовать всплывающие подсказки, мы просто должны добавить наш TooltipModule в app.module.ts:
...
import {TooltipModule} from './common/ui/tooltip/tooltip.module';
@NgModule({
...
imports: [
...
TooltipModule
],
...
})
И мы готовы применить директиву следующим образом:
<div class="cat-icon" [tooltip]="'Meow!'"></div>
или для кнопки:
<button [tooltip]="'Some explanation'>Do something</button>
Вот и все — теперь вы можете навести курсор на вашу кнопку (или кота 😺) и посмотреть, как появляется наша всплывающая подсказка:
Как мы можем улучшить наши всплывающие подсказки Angular?
Это был лишь очень базовый урок по созданию собственных директив всплывающих подсказок Angular. Потерпите меня и дождитесь второй части, в которой мы расширим это множество полезных опций настройки и использования. Часть 2. будет включать в себя:
- Задержка скрытия / задержка показа всплывающей подсказки;
- Положение всплывающей подсказки: выше/ниже/левее/правее;
- Динамические всплывающие подсказки, которые следуют за курсором мыши;
- Темы всплывающих подсказок, например, темный фон всплывающих подсказок;
- Опции по умолчанию & значения по умолчанию, например, задержка по умолчанию;
- Анимация показа и скрытия всплывающих подсказок;
- Поддержка сенсорных устройств / мобильных устройств.
Ура! 🍺