Интеграция CSS framework’ов в Angular или темезируем Angular с помощью Material и Bootstrap

Aleksandr Serenko
F.A.F.N.U.R
Published in
5 min readAug 11, 2019

После настроенного CSS препроцессора sass в предыдущей статье, подключим Angular Material для создания общей темы приложения, а также добавим сетку и часть миксинов из Bootstrap, которые существенно упрощают верстку.

Установка зависимостей

Установим необходимые зависимости для Angular Material

yarn add @angular/material @angular/cdk @angular/animations

Установим сам Bootstrap

yarn add bootstrap

Отметим, что peer dependency для bootstrap (jquery, popper.js) устанавливать не нужно

warning " > bootstrap@4.3.1" has unmet peer dependency "jquery@1.9.1 - 3".
warning " > bootstrap@4.3.1" has unmet peer dependency "popper.js@^1.14.7".

Вы не сможете и не будете использовать стандартную реализацию JS компонентов в Bootstrap. Так как, bootstrap не имеет нативных компонентов для Angular, установим библиотеку с реализацией компонентов bootstrap в angular.

Есть два популярных решения это:

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

В данной статье будем использовать второе решение.

@ng-bootstrap/ng-bootstrap

Также для эффекта swipe’ов и др. событий в мобильных приложениях и браузере, подключим библиотеку Hammer.JS

yarn add hammerjs

Подключение Material

В продолжении серии статей посвященный monorepo для Angular с Nx, склонируем последний проект common-styles и назовем его theming.

Подключим Hammer.JS в main.ts файл приложения

// main.browser.ts
import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import 'hammerjs';
...

Добавляем Material icons в styles/common/fonts.scss

