Реализация колонок в Angular с использованием Angular CDK

Aleksandr Serenko
F.A.F.N.U.R
Published in
4 min readFeb 15, 2022
Реализация колонок в Angular с использованием Angular CDK

В данной статье приведу решение для реализации колонок в Angular, используя breakpoints из Angular CDK.

В результате будет следующее решение:

Ранее я уже писал о реализации колонок как в BootstrapBanx. Создание базового лейаута в Angular, а также использовал данное решение в цикле статей про тестовое задание — Тестовое задание на Angular. Создание UI kit для приложения.

Однако, попользовавшись выше приведенными решениями, я пришел к выводу, что нужно 3 устройства (экрана) — мобильное устройство, планшет и пк. То огромное множество вариантов, которое есть в Bootstrap’е избыточно, при этом, если охота использовать инкапсуляцию стилей, то необходимо минимизировать количество экранов.

Google рекомендует 3 экрана. Совпадение? Не думаю.

Предыдущей статье я разбирал примеры использования breakpoints из Angular CDK — Создание лейаутов и сеток с помощью Angular CDK и Angular Material. Если коротко, то CDK представляет следующий объект:

В данной статье рассмотрели представленные варианты размеров из AngularВ данном случае имеется следующая таблица:

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

Создание Grid модуля

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

ng g m grid

Добавим модули строк и столбцов:

ng g m row
ng g c row
ng g m column
ng g c column

Если использовать Nx, тогда можно все это вынести в отдельную либу и команды будут следующими:

nx g lib ui/grid 
nx g m row --project=ui-grid
nx g c row --project=ui-grid
nx g m column --project=ui-grid
nx g c column --project=ui-grid

Теперь создадим 3 директивы, которые будут указывать на применимость устройства, то есть если будет у компонента с колонкой директива tablet='2' это будет говорить о том, что это на планшете колонка будет равна 2.

ng g d handset
ng g d tablet
ng g d web

В итоге получим следующие директивы:

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

private size!: string | number | undefined;

@Input() set web(size: string | number | undefined) {
this.size = size;
}

@HostBinding('class.web') get isWeb(): boolean {
return typeof this.size === 'number' || typeof this.size === 'string';
}

В примере выше видно, что директива WebDirective добавляет соответствующий класс — @HostBinding(‘class.web’).

Интегрируем логику работы с колонками.

Принцип работы колонок прост:

  • Есть родитель, который имеет свойство display: flex. У родителя есть отрицательные отступы слева и справа.
  • У потомков есть положительные отступы слева и справа.

Тогда RowComponent:

Как видно, у класса строки есть свойство no-padding, которое обнуляет отступы слева и справа.

В данном случае важна реализация SCSS:

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

@mixin make-row($breakpoint) {
&[#{$breakpoint}] {
display: flex;
flex-direction: row;
flex-wrap: wrap;
height: 100%;
margin-left: -1rem;
margin-right: -1rem;
}
&[#{$breakpoint}].no-padding {
margin-left: 0;
margin-right: 0;
}
}

:host {
@include store-mixins.media-handset-up() {
@include make-row(handset);
}

@include store-mixins.media-tablet-up() {
@include make-row(tablet);
}

@include store-mixins.media-web-up() {
@include make-row(web);
}
}

Используя имя директивы в качестве селектора, можно навешивать разные CSS стили. Из-за того, что директива принимает значение, можно реализовать колонки. В данном случае мы получим следующий CSS:

[banshop-row][tablet] { display: flex; ... }

Отмечу, что banshop-row трансформируется в узел и итоговый стиль будет что-то наподобии[tablet][_nghost-banshop-c10] {}

Добавим логику для колонок ColumnComponent:

Как можно увидеть, в компоненте важны тоже стили SCSS:

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

@mixin make-columns($mode) {
&[#{$mode}] {
flex: 1 0 0;
padding: 0 1rem;
}

&[#{$mode}]:not([#{$mode}='']) {
flex: 0 0 auto;
}

:host-context(banshop-row.no-padding) &[#{$mode}] {
padding: 0;
}

@for $column from 1 through 12 {
&[#{$mode}='#{$column}'] {
width: math.percentage(math.div($column, 12));
}
}
}

:host {
display: block;

@include store-mixins.media-handset-up() {
@include make-columns(handset);
}

@include store-mixins.media-tablet-up() {
@include make-columns(tablet);
}

@include store-mixins.media-web-up() {
@include make-columns(web);
}
}

Аналогично строкам, для каждого устройства (экрана) добавляем соответствующие колонки (12 колонок, как в bootstrap).

В итоге будут стили вида:

@media (min-width: 600px) and (orientation: portrait), (min-width: 960px) and (orientation: landscape)
[banshop-column][tablet="6"] {
width: 50%;
}
[banshop-column][tablet="4"] {
width: 33.33%;
}
...

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

Создадим колонки, где на мобильном устройстве одна колонка, на планшете 2, а на пк 3.

Запустим и посмотрим:

Резюме

В данной статье разобрали решение для реализации колонок в Angular с помощью размеров из Angular CDK:

  • handset — мобильное устройство
  • tablet — планшет
  • web — пк

Для минимизации работы с колонками и уменьшения логики в компонентах, добавили 3 директивы, с помощью которых с помощью CSS реализовали изменение стилей.

В конце привели решение и демонстрацию работы созданных колонок.

Ссылки

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

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

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

--

--

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

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