Angular — это мощный фреймворк для создания современных веб-приложений. Он предоставляет разработчикам широкий набор инструментов и возможностей для построения масштабируемых и эффективных приложений. В этой статье будет подробно рассмотрен процесс создания первого приложения на Angular, начиная с настройки окружения и заканчивая развертыванием готового продукта.
Содержание:
- Подготовка окружения
- Создание нового проекта
- Структура проекта Angular
- Компоненты в Angular
- Шаблоны и стили
- Работа с данными и сервисы
- Маршрутизация
- Формы и валидация
- HTTP-запросы и взаимодействие с API
- Оптимизация производительности
- Тестирование приложения
- Сборка и деплой
Подготовка окружения
Перед началом работы над проектом необходимо убедиться, что на компьютере установлены все необходимые инструменты. Для разработки на Angular потребуются:
- Node.js (версия 12.x или выше)
- npm (Node Package Manager)
- Angular CLI (Command Line Interface)
Для установки Node.js нужно перейти на официальный сайт и скачать установщик для соответствующей операционной системы. После установки Node.js, npm будет автоматически установлен вместе с ним.
Следующим шагом является установка Angular CLI. Для этого нужно открыть терминал и выполнить следующую команду:
npm install -g @angular/cli
После успешной установки Angular CLI, можно приступать к созданию первого проекта.
Создание нового проекта
Для создания нового проекта Angular используется команда ng new. Откройте терминал, перейдите в директорию, где будет располагаться проект, и выполните следующую команду:
ng new my-first-angular-app
Angular CLI задаст несколько вопросов о конфигурации проекта:
- Would you like to add Angular routing? (Y/n) — Добавить ли маршрутизацию?
- Which stylesheet format would you like to use? — Какой формат стилей использовать?
Для первого проекта можно выбрать «Yes» для маршрутизации и CSS в качестве формата стилей.
После ответа на вопросы, Angular CLI создаст новый проект с базовой структурой и установит все необходимые зависимости. Этот процесс может занять несколько минут.
Структура проекта Angular
После создания проекта, можно изучить его структуру. Основные файлы и директории:
- src/ — исходный код приложения
- src/app/ — компоненты, сервисы и модули приложения
- src/assets/ — статические файлы (изображения, шрифты и т.д.)
- src/environments/ — конфигурации для разных окружений (разработка, продакшн)
- src/index.html — главный HTML-файл приложения
- src/main.ts — точка входа в приложение
- src/styles.css — глобальные стили
- angular.json — конфигурация Angular CLI
- package.json — зависимости и скрипты npm
- tsconfig.json — конфигурация TypeScript
Теперь можно запустить приложение в режиме разработки с помощью команды:
ng serve
После выполнения этой команды, приложение будет доступно по адресу http://localhost:4200/.
Компоненты в Angular
Компоненты — это основные строительные блоки Angular-приложений. Каждый компонент состоит из трех основных частей:
- Шаблон (HTML)
- Стили (CSS)
- Класс компонента (TypeScript)
Давайте создадим новый компонент с помощью Angular CLI:
ng generate component hello-world
Эта команда создаст новый компонент HelloWorldComponent и добавит его в модуль приложения. Теперь отредактируем файл src/app/hello-world/hello-world.component.ts:
import { Component } from '@angular/core'; @Component({ selector: 'app-hello-world', templateUrl: './hello-world.component.html', styleUrls: ['./hello-world.component.css'] }) export class HelloWorldComponent { message: string = 'Привет, Angular!'; }
Затем обновим шаблон компонента в файле src/app/hello-world/hello-world.component.html:
<h1>{{ message }}</h1>
Теперь можно использовать этот компонент в главном компоненте приложения. Отредактируем файл src/app/app.component.html:
<h1>Мое первое Angular-приложение</h1> <app-hello-world></app-hello-world>
Шаблоны и стили
Angular использует расширенный синтаксис HTML для создания динамических шаблонов. Вот некоторые основные концепции:
- Интерполяция: {{ выражение }}
- Привязка свойств: [свойство]=»выражение»
- Привязка событий: (событие)=»обработчик()»
- Двусторонняя привязка: [(ngModel)]=»свойство»
Давайте обновим наш компонент HelloWorld, добавив некоторые из этих концепций:
import { Component } from '@angular/core'; @Component({ selector: 'app-hello-world', templateUrl: './hello-world.component.html', styleUrls: ['./hello-world.component.css'] }) export class HelloWorldComponent { message: string = 'Привет, Angular!'; buttonText: string = 'Нажми меня'; clickCount: number = 0; onButtonClick() { this.clickCount++; this.message = `Кнопка была нажата ${this.clickCount} раз`; } }
Обновим шаблон компонента:
<h1>{{ message }}</h1> <button [disabled]="clickCount > 10" (click)="onButtonClick()">{{ buttonText }}</button> <p>Количество кликов: {{ clickCount }}</p>
Для стилизации компонента можно использовать файл hello-world.component.css. Например:
h1 { color: #3f51b5; } button { background-color: #3f51b5; color: white; border: none; padding: 10px 20px; cursor: pointer; } button:disabled { background-color: #ccc; cursor: not-allowed; } p { font-style: italic; }
Работа с данными и сервисы
Сервисы в Angular используются для централизованного хранения и обработки данных, а также для выполнения бизнес-логики. Давайте создадим простой сервис для работы с данными:
ng generate service data
Отредактируем файл src/app/data.service.ts:
import { Injectable } from '@angular/core'; @Injectable({ providedIn: 'root' }) export class DataService { private data: string[] = ['Элемент 1', 'Элемент 2', 'Элемент 3']; getData(): string[] { return this.data; } addData(item: string): void { this.data.push(item); } }
Теперь обновим наш компонент HelloWorld, чтобы использовать этот сервис:
import { Component } from '@angular/core'; import { DataService } from '../data.service'; @Component({ selector: 'app-hello-world', templateUrl: './hello-world.component.html', styleUrls: ['./hello-world.component.css'] }) export class HelloWorldComponent { items: string[] = []; newItem: string = ''; constructor(private dataService: DataService) { this.items = this.dataService.getData(); } addItem() { if (this.newItem.trim() !== '') { this.dataService.addData(this.newItem); this.items = this.dataService.getData(); this.newItem = ''; } } }
Обновим шаблон компонента:
<h2>Список элементов</h2> <ul> <li *ngFor="let item of items">{{ item }}</li> </ul> <input [(ngModel)]="newItem" placeholder="Новый элемент"> <button (click)="addItem()">Добавить</button>
Не забудьте импортировать FormsModule в файле app.module.ts для использования [(ngModel)]:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { HelloWorldComponent } from './hello-world/hello-world.component'; @NgModule({ declarations: [ AppComponent, HelloWorldComponent ], imports: [ BrowserModule, FormsModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Маршрутизация
Маршрутизация позволяет создавать приложения с несколькими страницами. Давайте добавим еще один компонент и настроим маршрутизацию между ними:
ng generate component about
Теперь отредактируем файл src/app/app-routing.module.ts:
import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; import { HelloWorldComponent } from './hello-world/hello-world.component'; import { AboutComponent } from './about/about.component'; const routes: Routes = [ { path: '', component: HelloWorldComponent }, { path: 'about', component: AboutComponent } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
Обновим главный компонент приложения в файле src/app/app.component.html:
<nav> <ul> <li><a routerLink="/">Главная</a></li> <li><a routerLink="/about">О нас</a></li> </ul> </nav> <router-outlet></router-outlet>
Формы и валидация
Angular предоставляет мощные инструменты для работы с формами и их валидации. Давайте создадим простую форму регистрации с валидацией:
ng generate component registration-form
Отредактируем файл src/app/registration-form/registration-form.component.ts:
import { Component } from '@angular/core'; import { FormBuilder, FormGroup, Validators } from '@angular/forms'; @Component({ selector: 'app-registration-form', templateUrl: './registration-form.component.html', styleUrls: ['./registration-form.component.css']
})
export class RegistrationFormComponent {
registrationForm: FormGroup;
constructor(private formBuilder: FormBuilder) {
this.registrationForm = this.formBuilder.group({
username: ['', [Validators.required, Validators.minLength(3)]],
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(6)]],
confirmPassword: ['', Validators.required]
}, { validator: this.passwordMatchValidator });
}
passwordMatchValidator(form: FormGroup) {
const password = form.get('password');
const confirmPassword = form.get('confirmPassword');
if (password && confirmPassword && password.value !== confirmPassword.value) {
confirmPassword.setErrors({ passwordMismatch: true });
} else {
confirmPassword?.setErrors(null);
}
}
onSubmit() {
if (this.registrationForm.valid) {
console.log('Форма отправлена', this.registrationForm.value);
} else {
console.log('Форма содержит ошибки');
}
}
}
Теперь создадим шаблон формы в файле src/app/registration-form/registration-form.component.html:
<form [formGroup]="registrationForm" (ngSubmit)="onSubmit()"> <div> <label for="username">Имя пользователя:</label> <input id="username" type="text" formControlName="username"> <div *ngIf="registrationForm.get('username')?.invalid && registrationForm.get('username')?.touched"> <p *ngIf="registrationForm.get('username')?.errors?.['required']">Имя пользователя обязательно</p> <p *ngIf="registrationForm.get('username')?.errors?.['minlength']">Минимальная длина имени - 3 символа</p> </div> </div> <div> <label for="email">Email:</label> <input id="email" type="email" formControlName="email"> <div *ngIf="registrationForm.get('email')?.invalid && registrationForm.get('email')?.touched"> <p *ngIf="registrationForm.get('email')?.errors?.['required']">Email обязателен</p> <p *ngIf="registrationForm.get('email')?.errors?.['email']">Некорректный формат email</p> </div> </div> <div> <label for="password">Пароль:</label> <input id="password" type="password" formControlName="password"> <div *ngIf="registrationForm.get('password')?.invalid && registrationForm.get('password')?.touched"> <p *ngIf="registrationForm.get('password')?.errors?.['required']">Пароль обязателен</p> <p *ngIf="registrationForm.get('password')?.errors?.['minlength']">Минимальная длина пароля - 6 символов</p> </div> </div> <div> <label for="confirmPassword">Подтверждение пароля:</label> <input id="confirmPassword" type="password" formControlName="confirmPassword"> <div *ngIf="registrationForm.get('confirmPassword')?.invalid && registrationForm.get('confirmPassword')?.touched"> <p *ngIf="registrationForm.get('confirmPassword')?.errors?.['required']">Подтверждение пароля обязательно</p> <p *ngIf="registrationForm.get('confirmPassword')?.errors?.['passwordMismatch']">Пароли не совпадают</p> </div> </div> <button type="submit" [disabled]="registrationForm.invalid">Зарегистрироваться</button> </form>
Не забудьте импортировать ReactiveFormsModule в файле app.module.ts:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { ReactiveFormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { RegistrationFormComponent } from './registration-form/registration-form.component'; @NgModule({ declarations: [ AppComponent, RegistrationFormComponent ], imports: [ BrowserModule, ReactiveFormsModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
HTTP-запросы и взаимодействие с API
Angular предоставляет HttpClient для выполнения HTTP-запросов к серверу. Давайте создадим сервис для работы с API:
ng generate service api
Отредактируем файл src/app/api.service.ts:
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class ApiService { private apiUrl = 'https://jsonplaceholder.typicode.com/posts'; constructor(private http: HttpClient) { } getPosts(): Observable { return this.http.get(this.apiUrl); } createPost(post: any): Observable { return this.http.post(this.apiUrl, post); } }
Теперь создадим компонент для отображения постов:
ng generate component posts
Отредактируем файл src/app/posts/posts.component.ts:
import { Component, OnInit } from '@angular/core'; import { ApiService } from '../api.service'; @Component({ selector: 'app-posts', templateUrl: './posts.component.html', styleUrls: ['./posts.component.css'] }) export class PostsComponent implements OnInit { posts: any[] = []; newPost = { title: '', body: '' }; constructor(private apiService: ApiService) { } ngOnInit() { this.loadPosts(); } loadPosts() { this.apiService.getPosts().subscribe( (data) => { this.posts = data; }, (error) => { console.error('Ошибка при загрузке постов', error); } ); } createPost() { this.apiService.createPost(this.newPost).subscribe( (response) => { console.log('Пост создан', response); this.posts.unshift(response); this.newPost = { title: '', body: '' }; }, (error) => { console.error('Ошибка при создании поста', error); } ); } }
Создадим шаблон для компонента в файле src/app/posts/posts.component.html:
<h2>Посты</h2> <form (ngSubmit)="createPost()"> <input [(ngModel)]="newPost.title" name="title" placeholder="Заголовок"> <textarea [(ngModel)]="newPost.body" name="body" placeholder="Текст поста"></textarea> <button type="submit">Создать пост</button> </form> <ul> <li *ngFor="let post of posts"> <h3>{{ post.title }}</h3> <p>{{ post.body }}</p> </li> </ul>
Не забудьте импортировать HttpClientModule в файле app.module.ts:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { HttpClientModule } from '@angular/common/http'; import { FormsModule } from '@angular/forms'; import { AppComponent } from './app.component'; import { PostsComponent } from './posts/posts.component'; @NgModule({ declarations: [ AppComponent, PostsComponent ], imports: [ BrowserModule, HttpClientModule, FormsModule ], providers: [], bootstrap: [AppComponent] }) export class AppModule { }
Оптимизация производительности
Для оптимизации производительности Angular-приложения можно использовать несколько техник:
- Ленивая загрузка модулей: Разделите приложение на модули и загружайте их по мере необходимости. Это уменьшит начальный размер бандла и ускорит загрузку приложения.
- Использование OnPush стратегии обнаружения изменений: Это поможет уменьшить количество проверок изменений в компонентах.
- Виртуальная прокрутка: Для больших списков используйте виртуальную прокрутку, чтобы отображать только видимые элементы.
- Кэширование: Используйте сервисы для кэширования данных, чтобы уменьшить количество запросов к серверу.
- Оптимизация изображений: Используйте оптимизированные изображения и ленивую загрузку для них.
Давайте рассмотрим пример использования OnPush стратегии обнаружения изменений:
import { Component, ChangeDetectionStrategy, Input } from '@angular/core'; @Component({ selector: 'app-optimized-component', template: ` <h2>{{ title }}</h2> <p>{{ content }}</p> `, changeDetection: ChangeDetectionStrategy.OnPush }) export class OptimizedComponent { @Input() title: string = ''; @Input() content: string = ''; }
Теперь этот компонент будет обновляться только при изменении входных данных, что может значительно улучшить производительность для компонентов, которые не часто меняются.
Тестирование приложения
Тестирование является важной частью разработки Angular-приложений. Angular предоставляет инструменты для модульного тестирования (unit testing) и интеграционного тестирования (integration testing). Давайте рассмотрим пример написания модульного теста для компонента:
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { HelloWorldComponent } from './hello-world.component'; describe('HelloWorldComponent', () => { let component: HelloWorldComponent; let fixture: ComponentFixture; beforeEach(async () => { await TestBed.configureTestingModule({ declarations: [ HelloWorldComponent ] }) .compileComponents(); }); beforeEach(() => { fixture = TestBed.createComponent(HelloWorldComponent); component = fixture.componentInstance; fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); it('should have a message', () => { expect(component.message).toBe('Привет, Angular!'); }); it('should increment clickCount on button click', () => { component.onButtonClick(); expect(component.clickCount).toBe(1); }); });
Для запуска тестов используется команда:
ng test
Эта команда запустит Karma — тестовый раннер, который выполнит все тесты в проекте и отобразит результаты.
Сборка и деплой
Когда приложение готово к выпуску, его нужно собрать для продакшена. Angular CLI предоставляет команду для создания оптимизированной сборки:
ng build --prod
Эта команда создаст оптимизированную версию приложения в директории dist/. Файлы в этой директории можно затем развернуть на веб-сервере.
Для простого деплоя можно использовать такие сервисы, как Firebase Hosting или GitHub Pages. Например, для деплоя на GitHub Pages можно использовать следующие шаги:
- Установите пакет angular-cli-ghpages:
npm install -g angular-cli-ghpages
- Соберите приложение с правильным базовым URL:
ng build --prod --base-href "https://<username>.github.io/<repository-name>/"
- Разверните приложение на GitHub Pages:
ngh --dir=dist/<project-name>
После этих шагов приложение будет доступно по URL https://<username>.github.io/<repository-name>/.