// prettier-ignore
@import url(//fonts.googleapis.com/css?family=Roboto:100,300,400,500,700&subset=cyrillic,cyrillic-ext);
@import url(//fonts.googleapis.com/icon?family=Material+Icons);

Angular Material для корректной работы анимации требует подключения модуля анимации — BrowserAnimationsModule.

Подключим его в AppBrowserModule (или AppModule, если вы не используете Universal)

@NgModule({
imports: [
...
BrowserAnimationsModule,
...
],
bootstrap: [AppComponent]
})
export class AppBrowserModule {}

Material по-умолчанию не задет тему оформления. Создадим и зададим тему для приложения.

Для этого создадим папку material в styles и создадим там файл theming.scss

@import '~@angular/material/theming';

@include mat-core();

$candy-app-primary: mat-palette($mat-indigo);
$candy-app-accent: mat-palette($mat-pink, A200, A100, A400);

$candy-app-warn: mat-palette($mat-red);

$candy-app-theme: mat-light-theme($candy-app-primary, $candy-app-accent, $candy-app-warn);

@include angular-material-theme($candy-app-theme);

Подробнее с темизацией Angular Material можно ознакомиться здесь.

Для того, чтобы можно было использовать material переменные и миксины в проекте, добавим импорт в styles/utils.scss

// External variables and mixins
@import '~@angular/material/theming';

...

Из-за того, что невозможно гарантировать, что абсолютно все приложения в workspace Nx будут использовать material и bootstrap, подключим тему непросредственно в проекте theming, а не на уровне common styles в папке styles/style.scss

В папке проекта apps/frontend/theming/src/styles/style.scss, подключим тему

// Import app utils
@import './utils';

// Import common material theme
@import "styles/material/theme";

// Import common styles
@import 'styles/style';

После того, как тема была подключена, изменим home module и сделаем вывод home component в стандартных компонента Angular Material.

В файле home.common.ts уберем последние преобразования с layouts и оставим просто home component

import { Routes } from '@angular/router';

import { HomeComponent } from './containers/home/home.component';

export const homeContainers: any[] = [HomeComponent];

export const homeRoutes: Routes = [
{
path: '',
component: HomeComponent
}
];

Подключим модуль Material Card в home module

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MatCardModule } from '@angular/material';
import { RouterModule } from '@angular/router';
import { TranslateModule } from '@ngx-translate/core';

import { homeContainers, homeRoutes } from './home.common';

@NgModule({
imports: [CommonModule, MatCardModule, RouterModule.forChild(homeRoutes), TranslateModule],
declarations: [...homeContainers]
})
export class HomeModule {}

И выведем card в home.component.html

<div class="home-page">
<h2>{{ 'home.title' | translate }}</h2>

<mat-card class="home-card">
<div class="home-langs">
<button type="button" (click)="translationFacade.setLanguage('ru')">RU</button> /
<button type="button" (click)="translationFacade.setLanguage('en')">En</button>
</div>

<div class="home-title">{{ 'home.count' | translate }}: {{ counter }}</div>

<div class="home-actions">
<button type="btn btn-default home-action" (click)="add()">
{{ 'home.actions.add' | translate }}
</button>
</div>
</mat-card>
</div>

Добавим стили, чтобы карточка не сливалась с фоном

@import 'utils';

:host {
background-color: mat-color($mat-gray, 200);
display: block;
height: 100vh;
}

.home-page {
padding: 1rem 2rem;
}

Запустим проект и посмотрим, что получилось

ng serve frontend-theming

Подключение Bootstrap

Angular Material корректно работает, остается подключить Bootrstrap grid, чтобы было все совсем хорошо.

Можно подключить bootstrap целиком, но это является плохим решением, так как не нужно импортировать те компоненты, которыми вы не будете пользоваться.

Создадим в common styles в папку bootstrap, в который добавим файл theme.scss

@import 'utils';

html {
box-sizing: border-box;
-ms-overflow-style: scrollbar;
}

*,
*::before,
*::after {
box-sizing: inherit;
}


@import '~bootstrap/scss/mixins/breakpoints';
@import '~bootstrap/scss/mixins/grid-framework';
@import '~bootstrap/scss/mixins/grid';

@import '~bootstrap/scss/grid';
@import '~bootstrap/scss/utilities/display';
@import '~bootstrap/scss/utilities/flex';
@import '~bootstrap/scss/utilities/spacing';

В styles/utils.scss добавляем импорт bootstrap переменных

// External variables and mixins
@import '~@angular/material/theming';

@import '~bootstrap/scss/functions';
@import '~bootstrap/scss/variables';
@import '~bootstrap/scss/mixins/breakpoints';

// Mixins
//@import './mixins';

// Variables
@import './variables';

Заметим, что в bootstrap/theme.scss подключается локальный utils, который определен в angular.json, для того, чтобы каждое приложение из Nx могло использовать и переопределять переменные для bootstrap максимально кастимизируя его, если это необходимо.

Подключим bootstrap grid в frontend/theming приложение.

В файле apps/frontend/theming/src/styles/style.scss:

// Import app utils
@import './utils';

// Import common theme
@import 'styles/material/theme';
@import 'styles/bootstrap/theme';

// Import common styles
@import 'styles/style';

Для того, чтобы понять работу grid, обернем вывод home component в контейнер bootstrap:

<div class="home-page">
<div class="container">
<h2>{{ 'home.title' | translate }}</h2>

<mat-card class="home-card">
<div class="home-langs">
<button type="button" (click)="translationFacade.setLanguage('ru')">RU</button> /
<button type="button" (click)="translationFacade.setLanguage('en')">En</button>
</div>

<div class="home-title">{{ 'home.count' | translate }}: {{ counter }}</div>

<div class="home-actions">
<button type="btn btn-default home-action" (click)="add()">
{{ 'home.actions.add' | translate }}
</button>
</div>
</mat-card>
</div>
</div>

Обновим и посмотрим, что получилось:

Angular with bootstrap

Bootstrap работает!

Исходники

Все исходники находятся на github, в репозитории https://github.com/Fafnur/medium-stories

Все было реализовано в проекте frontend/theming.

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

git checkout theming

Предыдущие статьи:

  1. Статья про LocalStorage, SessionStorage в Angular Universal
  2. Статья про мультиязычность в Angular & Universal с помощью ngx-translate
  3. Статья про Redux в Angular с помощью Ngrx. Создание Store в Angular
  4. Статья про Тестирование Ngrx State в Angular с помощью Jest
  5. Статья про Настройка CSS предпроцессора в Angular с Nx

Следующие статьи:

  1. Динамическое управление адаптивностью с помощью Angular и Redux

--

--

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

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