Как создать и развернуть приложение Angular Material
Опубликовано: 2022-03-10Angular — один из популярных вариантов при создании новых веб-приложений. Более того, спецификации «Материальный дизайн» сегодня стали лучшим выбором для создания минимального и увлекательного опыта. Таким образом, любой новый проект «Angular» в основном использует «Библиотеку дизайна материалов Angular» для использования компонентов, соответствующих спецификациям дизайна материалов. От плавной анимации до правильной обратной связи — все это уже доступно как часть официальной библиотеки дизайна материалов для angular.
После разработки веб-приложения следующим шагом будет его развертывание. Вот тут-то и появляется «Netlify». Благодаря очень простому в использовании интерфейсу, автоматическому развертыванию, разделению трафика для A/B-тестирования и множеству других функций Netlify, безусловно, является отличным инструментом.
Статья будет пошаговым руководством по созданию веб-приложения Angular 8 с использованием официальной библиотеки Angular Material Design. Мы будем создавать веб-приложение генератора QR-кода, полностью основанное на Angular, при размещении на Netlify.
Файлы для этого руководства можно найти на GitHub, а демо-версия развернута здесь.
Начиная
- Установите угловой 8,
- Создайте учетную запись GitHub,
- Установите Git на свой компьютер,
- Создайте учетную запись Netlify.
Примечание . Я буду использовать VSCode и Microsoft Windows в качестве предпочтительной IDE и ОС, хотя шаги будут аналогичны для любой другой IDE на любой другой ОС.
После того, как вышеуказанные предварительные условия выполнены, давайте начнем!
Макеты и планирование
Прежде чем мы начнем создавать проект, было бы полезно заранее спланировать: какой пользовательский интерфейс нам нужен в нашем приложении? Будут ли многоразовые детали? Как приложение будет взаимодействовать с внешними сервисами?
Во-первых, проверьте макеты пользовательского интерфейса.
Это три разные страницы, которые будут содержаться в приложении. Домашняя страница будет отправной точкой нашего приложения. Создание страницы QR должно быть связано с созданием нового QR-кода. На странице «История» будут показаны все сохраненные QR-коды.
Макеты не только дают представление о внешнем виде приложения, но и распределяют ответственность каждой страницы.
Одно наблюдение (из макетов) заключается в том, что кажется, что верхняя панель навигации является общей для всех страниц. Таким образом, панель навигации может быть создана как повторно используемый компонент и использоваться повторно.
Теперь, когда у нас есть некоторое представление о том, как будет выглядеть приложение и что можно использовать повторно, давайте начнем.
Создание нового углового проекта
Запустите VSCode, затем откройте окно терминала в VSCode, чтобы создать новый проект Angular.
Терминал откроется с путем по умолчанию, как показано в подсказке. Вы можете перейти в предпочтительный каталог, прежде чем продолжить; в случае Windows я буду использовать команду cd
.
Двигаясь вперед, у angular-cli есть команда для создания новых проектов ng new <project-name>
. Просто используйте любое причудливое имя проекта, которое вам нравится, и нажмите Enter, например, ng new qr
.
Это вызовет магию angular-cli; он предоставит несколько опций для настройки некоторых аспектов проекта, например, добавление угловой маршрутизации. Затем на основе выбранных параметров будет сгенерирован весь скелет проекта, который можно будет запустить без каких-либо изменений.
Для этого руководства введите « Да » для маршрутизации и выберите « CSS » для стиля. Это создаст новый проект Angular:
Теперь у нас есть полностью работающий проект Angular. Чтобы убедиться, что все работает правильно, мы можем запустить проект, введя эту команду в терминале: ng serve
. О, но подождите, это приводит к ошибке. Что могло случиться?
Не волнуйтесь. Всякий раз, когда вы создаете новый проект с помощью angular-cli, он генерирует весь скелет внутри папки, названной в честь имени проекта, указанного в команде ng new qr
. Здесь нам нужно будет изменить текущий рабочий каталог на только что созданный. В Windows используйте команду cd qr
для смены каталога.
Теперь попробуйте снова запустить проект с помощью ng serve
:
Откройте веб-браузер, перейдите по URL-адресу https://localhost:4200, чтобы увидеть, как работает проект. Команда ng serve
по умолчанию запускает приложение на порту 4200.
СОВЕТ : Чтобы запустить его на другом порту, мы используем команду ng serve --port <any-port>
, например, ng serve --port 3000
.
Это гарантирует, что наш базовый проект Angular запущен и работает. Давайте двигаться дальше.
Нам нужно добавить папку проекта в VSCode. Перейдите в меню «Файл», выберите «Открыть папку» и выберите папку проекта. Папка проекта теперь будет отображаться в проводнике слева.
Добавление библиотеки материалов Angular
Чтобы установить библиотеку материалов Angular, используйте следующую команду в окне терминала: ng add @angular/material
. Это (опять же) задаст некоторые вопросы, например, какую тему вы хотите, хотите ли вы анимацию по умолчанию, требуется ли поддержка сенсорного ввода, среди прочего. Мы просто выберем тему Indigo/Pink
по умолчанию, Yes
, чтобы добавить библиотеку HammerJS
и анимацию браузера.
Приведенная выше команда также настраивает весь проект, чтобы включить поддержку компонентов материала.
- Он добавляет зависимости проекта в package.json ,
- Он добавляет шрифт Roboto в файл index.html ,
- Он добавляет шрифт значка Material Design в ваш index.html ,
- Он также добавляет несколько глобальных стилей CSS:
- Удалить поля с тела,
- Установить
height: 100%
в HTML и теле, - Установите Roboto в качестве шрифта приложения по умолчанию.
Просто чтобы убедиться, что все в порядке, вы можете снова запустить проект на этом этапе, хотя вы не заметите ничего нового.
Добавление домашней страницы
Скелет нашего проекта готов. Начнем с добавления домашней страницы.
Мы хотим, чтобы наша домашняя страница была простой, как на картинке выше. Эта домашняя страница использует несколько угловых компонентов материала. Давайте разбирать.
- Верхняя панель представляет собой простой HTML-элемент
nav
, который содержит кнопку в стиле материала,mat-button
, с изображением и текстом в качестве дочернего элемента. Цвет полосы совпадает с основным цветом, выбранным при добавлении библиотеки материалов Angular; - центрированное изображение;
- Другой,
mat-button
, только с текстом в качестве дочернего элемента. Эта кнопка позволит пользователям перейти на страницу истории; - Значок подсчета,
matBadge
, прикрепленный к вышеуказанной кнопке и показывающий количество QR-кодов, сохраненных пользователем; - Плавающая кнопка действия,
mat-fab
, в правом нижнем углу с акцентным цветом из выбранной темы.
Немного отвлекшись, давайте сначала добавим другие необходимые компоненты и сервисы.
Добавление заголовка
Как и планировалось ранее, панель навигации должна использоваться повторно, давайте создадим ее как отдельный компонент angular. Откройте терминал в VSCode, введите ng gc header
(сокращение от ng generate component header) и нажмите Enter. Это создаст новую папку с именем «header», которая будет содержать четыре файла:
- header.component.css : используется для оформления этого компонента;
- header.component.html : для добавления элементов HTML;
- header.component.spec.ts : для написания тестовых случаев;
- header.component.ts : чтобы добавить логику на основе Typescript.
Чтобы заголовок выглядел так, как в моках, добавьте приведенный ниже HTML-код в header.component.html :
<nav class="navbar" [class.mat-elevation-z8]=true> <div> <button *ngIf="showBackButton" aria-hidden=false mat-icon-button routerLink="/"> <mat-icon> <i class="material-icons md-32">arrow_back</i> </mat-icon> </button> <span>{{currentTitle}}</span> </div> <button *ngIf="!showBackButton" aria-hidden=false mat-button class="button"> <img src="../../assets/qr-icon-white.png"> <span>QR Generator</span> </button> <button *ngIf="showHistoryNav" aria-hidden=false mat-button class="button" routerLink="/history"> <span>History</span> </button> </nav>
СОВЕТ : Чтобы добавить высоту для любого компонента материала, используйте [class.mat-elevation-z8]=true
, значение высоты можно изменить, изменив значение z , в данном случае это z8
. Например, чтобы изменить высоту на 16, используйте [class.mat-elevation-z16]=true
.
В приведенном выше фрагменте HTML используются два элемента материала Angular: mat-icon
и mat-button/mat-icon-button
. Их использование очень просто; во-первых, нам нужно добавить эти два модуля в наш app.module.ts , как показано ниже:
Это позволит нам использовать эти два элемента материала Angular где угодно в любом компоненте.
Для добавления кнопок материала используется следующий фрагмент HTML:
<button mat-button> Material Button </button>
В библиотеке материалов Angular доступны различные типы элементов кнопок материала, такие как mat-raised-button
, mat-flat-button
, mat-fab
и другие; просто замените mat-button
в приведенном выше фрагменте кода на любой другой тип.
Другой элемент — это mat-icon
, который используется для отображения значков, доступных в библиотеке значков материалов. Когда в начале была добавлена библиотека материалов Angular, была добавлена и ссылка на библиотеку значков материалов, что позволило нам использовать значки из огромного множества значков.
Использование так же просто, как:
<mat-icon> <i class="material-icons md-32">arrow_back</i> </mat-icon>
Вложенный <i>
можно использовать для изменения размера значка (здесь это md-32
), что сделает размер значка 32 пикселя по высоте и ширине. Это значение может быть md-24
, md-48
и так далее. Значением вложенного <i>
является имя значка. (Имя можно найти здесь для любого другого значка.)
Доступность
Всякий раз, когда используются значки или изображения, обязательно, чтобы они предоставляли достаточную информацию для целей доступности или для пользователя программы чтения с экрана. ARIA (Accessible Rich Internet Applications) определяет способ сделать веб-контент и веб-приложения более доступными для людей с ограниченными возможностями.
Следует отметить, что HTML-элементы, имеющие собственную семантику (например, nav
), не нуждаются в атрибутах ARIA; программа чтения с экрана уже знала бы, что nav
— это элемент навигации, и считывала бы его как таковой.
Спецификации ARIA разделены на три категории: роли, состояния и свойства. Предположим, что div
используется для создания индикатора выполнения в HTML-коде. У него нет родной семантики; Роль ARIA может описывать этот виджет как индикатор выполнения, свойство ARIA может обозначать его характеристику, например, его можно перетаскивать. Состояние ARIA будет описывать его текущее состояние, например текущее значение индикатора выполнения. См. фрагмент ниже:
<div role="progressbar" aria-valuenow="75" aria-valuemin="0" aria-valuemax="100"> </div>
Точно так же используется очень часто используемый атрибут aria: aria-hidden=true/false
. Значение true делает этот элемент невидимым для программ чтения с экрана.
Поскольку большинство элементов пользовательского интерфейса, используемых в этом приложении, имеют собственное семантическое значение, единственные используемые атрибуты ARIA предназначены для указания состояний видимости ARIA. Подробную информацию см. здесь.
Заголовок header.component.html содержит некоторую логику для скрытия и отображения кнопки «Назад» в зависимости от текущей страницы. Кроме того, кнопка «Домой» также содержит изображение/логотип, который следует добавить в папку /assets
. Загрузите образ отсюда и сохраните его в папке /assets
.
Для оформления панели навигации добавьте приведенный ниже css в header.component.css :
.navbar { position: fixed; top: 0; left: 0; right: 0; z-index: 2; background: #3f51b5; display: flex; flex-wrap: wrap; align-items: center; padding: 12px 16px; } .button { color: white; margin: 0px 10px; }
Поскольку мы хотим, чтобы компонент заголовка можно было повторно использовать в других компонентах, таким образом, чтобы решить, что следует отображать, мы потребуем их в качестве параметров от других компонентов. Это требует использования декоратора @Input()
, который будет привязываться к переменным, которые мы использовали в header.component.html .
Добавьте эти строки в файл header.component.ts :
// Add these three lines above the constructor entry. @Input() showBackButton: boolean; @Input() currentTitle: string; @Input() showHistoryNav: boolean; constructor() { }
Вышеупомянутые три привязки будут переданы в качестве параметра из других компонентов, которые будет использовать компонент заголовка. Его использование станет более понятным, когда мы продвинемся вперед.
Двигаясь дальше, нам нужно создать домашнюю страницу, которая может быть представлена компонентом Angular. Итак, давайте начнем с создания еще одного компонента; введите ng gc home
в терминале, чтобы автоматически сгенерировать домашний компонент. Как и ранее, будет создана новая папка с именем «home», содержащая четыре разных файла. Прежде чем приступить к изменению этих файлов, давайте добавим некоторую информацию о маршрутизации в модуль маршрутизации angular.
Добавление маршрутизации
Angular предоставляет способ сопоставления URL-адреса с определенным компонентом. Всякий раз, когда происходит какая-либо навигация, платформа Angular отслеживает URL-адрес и на основе информации, представленной в файле app-routing.module.ts ; он инициализирует отображенный компонент. Таким образом, разные компоненты не должны брать на себя ответственность за инициализацию других компонентов. В нашем случае приложение имеет три страницы, по которым можно перемещаться, нажимая разные кнопки. Мы достигаем этого, используя поддержку маршрутизации, предоставляемую фреймворком Angular.
Домашний компонент должен быть отправной точкой приложения. Давайте добавим эту информацию в файл app-routing.module.ts .
Свойство path
задается как пустая строка; это позволяет нам сопоставить URL-адрес приложения с компонентом домашней страницы, например, google.com
, который показывает домашнюю страницу Google.
СОВЕТ : Значение пути никогда не начинается с « /
», вместо этого используется пустая строка, даже если путь может быть похож на search/coffee
.
Вернувшись к компоненту домашней страницы, замените содержимое home.component.html следующим:
<app-header [showBackButton]="false" [currentTitle]=""></app-header> <app-profile></app-profile> <!-- FAB Fixed --> <button mat-fab class="fab-bottom-right" routerLink="/create"> <mat-icon> <i class="material-icons md-48">add</i> </mat-icon> </button>
Домашний компонент состоит из трех частей:
- Компонент многократно используемого заголовка
<app-header>
, - Компонент профиля
<app-profile>
, - Плавающая кнопка действия внизу справа.
Приведенный выше фрагмент HTML показывает, как повторно используемый компонент заголовка используется в других компонентах; мы просто используем селектор компонентов и передаем необходимые параметры.
Компонент профиля создан для использования в качестве тела главной страницы — мы создадим его в ближайшее время.
Плавающая кнопка действия со значком +
— это своего рода кнопка материала Angular типа mat-fab
в правом нижнем углу экрана. У него есть директива атрибута routerLink
, которая использует для навигации информацию о маршруте, предоставленную в app-routing.module.ts
. В этом случае кнопка имеет значение маршрута /create , которое будет сопоставлено для создания компонента.
Чтобы кнопка «Создать» плавала внизу справа, добавьте приведенный ниже код CSS в home.component.css :
.fab-bottom-right { position: fixed; left: auto; bottom: 5%; right: 10%; }
Поскольку компонент профиля должен управлять телом домашней страницы, мы оставим home.component.ts
нетронутым.
Добавление компонента профиля
Откройте терминал, введите ng gc profile
и нажмите Enter, чтобы сгенерировать компонент профиля. Как и планировалось ранее, этот компонент будет обрабатывать основную часть домашней страницы. Откройте profile.component.html
и замените его содержимое следующим:
<div class="center profile-child"> <img class="avatar" src="../../assets/avatar.png"> <div class="profile-actions"> <button mat-raised-button matBadge="{{historyCount}}" matBadgeOverlap="true" matBadgeSize="medium" matBadgeColor="accent" color="primary" routerLink="/history"> <span>History</span> </button> </div> </div>
Приведенный выше фрагмент HTML показывает, как использовать элемент matBadge
библиотеки материалов. Чтобы иметь возможность использовать его здесь, нам нужно выполнить обычную процедуру добавления MatBadgeModule
в файл app.module.ts
. Значки — это небольшие графические дескрипторы состояния для элементов пользовательского интерфейса, таких как кнопки, значки или тексты. В этом случае он используется с кнопкой, чтобы показать количество QR, сохраненных пользователем. Значок библиотеки материалов Angular имеет различные другие свойства, такие как установка положения значка с помощью matBadgePosition
, matBadgeSize
для указания размера и matBadgeColor
для установки цвета значка.
В папку с ресурсами необходимо добавить еще один ресурс изображения: Скачать. Сохраните то же самое в папке /assets
проекта.
Откройте profile.component.css и добавьте это:
.center { top: 50%; left: 50%; position: absolute; transform: translate(-50%, -50%); } .profile-child { display: flex; flex-direction: column; align-items: center; } .profile-actions { padding-top: 20px; } .avatar { border-radius: 50%; width: 180px; height: 180px; }
Вышеупомянутый CSS создаст пользовательский интерфейс, как и планировалось.
Двигаясь дальше, нам нужна какая-то логика для обновления значения счетчика истории, поскольку оно будет отражаться в использованном ранее matBadge
. Откройте profile.component.ts и соответствующим образом добавьте следующий фрагмент:
export class ProfileComponent implements OnInit { historyCount = 0; constructor(private storageUtilService: StorageutilService) { } ngOnInit() { this.updateHistoryCount(); } updateHistoryCount() { this.historyCount = this.storageUtilService.getHistoryCount(); } }
Мы добавили StorageutilService , но до сих пор не создали такой сервис. Игнорируя ошибку, мы завершили наш компонент профиля, который также завершает компонент нашей домашней страницы. Мы вернемся к этому компоненту профиля после создания службы утилиты хранения. Хорошо, тогда давайте так.
Локальное хранилище
HTML5 предоставляет функцию веб-хранилища, которую можно использовать для локального хранения данных. Это обеспечивает гораздо больше места для хранения по сравнению с файлами cookie — не менее 5 МБ против 4 КБ. Существует два типа веб-хранилищ с разным объемом и временем жизни: локальное и сеансовое . Первые могут хранить данные постоянно, а вторые — временно и для одного сеанса. Решение о выборе типа может быть основано на сценарии использования, в нашем сценарии мы хотим сохранять между сеансами, поэтому мы выберем локальное хранилище.
Каждый фрагмент данных хранится в паре ключ/значение. Мы будем использовать текст, для которого сгенерирован QR, в качестве ключа, а изображение QR, закодированное в виде строки base64, в качестве значения. Создайте папку сущности, внутри папки создайте новый файл qr-object.ts и добавьте фрагмент кода, как показано ниже:
Содержание класса:
export class QR { text: string; imageBase64: string; constructor(text: string, imageBase64: string) { this.imageBase64 = imageBase64; this.text = text; } }
Всякий раз, когда пользователь сохраняет сгенерированный QR, мы создаем объект вышеуказанного класса и сохраняем этот объект с помощью службы утилиты хранения.
Создайте новую папку сервисов, мы будем создавать много сервисов, лучше сгруппировать их вместе.
Измените текущий рабочий каталог на services, cd services
, чтобы создать новую службу, используя ng gs <any name>
. Это сокращение от ng generate service <any name>
, введите ng gs storageutil
и нажмите Enter.
Это создаст два файла:
- storageutil.service.ts
- storageutil.service.spec.ts
Последний предназначен для написания модульных тестов. Откройте storageutil.service.ts и добавьте это:
private historyCount: number; constructor() { } saveHistory(key : string, item :string) { localStorage.setItem(key, item) this.historyCount = this.historyCount + 1; } readHistory(key : string) : string { return localStorage.getItem(key) } readAllHistory() : Array<QR> { const qrList = new Array<QR>(); for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); const value = localStorage.getItem(key); if (key && value) { const qr = new QR(key, value); qrList.push(qr); } } this.historyCount = qrList.length; return qrList; } getHistoryCount(): number { if (this.historyCount) { return this.historyCount; } this.readAllHistory(); return this.historyCount; } deleteHistory(key : string) { localStorage.removeItem(key) this.historyCount = this.historyCount - 1; }
Импортируйте класс qr-object, чтобы исправить любые ошибки. Чтобы использовать функцию локального хранилища, нет необходимости импортировать что-либо новое, просто используйте ключевое слово localStorage
для сохранения или получения значения на основе ключа.
Теперь снова откройте файл profile.component.ts и импортируйте класс StorageutilService
, чтобы правильно завершить компонент профиля.
Запустив проект, мы видим, что домашняя страница работает, как и планировалось.
Добавление страницы создания QR
У нас есть готовая домашняя страница, хотя кнопка создать/добавить ничего не делает. Не волнуйтесь, фактическая логика уже написана. Мы использовали директиву routerLink
, чтобы изменить базовый путь URL-адреса на /create
, но сопоставление не было добавлено в файл app-routing.module.ts .
Давайте создадим компонент, который будет заниматься созданием новых QR-кодов, введите ng gc create-qr
и нажмите Enter, чтобы сгенерировать новый компонент.
Откройте файл app-routing.module.ts и добавьте следующую запись в массив routes
:
{ path: 'create', component: CreateQrComponent },
Это сопоставит CreateQRComponent
с URL-адресом /create
.
Откройте create-qr.components.html и замените содержимое следующим:
<app-header [showBackButton]="showBackButton" [currentTitle]="title" [showHistoryNav]="showHistoryNav"></app-header> <mat-card class="qrCard" [class.mat-elevation-z12]=true> <div class="qrContent"> <!--Close button section--> <div class="closeBtn"> <button mat-icon-button color="accent" routerLink="/" matTooltip="Close"> <mat-icon> <i class="material-icons md-48">close</i> </mat-icon> </button> </div> <!--QR code image section--> <div class="qrImgDiv"> <img *ngIf="!showProgressSpinner" src={{qrCodeImage}} width="200px" height="200px"> <mat-spinner *ngIf="showProgressSpinner"></mat-spinner> <div class="actionButtons" *ngIf="!showProgressSpinner"> <button mat-icon-button color="accent" matTooltip="Share this QR"> <mat-icon> <i class="material-icons md-48">share</i> </mat-icon> </button> <button mat-icon-button color="accent" (click)="saveQR()" matTooltip="Save this QR"> <mat-icon> <i class="material-icons md-48">save</i> </mat-icon> </button> </div> </div> <!--Textarea to write any text or link--> <div class="qrTextAreaDiv"> <mat-form-field> <textarea matInput [(ngModel)]="qrText" cdkTextareaAutosize cdkAutosizeMinRows="4" cdkAutosizeMaxRows="4" placeholder="Enter a website link or any text..."></textarea> </mat-form-field> </div> <!--Create Button--> <div class="createBtnDiv"> <button class="createBtn" mat-raised-button color="accent" matTooltip="Create new QR code" matTooltipPosition="above" (click)="createQrCode()">Create</button> </div> </div> </mat-card>
В приведенном выше фрагменте используются многие элементы библиотеки материалов Angular. Как и планировалось, он имеет одну ссылку на компонент заголовка, в которую передаются необходимые параметры. Далее идет основная часть страницы создания; он состоит из одной карты материала Angular или mat-card
центрированной и поднятой до 12 пикселей, поскольку [class.mat-elevation-z12]=true
.
Карта материала — это еще один тип контейнера, который можно использовать как любой другой тег div
. Хотя библиотека материалов предоставляет некоторые свойства для размещения четко определенной информации на mat-card
, такой как размещение изображения, заголовок, подзаголовок, описание и действие, как показано ниже.
В приведенном выше фрагменте HTML мы использовали mat-card
так же, как и любой другой контейнер. Другой используемый элемент библиотеки материалов — matTooltip
; это просто еще одна удобная всплывающая подсказка, отображаемая, когда пользователь наводит курсор или долго нажимает на элемент. Просто используйте фрагмент ниже, чтобы показать всплывающую подсказку:
matTooltip="Any text you want to show"
Его можно использовать с кнопками со значками или любым другим элементом пользовательского интерфейса для передачи дополнительной информации. В контексте приложения он отображает информацию о кнопке закрытия значка. Чтобы изменить размещение всплывающей подсказки, используется matTooltipPosition
:
matTooltip="Any text you want to show" matTooltipPosition="above"
Помимо matTooltip
, для отображения хода загрузки используется mat-spinner
. Когда пользователь нажимает кнопку «Создать», выполняется сетевой вызов. Это когда показывается счетчик прогресса. Когда сетевой вызов возвращает результат, мы просто прячем счетчик. Его можно использовать просто так:
<mat-spinner *ngIf="showProgressSpinner"></mat-spinner>
showProgressSpinner
— это логическая переменная, которая используется для отображения/скрытия индикатора выполнения. Библиотека также предоставляет некоторые другие параметры, такие как [color]='accent'
для изменения цвета, [mode]='indeterminate'
для изменения типа счетчика прогресса. Неопределенный счетчик прогресса не будет отображать ход выполнения задачи, в то время как определенный может иметь разные значения, отражающие ход выполнения задачи. Здесь используется неопределенный счетчик, поскольку мы не знаем, сколько времени займет сетевой вызов.
Библиотека материалов предоставляет вариант textarea, соответствующий руководству по материалам, но его можно использовать только как потомка mat-form-field
. Использование текстовой области материала так же просто, как и HTML по умолчанию, как показано ниже:
<mat-form-field> <textarea matInput placeholder="Hint text"></textarea> </mat-form-field>
matInput
— это директива, которая позволяет нативному тегу input
работать с mat-form-field
. Свойство placeholder
позволяет добавить любой текст подсказки для пользователя.
СОВЕТ . Используйте свойство текстового cdkTextareaAutosize
, чтобы сделать его автоматически изменяемым. Используйте cdkAutosizeMinRows
и cdkAutosizeMaxRows
, чтобы установить строки и столбцы, и все три вместе, чтобы сделать автоматическое изменение размера текстовой области до тех пор, пока не будет достигнуто максимальное количество строк и столбцов.
Чтобы использовать все эти элементы библиотеки материалов, нам нужно добавить их в файл app.module.ts .
В HTML используется замещающее изображение. Скачайте и сохраните его в папку /assets
.
Приведенный выше HTML-код также требует стилей CSS, поэтому откройте файл create-qr.component.ts и добавьте следующее:
.qrCard { display: flex; flex-direction: column; align-items: center; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 20%; height: 65%; padding: 50px 20px; } .qrContent { display: flex; flex-direction: column; align-items: center; width: 100%; } .qrTextAreaDiv { width: 100%; display: flex; flex-direction: row; justify-content: center; padding: 0px 0px; position: absolute; bottom: 10%; } .createBtn { left: 50%; transform: translate(-50%, 0px); width: 80%; } .createBtnDiv { position: absolute; bottom: 5%; width: 100%; } .closeBtn { display: flex; flex-direction: row-reverse; align-items: flex-end; width: 100%; margin-bottom: 20px; } .closeBtnFont { font-size: 32px; color: rgba(0,0,0,0.75); } .qrImgDiv { top: 20%; position: absolute; display: flex; flex-direction: column; align-items: center; justify-content: center; width: 100%; } .actionButtons { display: flex; flex-direction: row; padding-top: 20px; }
Давайте соединим пользовательский интерфейс с логикой. Откройте файл create-qr.component.ts и добавьте приведенный ниже код, оставив те строки, которые уже присутствуют:
export class CreateQrComponent implements OnInit { qrCodeImage = '../../../assets/download.png'; showProgressSpinner = false; qrText: string; currentQR; showBackButton = true; title = 'Generate New QR Code'; showHistoryNav = true; constructor(private snackBar: MatSnackBar, private restutil: RestutilService, private storageService: StorageutilService) { } ngOnInit() { } createQrCode() { //Check if any value is given for the qr code text if (!!this.qrText) { //Make the http call to load qr code this.loadQRCodeImage(this.qrText); } else { //Show snackbar this.showSnackbar('Enter some text first') } } public loadQRCodeImage(text: string) { // Show progress spinner as the request is being made this.showProgressSpinner = true; // Trigger the API call this.restutil.getQRCode(text).subscribe(image =>{ // Received the result - as an image blob - require parsing this.createImageBlob(image); }, error => { console.log('Cannot fetch QR code from the url', error) // Hide the spinner - show a proper error message this.showProgressSpinner = false; }); } private createImageBlob(image: Blob) { // Create a file reader to read the image blob const reader = new FileReader(); // Add event listener for "load" - invoked once the blob reading is complete reader.addEventListener('load', () => { this.qrCodeImage = reader.result.toString(); //Hide the progress spinner this.showProgressSpinner = false; this.currentQR = reader.result.toString(); }, false); // Read image blob if it is not null or undefined if (image) { reader.readAsDataURL(image); } } saveQR() { if (!!this.qrText) { this.storageService.saveHistory(this.qrText, this.currentQR); this.showSnackbar('QR saved') } else { //Show snackbar this.showSnackbar('Enter some text first') } } showSnackbar(msg: string) { //Show snackbar this.snackBar.open(msg, '', { duration: 2000, }); } }
Для предоставления пользователям контекстной информации мы также используем MatSnackBar
из библиотеки дизайна материалов. Это отображается как всплывающее окно снизу экрана и остается на несколько секунд, прежде чем исчезнуть. Это не элемент, а служба, которую можно вызвать из кода Typescript.
Приведенный выше фрагмент с именем метода showSnackbar
показывает, как открыть закусочную, но прежде чем его можно будет использовать, нам нужно добавить запись MatSnackBar
в файл app.module.ts , как мы делали это для других элементов библиотеки материалов.
СОВЕТ : В последних версиях библиотеки материалов Angular нет простого способа изменить стиль закусочной. Вместо этого нужно сделать два дополнения к коду.
Во-первых, используйте приведенный ниже CSS, чтобы изменить цвета фона и переднего плана:
::ng-deep snack-bar-container.snackbarColor { background-color: rgba(63, 81, 181, 1); } ::ng-deep .snackbarColor .mat-simple-snackbar { color: white; }
Во-вторых, используйте свойство с именем panelClass
, чтобы установить стиль для вышеуказанного класса CSS:
this.snackBar.open(msg, '', { duration: 2000, panelClass: ['snackbarColor'] });
Вышеупомянутые две комбинации позволяют настраивать стили для компонента закусочной библиотеки дизайна материалов.
На этом шаги по созданию QR-страницы завершены, но еще не хватает одного элемента. Проверка файла create-qr.component.ts покажет ошибку, касающуюся отсутствующей части. Недостающим элементом этой головоломки является RestutilService
, который отвечает за получение изображения QR-кода из стороннего API.
В терминале измените текущий каталог на services, введя ng gs restutil
и нажав Enter. Это создаст файлы RestUtilService. Откройте файл restutil.service.ts и добавьте этот фрагмент:
private edgeSize = '300'; private BASE_URL = 'https://api.qrserver.com/v1/create-qr-code/?data={data}!&size={edge}x{edge}'; constructor(private httpClient: HttpClient) { } public getQRCode(text: string): Observable { // Create the url with the provided data and other options let url = this.BASE_URL; url = url.replace("{data}", text).replace(/{edge}/g, this.edgeSize); // Make the http api call to the url return this.httpClient.get(url, { responseType: 'blob' }); }
private edgeSize = '300'; private BASE_URL = 'https://api.qrserver.com/v1/create-qr-code/?data={data}!&size={edge}x{edge}'; constructor(private httpClient: HttpClient) { } public getQRCode(text: string): Observable { // Create the url with the provided data and other options let url = this.BASE_URL; url = url.replace("{data}", text).replace(/{edge}/g, this.edgeSize); // Make the http api call to the url return this.httpClient.get(url, { responseType: 'blob' }); }
Вышеуказанный сервис извлекает изображение QR из стороннего API, и, поскольку ответ имеет не тип JSON, а изображение, поэтому мы указываем тип responseType
как 'blob'
в приведенном выше фрагменте.
Angular предоставляет класс HttpClient
для связи с любым сервером, поддерживающим HTTP. Он предоставляет множество функций, таких как фильтрация запроса до его запуска, получение ответа, возможность обработки ответа с помощью обратных вызовов и другие. Чтобы использовать то же самое, добавьте запись для HttpClientModule в файл app.module.ts .
Наконец, импортируйте этот сервис в файл create-qr.component.ts , чтобы завершить создание QR-кода.
Но ждать! Существует проблема с приведенной выше логикой создания QR. Если пользователь снова и снова использует один и тот же текст для создания QR, это приведет к сетевому вызову. Один из способов исправить это — кэшировать запрос на основе запроса, таким образом обслуживая ответ из кеша, если текст запроса тот же.
Запрос кэширования
Angular предоставляет упрощенный способ выполнения HTTP-вызовов, HttpClient вместе с HttpInterceptors для проверки и преобразования HTTP-запросов или ответов на серверы и с них. Его можно использовать для аутентификации или кэширования и многих других вещей, несколько перехватчиков могут быть добавлены и объединены в цепочку для дальнейшей обработки. В этом случае мы перехватываем запросы и обслуживаем ответ из кеша, если текст QR совпадает.
Создайте папку перехватчика, затем создайте файл cache-interceptor.ts :
Добавьте приведенный ниже фрагмент кода в файл:
import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpResponse, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { tap } from 'rxjs/operators'; import { of, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class RequestCachingService implements HttpInterceptor { private cacheMap = new Map<string, HttpResponse<any>>(); constructor() { } intercept(req: HttpRequest , next: HttpHandler): Observable<HttpEvent<any>> { const cachedResponse = this.cacheMap.get(req.urlWithParams); if (cachedResponse) { return of(cachedResponse); } return next.handle(req).pipe(tap(event => { if (event instanceof HttpResponse) { this.cacheMap.set(req.urlWithParams, event); } })) } }
import { Injectable } from '@angular/core'; import { HttpInterceptor, HttpResponse, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; import { tap } from 'rxjs/operators'; import { of, Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class RequestCachingService implements HttpInterceptor { private cacheMap = new Map<string, HttpResponse<any>>(); constructor() { } intercept(req: HttpRequest , next: HttpHandler): Observable<HttpEvent<any>> { const cachedResponse = this.cacheMap.get(req.urlWithParams); if (cachedResponse) { return of(cachedResponse); } return next.handle(req).pipe(tap(event => { if (event instanceof HttpResponse) { this.cacheMap.set(req.urlWithParams, event); } })) } }
В приведенном выше фрагменте кода у нас есть карта, где ключ является URL-адресом запроса, а ответ — значением. Проверяем, присутствует ли текущий URL на карте; if it is, then return the response (the rest is handled automatically). If the URL is not in the map, we add it.
We are not done yet. An entry to the app.module.ts is required for its proper functioning. Add the below snippet:
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { CacheInterceptor } from './interceptor/cache-interceptor'; providers: [ { provide: HTTP_INTERCEPTORS, useClass: CacheInterceptor, multi: true } ],
This adds the caching feature to our application. Let's move on to the third page, the History page.
Adding The History Page
All the saved QR codes will be visible here. To create another component, open terminal type ng gc history
and press Enter.
Open history.component.css and add the below code:
.main-content { padding: 5% 10%; } .truncate { width: 90%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .center-img { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); display: flex; flex-direction: column; align-items: center; }
Open history.component.html and replace the content with this:
<app-header [showBackButton]="showBackButton" [currentTitle]="title" [showHistoryNav]="showHistoryNav"></app-header> <div class="main-content"> <mat-grid-list cols="4" rowHeight="500px" *ngIf="historyList.length > 0"> <mat-grid-tile *ngFor="let qr of historyList"> <mat-card> <img mat-card-image src="{{qr.imageBase64}}"> <mat-card-content> <div class="truncate"> {{qr.text}} </div> </mat-card-content> <mat-card-actions> <button mat-button (click)="share(qr.text)">SHARE</button> <button mat-button color="accent" (click)="delete(qr.text)">DELETE</button> </mat-card-actions> </mat-card> </mat-grid-tile> </mat-grid-list> <div class="center-img" *ngIf="historyList.length == 0"> <img src="../../assets/no-see.png" width="256" height="256"> <span>Nothing to see here</span> </div> </div>
As usual, we have the header component at the top. Then, the rest of the body is a grid list that will show all the saved QR codes as individual mat-card
. For the grid view, we are using mat-grid-list
from the Angular material library. As per the drill, before we can use it, we have to first add it to the app.module.ts file.
Список матовых сеток действует как контейнер с несколькими дочерними элементами плитки, называемыми mat-grid-tile
. В приведенном выше фрагменте HTML каждая плитка создается с помощью mat-card
с использованием некоторых ее свойств для общего размещения других элементов пользовательского интерфейса. Мы можем указать number of columns
и rowHeight
, которые используются для автоматического расчета ширины. В приведенном выше фрагменте мы предоставляем как количество столбцов, так и значение rowHeight
.
Мы используем изображение-заполнитель, когда история пуста, загружаем его и добавляем в папку с ресурсами.
Чтобы реализовать логику заполнения всей этой информации, откройте файл history.component.ts и добавьте приведенный ниже фрагмент в класс HistoryComponent
:
showBackButton = true; title = 'History'; showHistoryNav = false; historyList; constructor(private storageService: StorageutilService, private snackbar: MatSnackBar ) { } ngOnInit() { this.populateHistory(); } private populateHistory() { this.historyList = this.storageService.readAllHistory(); } delete(text: string) { this.storageService.deleteHistory(text); this.populateHistory(); } share(text: string) { this.snackbar.open(text, '', {duration: 2000,}) }
Приведенная выше логика просто извлекает все сохраненные QR и заполняет ими страницу. Пользователи могут удалить сохраненный QR, что приведет к удалению записи из локального хранилища.
Итак, на этом наша историческая составляющая заканчивается... или нет? Нам все еще нужно добавить отображение маршрута для этого компонента. Откройте app-routing.module.ts и добавьте сопоставление для страницы истории:
{ path: 'history', component: HistoryComponent },
Весь массив маршрутов должен выглядеть следующим образом:
const routes: Routes = [ { path: '', component: HomeComponent }, { path: 'create', component: CreateQrComponent }, { path: 'history', component: HistoryComponent }, ];
Сейчас самое время запустить приложение, чтобы проверить весь поток, поэтому откройте терминал, введите ng serve
и нажмите Enter. Затем перейдите на localhost:4200
, чтобы проверить работу приложения.
Добавить на GitHub
Прежде чем перейти к этапу развертывания, было бы неплохо добавить проект в репозиторий GitHub.
- Откройте Гитхаб.
- Создайте новый репозиторий.
- В VS Code используйте терминал и следуйте первому набору команд, упомянутых в кратком руководстве, чтобы отправить все файлы проекта.
Просто обновите страницу, чтобы проверить, видны ли все файлы. С этого момента любые изменения git (такие как коммит, вытягивание/пуш) будут отражаться в этом вновь созданном репозитории.
Netlify и развертывание
Наше приложение работает на нашем локальном компьютере, но чтобы другие могли получить к нему доступ, мы должны развернуть его на облачной платформе и зарегистрировать на доменное имя. Вот где Netlify вступает в игру. Он предоставляет услуги непрерывного развертывания, интеграцию с GitHub и многие другие полезные функции. Прямо сейчас мы хотим включить глобальный доступ к нашему приложению. Давайте начнем.
- Зарегистрируйтесь на Netlify.
- На панели инструментов нажмите кнопку « Новый сайт из Git ».
- Нажмите на GitHub на следующем экране.
- Разрешите Netlify доступ к вашим репозиториям GitHub.
- Найдите и выберите только что созданный репозиторий
qr
. - Netlify на следующем шаге позволяет нам выбрать ветку репозитория GitHub для развертывания. Обычно используется
master
ветка, но также может быть отдельная веткаrelease
, которая содержит только связанные с выпуском и стабильные функции.
Поскольку это веб-приложение Angular, добавьте ng build --prod
в качестве команды сборки. Опубликованные каталоги будут dist/qr
, как указано в файле angular.json
.
Теперь нажмите кнопку « Deploy site
», которая запустит сборку проекта с помощью команды ng build --prod
и выведет файл в dist/qr
.
Поскольку мы предоставили Netlify информацию о пути, он автоматически выберет правильные файлы для обслуживания веб-приложения. Netlify по умолчанию добавляет в наше приложение случайный домен.
Теперь вы можете щелкнуть ссылку на странице выше, чтобы получить доступ к приложению из любого места. Наконец, приложение было развернуто.
Пользовательский домен
На изображении выше показан URL-адрес нашего приложения, а поддомен генерируется случайным образом. Давайте изменим это.
Нажмите кнопку « Domain settings
», затем в разделе «Пользовательские домены» щелкните меню с тремя точками и выберите « Edit site name
».
Это откроет всплывающее окно, в котором можно ввести новое имя сайта; это имя должно быть уникальным в домене Netlify. Введите любое доступное имя сайта и нажмите Сохранить .
Теперь ссылка на наше приложение будет обновлена с новым названием сайта.
Сплит-тестирование
Еще одна интересная функция, предлагаемая Netlify, — это сплит-тестирование. Это позволяет разделить трафик, чтобы разные наборы пользователей взаимодействовали с разными развертываниями приложений. Мы можем добавить новые функции в другую ветку и разделить трафик на развертывание этой ветки, проанализировать трафик, а затем объединить ветку функций с основной веткой развертывания. Давайте настроим его.
Необходимым условием для включения сплит-тестирования является наличие репозитория GitHub как минимум с двумя ветками. Перейдите в репозиторий приложений в GitHub, который был создан ранее, и создайте новую a
.
Репозиторий теперь будет иметь master
ветку и a
. Netlify необходимо настроить для развертывания филиалов, поэтому откройте панель инструментов Netlify и нажмите « Settings
». С левой стороны нажмите Build & Deploy
, затем Continuous Deployment
, затем с правой стороны в разделе Deploy contexts
нажмите Edit settings
.
В подразделе « Branch deploys
ветки» выберите параметр «Позвольте мне добавить отдельные ветки», введите имена веток и сохраните их.
Развертывание филиалов — еще одна полезная функция, предоставляемая Netlify; мы можем выбрать, какие ветки репозитория GitHub развернуть, а также включить предварительный просмотр для каждого запроса на включение в master
ветку перед слиянием. Это полезная функция, позволяющая разработчикам фактически протестировать свои изменения вживую, прежде чем добавлять свои изменения кода в основную ветку развертывания.
Теперь нажмите на вкладку « Split Testing
» в верхней части страницы. Конфигурации сплит-тестирования будут представлены здесь.
Мы можем выбрать ветку (кроме производственной ветки) — в данном случае a
. Мы также можем поиграться с настройками разделения трафика. В зависимости от процента трафика, выделенного каждой ветке, Netlify перенаправит некоторых пользователей к приложению, развернутому с помощью ветки a
а других — в master
ветку. После настройки нажмите кнопку « Start test
», чтобы включить разделение трафика.
СОВЕТ : Netlify может не распознать, что подключенный репозиторий GitHub имеет более одной ветки, и может выдать эту ошибку:
Чтобы решить эту проблему, просто повторно подключитесь к репозиторию из параметров Build & Deploy
.
Netlify также предоставляет множество других функций. Мы только что рассмотрели некоторые из его полезных функций, чтобы продемонстрировать простоту настройки различных аспектов Netlify.
Это подводит нас к концу нашего путешествия. Мы успешно создали дизайн Angular Material на основе веб-приложения и развернули его на Netlify.
Заключение
Angular — отличный и популярный фреймворк для разработки веб-приложений. С официальной библиотекой дизайна материалов Angular намного проще создавать приложения, которые соответствуют спецификациям дизайна материалов для очень естественного взаимодействия с пользователями. Более того, приложение, разработанное с использованием отличной среды, должно использовать отличную платформу для развертывания, и Netlify как раз подходит для этого. Благодаря постоянному развитию, отличной поддержке и множеству функций, это, безусловно, отличная платформа для массового распространения веб-приложений или статических сайтов. Надеемся, что эта статья поможет вам начать работу над новым проектом Angular, начиная от идеи и заканчивая развертыванием.
Дальнейшее чтение
- Угловая архитектура
- Дополнительные компоненты углового материала
- Подробнее о возможностях Netlify