[Angular] Построение компонентов с выпадающими блоками с помощью Angular и Material CDK
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Каждое приложение использует компоненты с выпадающими блоками. Такие панели используются в выпадающем списке, Autocomplete, Tooltip и т.д. В Material CDK есть инструмент Overlay для создания такого функционала.
Зачем это нужно? Z-index и вперед!С точки зрения простого html и css, мы можем использовать Z-index. Например, для dropdownmenu есть следующий рецепт. Но как писал Александр Инкин в своей статье, мы получим войну миров z-index. У Taiga UI есть свой способ решить эту проблему. Рассмотрим альтернативный инструмент от команды Material - CDK Overlay.Кто применяетМожет показаться, что Material CDK обязывает использовать нас библиотеку компонентов Angular Material, но это не так. CDK и его базовые стили можно подключить отдельно. Благодаря этому команда создателей CDK дала возможность использовать их наработки в своих проектах. Более того, популярные и крупные проекты тоже активно используют этот CDK. Библиотеки компонентов, которые используют Material CDK:
- Material
- Ant Design (ng-zorro)
Какие компоненты можно сделать на базе CDK overlay:
- Tooltip
- Dialog
- Select
- Autocomplete
- DropDawnMenu
- TreeSelect
- Calendar
Почему бы не попробовать использовать его в своей работе, ведь он облегчает решение задач. Ниже мы рассмотрим примеры, как его использовать, и создадим компонент.Начало работы, подключим CDKУстановить пакет
npm i @angular/cdk
Можно работать без основных материал компонентов, для этого нужно подключить стиль CDKПо ходу создания компонентов будем описывать Api CDK Overlay. Итак, приступим к созданию Tooltip!Требования к TooltipПеред началом реализации подумаем, как мы его хотим использовать и из чего он будет состоять. Для этого зададим к нему небольшие требования.
- Не использует Z-index!
- Может позиционироваться как над, так и под элементом, к которому применяется подсказка и зависит от пространства сверху/снизу. Если нет места сверху на экране, он должен отображаться снизу.
- Работает в 2-х режимах. Первый скрывает подсказку, если исчезает фокус с элемента (так работает Material). Второй не исчезает (так работает ng-zorro), чтобы можно было скопировать текст или совершить какие-либо действия.
- Может отображать простой текст или шаблон.
- Должен иметь легкий API для использования.
Архитектура Tooltip
- Директива - используется в компоненте, к которому применяется Tooltip
- Компонент - это отображение самого Tooltip.
Принцип работы
- Директива добавляет функциональность к компоненту. При событии Mouseenter будет создаваться компонент. В работе будут использованы следующие API Angular: ComponentFactoryResolver и ViewContainerRef - с их помощью будет создаваться компонент TooltipComponent.ElementRef - для получения ссылки на host элемент, она потребуется для OverlayCdk.Renderer2 - с его помощью него можно подписаться на события (Mouseenter)
- После создания инстанса компонента ему устанавливаются параметры:title - простой текст;view - ссылка на шаблон;origin - ссылка на host компонент.
- Теперь самое интересное, работа с СDK Overlay:cdkConnectedOverlay - директива для декларативного создания всплывающих элементов;cdkConnectedOverlayOrigin - сюда передается наш host, элемент относительно которого будет создаваться всплывающая область;cdkConnectedOverlayPositions - список возможного позиционирования всплывающей области. В ней можно настроить всплывающую область сверху, снизу, слева, справа и задать дополнительный класс css. В примере настройка для всплытия снизу и сверху.
Код директивы
@Directive({
selector: '[ui-tooltip]',
exportAs: 'uiTooltip'
})
export class TooltipDirective implements OnInit, OnDestroy, AfterViewInit {
@Input()
content: string;
@Input()
view: TemplateRef<any>;
component: TooltipComponent;
componentFactory: ComponentFactory<
TooltipComponent
> = this.resolver.resolveComponentFactory(TooltipComponent);
protected readonly disposables: Array<() => void> = [];
constructor(
private elementRef: ElementRef,
private hostView: ViewContainerRef,
private renderer: Renderer2,
private resolver: ComponentFactoryResolver
) {}
ngOnInit(): void {}
ngAfterViewInit(): void {
this.registerTriggers();
}
createComponent(): void {
const componentRef = this.hostView.createComponent(this.componentFactory);
this.component = componentRef.instance as TooltipComponent;
this.component.setTitle(this.content);
this.component.setView(this.view);
this.component.setOverlayOrigin({ elementRef: this.elementRef });
this.component.mouseleave.subscribe(x => {
this.component.hide();
});
}
registerTriggers(): void {
const el = this.elementRef.nativeElement;
const listnerMouseenter = this.renderer.listen(el, 'mouseenter', () => {
this.createComponent();
});
this.disposables.push(listnerMouseenter);
const listnerMouseleave = this.renderer.listen(el, 'mouseleave', () => {
console.log(el);
setTimeout(x => {
if (!this.component.isActive) {
this.component.hide();
}
}, 50);
});
this.disposables.push(listnerMouseleave);
}
ngOnDestroy(): void {
this.disposables.forEach(dispose => dispose());
}
}
Код Tooltip
@Component({
selector: 'uikit-tooltip',
exportAs: 'uikitTooltip',
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
template: `
<ng-template
#overlay="cdkConnectedOverlay"
cdkConnectedOverlay
[cdkConnectedOverlayOrigin]="origin"
[cdkConnectedOverlayOpen]="visible"
[cdkConnectedOverlayPositions]="positions"
[cdkConnectedOverlayPush]="true"
,
(detach)="hide()"
(positionChange)="positionChange($event)"
>
<div #tooltip class="tooltip-container">
<div class="tooltip-body">
<div *ngIf="title">{{ title }}</div>
<div *ngIf="view">
<ng-template [ngTemplateOutlet]="$any(view)"></ng-template>
</div>
</div>
</div>
</ng-template>
`,
styleUrls: ['./tooltip.component.scss']
})
export class TooltipComponent implements OnInit, AfterViewInit {
title: string;
view: TemplateRef<any>;
isActive = false;
@ViewChild('overlay', { static: false }) overlay!: CdkConnectedOverlay;
@ViewChild('tooltip', { static: false }) tooltip!: ElementRef;
@Output() mouseleave = new EventEmitter();
origin!: CdkOverlayOrigin;
positions = [
new ConnectionPositionPair(
{ originX: 'center', originY: 'top' },
{ overlayX: 'center', overlayY: 'bottom' },
0,
0,
'tooltip-body-top'
),
new ConnectionPositionPair(
{ originX: 'center', originY: 'bottom' },
{ overlayX: 'center', overlayY: 'top' },
0,
0,
'tooltip-body-bottom'
)
];
visible = false;
constructor(public cdr: ChangeDetectorRef) {}
overlayRef: any;
ngOnInit(): void {}
ngAfterViewInit() {
fromEvent<any>(this.tooltip.nativeElement, 'mouseenter').subscribe(
(event: any) => {
this.isActive = true;
this.cdr.markForCheck();
}
);
fromEvent<any>(this.tooltip.nativeElement, 'mouseleave').subscribe(
(event: any) => {
this.isActive = false;
this.cdr.markForCheck();
this.mouseleave.emit();
}
);
}
setOverlayOrigin(origin: CdkOverlayOrigin): void {
this.origin = origin;
this.show();
}
hide(): void {
this.visible = false;
this.cdr.markForCheck();
}
show(): void {
this.visible = true;
}
setTitle(title: string): void {
this.title = title;
}
setView(view): void {
this.view = view;
}
positionChange($event) {
}
}
Результат работы
Angular Tooltip CDKЗаключениеМы изучили Material CDK Overlay. Сделали Tooltip и познакомились с некоторыми важными API Angular. Эта статья поможет вам создавать свои прекрасные компоненты на базе CDK. А в следующей части мы рассмотрим создание других компонентов на базе CDK.К примеру нужно относиться как к заготовке и ознакомлением с CDK Overlay. Для того чтобы использовать его в проекте, нужно будет доработать следующие моменты:
- отписаться от событий;
- покрыть тестом;
- прикрутить палитру из ваших переменных темы.
===========
Источник:
habr.com
===========
Похожие новости:
- [Разработка веб-сайтов, Angular, Конференции, Микросервисы] От одного приложения — к сотне. Путь микрофронтенда в Тинькофф Бизнес
- [JavaScript, Fidonet, HTML, Карьера в IT-индустрии] X5 frontend meetup
- [Angular, ReactJS, TypeScript] Почему мы должны выбросить React и взяться за Angular (перевод)
- [Google App Engine, Angular] Коротко и ясно: размещаем фронт Angular 11, бэк Spring Boot Java 11 и mySQL DB на Google App Engine
- [HTML, Usability, Accessibility] 7 хороших HTML привычек
- [CSS, Angular] Заметка о вариантах организации Sass/SCSS в Angular приложении
- [Тестирование IT-систем, Учебный процесс в IT, Карьера в IT-индустрии, Конференции] Июньский дайджест: митапы, практикум, мастер-классы
- [JavaScript, Программирование, Angular] Кастомные операторы RxJS (перевод)
- [JavaScript, ReactJS] react-router: Три метода рендеринга маршрутов (компонентный, рендеринговый и дочерний) (перевод)
- [Дизайн] Цветовая палитра как часть дизайн-системы
Теги для поиска: #_angular, #_angular, #_frontend, #_material, #_blog_kompanii_evroplan (
Блог компании Европлан
), #_angular
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 24-Ноя 23:40
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Каждое приложение использует компоненты с выпадающими блоками. Такие панели используются в выпадающем списке, Autocomplete, Tooltip и т.д. В Material CDK есть инструмент Overlay для создания такого функционала. Зачем это нужно? Z-index и вперед!С точки зрения простого html и css, мы можем использовать Z-index. Например, для dropdownmenu есть следующий рецепт. Но как писал Александр Инкин в своей статье, мы получим войну миров z-index. У Taiga UI есть свой способ решить эту проблему. Рассмотрим альтернативный инструмент от команды Material - CDK Overlay.Кто применяетМожет показаться, что Material CDK обязывает использовать нас библиотеку компонентов Angular Material, но это не так. CDK и его базовые стили можно подключить отдельно. Благодаря этому команда создателей CDK дала возможность использовать их наработки в своих проектах. Более того, популярные и крупные проекты тоже активно используют этот CDK. Библиотеки компонентов, которые используют Material CDK:
npm i @angular/cdk
@Directive({
selector: '[ui-tooltip]', exportAs: 'uiTooltip' }) export class TooltipDirective implements OnInit, OnDestroy, AfterViewInit { @Input() content: string; @Input() view: TemplateRef<any>; component: TooltipComponent; componentFactory: ComponentFactory< TooltipComponent > = this.resolver.resolveComponentFactory(TooltipComponent); protected readonly disposables: Array<() => void> = []; constructor( private elementRef: ElementRef, private hostView: ViewContainerRef, private renderer: Renderer2, private resolver: ComponentFactoryResolver ) {} ngOnInit(): void {} ngAfterViewInit(): void { this.registerTriggers(); } createComponent(): void { const componentRef = this.hostView.createComponent(this.componentFactory); this.component = componentRef.instance as TooltipComponent; this.component.setTitle(this.content); this.component.setView(this.view); this.component.setOverlayOrigin({ elementRef: this.elementRef }); this.component.mouseleave.subscribe(x => { this.component.hide(); }); } registerTriggers(): void { const el = this.elementRef.nativeElement; const listnerMouseenter = this.renderer.listen(el, 'mouseenter', () => { this.createComponent(); }); this.disposables.push(listnerMouseenter); const listnerMouseleave = this.renderer.listen(el, 'mouseleave', () => { console.log(el); setTimeout(x => { if (!this.component.isActive) { this.component.hide(); } }, 50); }); this.disposables.push(listnerMouseleave); } ngOnDestroy(): void { this.disposables.forEach(dispose => dispose()); } } @Component({
selector: 'uikit-tooltip', exportAs: 'uikitTooltip', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.None, template: ` <ng-template #overlay="cdkConnectedOverlay" cdkConnectedOverlay [cdkConnectedOverlayOrigin]="origin" [cdkConnectedOverlayOpen]="visible" [cdkConnectedOverlayPositions]="positions" [cdkConnectedOverlayPush]="true" , (detach)="hide()" (positionChange)="positionChange($event)" > <div #tooltip class="tooltip-container"> <div class="tooltip-body"> <div *ngIf="title">{{ title }}</div> <div *ngIf="view"> <ng-template [ngTemplateOutlet]="$any(view)"></ng-template> </div> </div> </div> </ng-template> `, styleUrls: ['./tooltip.component.scss'] }) export class TooltipComponent implements OnInit, AfterViewInit { title: string; view: TemplateRef<any>; isActive = false; @ViewChild('overlay', { static: false }) overlay!: CdkConnectedOverlay; @ViewChild('tooltip', { static: false }) tooltip!: ElementRef; @Output() mouseleave = new EventEmitter(); origin!: CdkOverlayOrigin; positions = [ new ConnectionPositionPair( { originX: 'center', originY: 'top' }, { overlayX: 'center', overlayY: 'bottom' }, 0, 0, 'tooltip-body-top' ), new ConnectionPositionPair( { originX: 'center', originY: 'bottom' }, { overlayX: 'center', overlayY: 'top' }, 0, 0, 'tooltip-body-bottom' ) ]; visible = false; constructor(public cdr: ChangeDetectorRef) {} overlayRef: any; ngOnInit(): void {} ngAfterViewInit() { fromEvent<any>(this.tooltip.nativeElement, 'mouseenter').subscribe( (event: any) => { this.isActive = true; this.cdr.markForCheck(); } ); fromEvent<any>(this.tooltip.nativeElement, 'mouseleave').subscribe( (event: any) => { this.isActive = false; this.cdr.markForCheck(); this.mouseleave.emit(); } ); } setOverlayOrigin(origin: CdkOverlayOrigin): void { this.origin = origin; this.show(); } hide(): void { this.visible = false; this.cdr.markForCheck(); } show(): void { this.visible = true; } setTitle(title: string): void { this.title = title; } setView(view): void { this.view = view; } positionChange($event) { } } Angular Tooltip CDKЗаключениеМы изучили Material CDK Overlay. Сделали Tooltip и познакомились с некоторыми важными API Angular. Эта статья поможет вам создавать свои прекрасные компоненты на базе CDK. А в следующей части мы рассмотрим создание других компонентов на базе CDK.К примеру нужно относиться как к заготовке и ознакомлением с CDK Overlay. Для того чтобы использовать его в проекте, нужно будет доработать следующие моменты:
=========== Источник: habr.com =========== Похожие новости:
Блог компании Европлан ), #_angular |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 24-Ноя 23:40
Часовой пояс: UTC + 5