Сайт визитка на Angular. Создание UI KIT.

Aleksandr Serenko
F.A.F.N.U.R
Published in
7 min readMar 27, 2022
Сайт визитка на Angular. Создание UI KIT

В данной статье поговорим о создании UI KIT для приложения. Поговорим о том зачем он нужен, и приведем реализацию компонентов с использованием Angular Material.

Под UI KIT будем понимать набор компонентов из которых будет собираться приложение. Каждый элемент в UI KIT’е не привязан к конкретному контексту, то есть не зависит от конкретных данных. Если компонент и принимает данные, то скорее всего в виде предоставляемого компонентом интерфейсом.

Обычно при разработке приложения есть дизайнер, который формирует позиционирование бренда, вместе с этим разрабатывает и требования к дизайну.

Так как дизайнер отсутствует, будем считать, что Angular Material это лучшее решение.

Angular Material предоставляет стили для большинства UI элементов. Но есть часть компонентов, которые отсутствуют, в частности решение для задания сеток и карусель.

У Angular есть пакет Angular Flex-Layout, но лично меня он немного коробит, и я не рекомендую его использовать, так как перекладывать логику задания стилей с CSS на JS достаточно сомнительно.

Реализуем следующие компоненты и модули:

  • CarouselModule — модуль, который будет создавать карусель из принимающих изображений.
  • ContainerModule — модуль, который будет создавать блок фиксированного размера и выравнивать его по центру. Модуль реализует класс контейнера из Twitter Bootstrap.
  • GridModule — модуль, который позволит создавать сетки отобращения элементов. Модуль реализует метод сеток из Twitter Bootstrap.
  • ImageModule — модуль, который будет содержать pipe для работы с изображениями.
  • LayoutModule — главный макет приложения, который будет включать в себя такие компоненты как шапка, подвал, sidebar и прочее.
  • LinkModule — модуль, который будет содержать директивы и пайпы для ссылок.
  • MultiplatformModule — модуль, который позволяет выводить разное содержимое для 3 платформ (handset, tablet и web).
  • WidthModule — модуль для манипуляции шириной элементов.

Создание темы Angular Material

Создадим новую папку stylesheets в libs. Добавим её в .nxignore.

Создадим новый файл material-theme.scss, в который переместим стили создания темы Angular Material:

Добавим файл с глобальными стилями global.scss:

Для того, чтобы не зависеть от пакета normalize, украдем все стили и добавим их в файл normalize.scss.

Создадим файл mixins.scss, в котором сделаем короткие вызовы для адаптивных размеров :

И в конце создадим файл с глобальными переменными:

Добавим все это в styles.scss:

// Global styles
@import 'libs/ui/stylesheets/normalize';
@import 'libs/ui/stylesheets/material-theme';
@import 'libs/ui/stylesheets/global';

Carousel Module

Реализуем простую карусель, где в компонент будет передаваться массив изображений, которые будут отображаться.

Так как нужен адаптивный компонент, для карусели нужно будет задавать фиксированную высоту.

Принцип работы следующий:

  • Создается компонент карусели.
  • В компоненте создаются кнопки навигации влево и вправо, а также точки, при клике на которые можно перейти к конкретному слайду.
  • Далее все изображения выводятся в дочернем компоненте, со свойствами {position: absolute; top: 0; left: 0; z-index: 1;}. Это позволит наложить изображения друг на друга. Так как в карусели есть индекс активного слайда, для активного слайда устанавливается { z-index: 2 }.
  • Так как изображения могут быть разного размера, будет использоваться отображение с помощью CSS свойстваbackground-image, вместо стандартного отображения с помощью тегаimage.

Создадим библиотеку:

nx g lib ui/carousel

Реализуем компонент:

Компонент точек CarouselDotsModule:

Как видно из реализации, по количеству слайдов создается массив точек.

export class CarouselDotsComponent implements OnInit {
@Input() counts!: number;
@Input() active!: number;

@Output() selected = new EventEmitter<number>();

dots!: number[];

ngOnInit(): void {
this.dots = this.counts ? new Array(this.counts) : [];
}

onSelect(index: number): void {
this.selected.emit(index);
}
}

При клике на точку, происходит событие selected.

Компонент навигации по слайдам влево и право CarouselNavsModule:

Компонент с навигацией еще проще.

export class CarouselNavsComponent {
@Output() prev = new EventEmitter<void>();
@Output() next = new EventEmitter<void>();

onPrev(): void {
this.prev.emit();
}

onNext(): void {
this.next.emit();
}
}

При клике соответствующих кнопок происходят события prev или next.

Модуль слайда выглядит следующим образом CarouselSlideModule:

Компонент слайда принимает 2 параметра:

  • image — путь до изображения;
  • active — является ли текущий слайд активным.
export class CarouselSlideComponent {
@Input() image!: string;
@Input() active = false;
@Output() clicked = new EventEmitter<void>();

onClick(): void {
this.clicked.emit();
}
}

При клике по слайду происходит вызов события clicked.

Событие называется clicked не случайно. В документации Angular не совсем ясно написано, что все события которые создаются с помощью @Output могут перекрывать стандартные события. Поэтому лучше все кастомные события называть так, чтобы они не совпадали со стандартными событиями DOM’а.

Пример работы карусели:

<banshop-carousel [images]="product.photos"></banshop-carousel>

Container Module

Цель компонента контейнера задать фиксированную ширину в зависимости от ширины экрана.

Создадим библиотеку:

nx g lib ui/container

Как видно из реализации, контейнер в зависимости от ширины экрана и ориентации получает необходимые размеры:

@use 'libs/ui/stylesheets/mixins' as store-mixins;

:host {
display: block;
padding-left: 1rem;
padding-right: 1rem;
margin-left: auto;
margin-right: auto;
width: 100%;

...
&.is-fluid {
max-width: 100%;
}

@include store-mixins.media-tablet-portrait() {
max-width: 540px;

&.is-fluid {
max-width: 100%;
}
}

@include store-mixins.media-tablet-landscape() {
max-width: 900px;

&.is-fluid {
max-width: 100%;
}
}

@include store-mixins.media-web-portrait() {
max-width: 790px;

&.is-fluid {
max-width: 100%;
}
}

@include store-mixins.media-web-landscape() {
max-width: 1200px;

&.is-fluid {
max-width: 100%;
}
}
}

Также контейнер принимает свойство mode, которое добавляет специфичные стили компоненту:

:host {
...
&.is-flex {
display: flex;
flex-direction: column;
flex-wrap: wrap;
}

&.is-flex-row {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}

&.is-flex-row-space {
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: space-between;
align-items: center;
}

&.is-max-height {
height: 100%;
}
...
}

Пример работы контейнера:

<banshop-container [mode]="mode">
<router-outlet></router-outlet>
</banshop-container>

Grid Module

Реализация сетки достаточно тривиальна.

Процесс следующий:

  • Создается два компонента row и column.
  • Далее создаются директивы размеров handset, tablet и web.
  • Затем в зависимости от наличия директив, компонент принимает те или иные свойства.

Подробнее о создании сеток можно почитать здесь — Реализация колонок в Angular с использованием Angular CDK.

Создадим библиотеку:

nx g lib ui/grid

В результате получим модуль для строк RowModule:

Модуль для колонок ColumnModule:

Модули директив:

Модуль для сеток, который подключает в себя модули колонок, строк и директив GridModule:

Пример работы сеток:

<banshop-row tablet>
<banshop-column tablet="6" web="4" *ngFor="let product of products;">
<banshop-product-card [product]="product"></banshop-product-card>
</banshop-column>
</banshop-row>

Image Module

Небольшой модуль для создания backgroundImage.

Создадим библиотеку и добавим реализацию:

nx g lib ui/image

Пример использования:

<div [ngStyle]="image | backgroundImage"></div>

Link Module

Модуль для задания ссылкам определенных стилей.

Аналогично создадим библиотеку и добавим реализацию:

nx g lib ui/link

Так как Angular не дает задавать стили директивам, использование следующее — в файле стилей компонента импортируются следующие стили.

@import 'libs/ui/link/src/lib/link.directive';

Это необходимо, чтобы работала инкапсуляция стилей. В противном случае стили необходимо делать глобальными.

Multiplatform Module

Модуль, который позволяет выводить разное содержимое для 3 платформ: handset, tablet и web.

Создадим библиотеку:

nx g lib ui/multiplatform

Реализация заключается в подписке на текущий размер с помощью LayoutService.

В шаблоне компонента определяются разделы для вывода ng-content’а в соответствии с требуемым размером.

<ng-container [ngSwitch]="layoutType$ | async">
<ng-container *ngSwitchCase="breakpoints.Web">
<ng-content select="[web]" automation-id="web"></ng-content>
</ng-container>
<ng-container *ngSwitchCase="breakpoints.Tablet">
<ng-content select="[tablet]" automation-id="tablet"></ng-content>
</ng-container>
<ng-container *ngSwitchDefault>
<ng-content select="[handset]" automation-id="handset"></ng-content>
</ng-container>
</ng-container>

Пример работы на карточке товара в корзине:

<banshop-multiplatform>
<ng-container handset> ... </ng-container>
<ng-container tablet> ... </ng-container>
<ng-container web> ... </ng-container>
</banshop-multiplatform>

Width Module

Модуль для задания ширины компонентам.

Создадим библиотеку:

nx g lib ui/width

В основном используется для задания {width: 100% } элементам формы Angular Material.

Layout Module

Добавим основной макет приложения.

Создадим библиотеку:

nx g lib ui/layout

Header

Сгенерируем шапку приложения:

nx g m header --project=ui-layout
nx g c header --project=ui-layout

Добавим Logo:

Добавим кнопки для шапки:

В результате получим:

web
handset

Footer

Создадим подвал приложения.

В результате получим:

Sidebar

Добавим боковую панель для tablet.

Nav

Добавим меню для tablet.

И меню для мобильной версии:

Тогда итоговый layout примет вид:

Пример с условиями самый показательный:

Ссылки

Оглавление

Предыдущая статья — Создание core библиотек.

Следующая статья —Модуль товаров.

Все исходники находятся на github, в репозитории:

Для того, чтобы посмотреть состояние проекта на момент написания статьи, нужно выбрать соответствующий тег — article.

Подписывайтесь на блог, чтобы не пропустить новые статьи про Angular, и веб-разработку. Medium | Telegram| VK |Tw| Ln

--

--

Aleksandr Serenko
F.A.F.N.U.R

Senior Front-end Developer, Angular evangelist, Nx apologist, NodeJS warlock