Angular — это среда JavaScript на стороне клиента, которая позволяет создавать приложения типа «Одностраничное приложение» (SPA). Он действительно основан на концепции архитектуры MVC (Model View Controller) или, точнее, на архитектуре MVVM (Model View ViewModel), которая позволяет разделять данные, представления и различные действия. Он использует TypeScript, а не JavaScript.

Что такое TypeScript и почему TypeScript?

TypeScript — это надмножество JavaScript со статическими типами, которое добавляет в язык необязательные аннотации типов. Он предоставляет расширенные инструменты и помогает выявлять ошибки во время разработки, повышая удобство сопровождения и читабельность кода.

В Angular TypeScript используется в качестве основного языка, поскольку он позволяет разработчикам более эффективно создавать крупномасштабные приложения. Он обеспечивает лучшую организацию кода, проверку типов и поддержку редактора, упрощая работу со сложными проектами Angular и снижая вероятность ошибок во время выполнения.

Среда разработки:

Чтобы запустить проект Angular, на вашем компьютере должно быть установлено следующее:

  1. Node.js: Angular требует Node.js, который включает npm (диспетчер пакетов узлов) для управления зависимостями и пакетами.
  2. Angular CLI (интерфейс командной строки): после установки Node.js вы можете глобально установить Angular CLI с помощью npm. Angular CLI предоставляет набор команд для простого создания проектов Angular и управления ими.

Создайте свой первый проект:

ng new my-first-project

Обслуживать проект:

ng serve

Структура проекта:

Добавить компонент:

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

ng generate component my-first-component

  1. Angular CLI создает новую папку с именем my-first-component внутри папки app вашего проекта. Эта папка будет содержать все файлы, относящиеся к только что сгенерированному компоненту.

2. Внутри папки my-first-component Angular CLI создает следующие файлы:

  • my-first-component.component.ts: это файл TypeScript для компонента. Он содержит класс компонента с логикой и свойствами.
  • my-first-component.component.html: Это файл шаблона HTML для компонента. Он определяет структуру представления компонента.
  • my-first-component.component.css: это файл CSS для компонента. Он содержит стили, специфичные для этого компонента.
  • my-first-component.component.spec.ts: Это тестовый файл для компонента. Он содержит тестовые примеры, чтобы убедиться, что компонент работает должным образом.

3. Angular CLI также обновляет файл app.module.ts (или соответствующий файл модуля), чтобы добавить вновь сгенерированный компонент в массив объявлений. Этот шаг регистрации делает компонент доступным для использования в вашем приложении.

Управление динамическими данными:

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

  1. Привязка данных: Angular предоставляет мощные возможности привязки данных, которые позволяют синхронизировать ваши шаблоны и данные. Вы можете использовать одностороннюю привязку данных ({{ data }}) или двустороннюю привязку данных ([(ngModel)]) для обновления представления при изменении базовых данных и наоборот.

Вот простой пример привязки данных в Angular:

  1. Создайте новый компонент Angular. Назовем его ExampleComponent.
  2. В файле example.component.ts определите свойство с именем message с некоторым начальным значением:
// example.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-example',
  templateUrl: './example.component.html',
  styleUrls: ['./example.component.css']
})
export class ExampleComponent {
  message: string = "Hello, World!";
}

В файле example.component.html используйте интерполяцию ({{ }}) для отображения свойства message:

<!-- example.component.html -->

<div>
  <p>{{ message }}</p>
</div>

Теперь всякий раз, когда вы используете ExampleComponent в своем приложении, значение свойства message будет отображаться в шаблоне. Привязка данных синхронизирует представление со свойством компонента.

Например, если вы включите ExampleComponent в другой шаблон:

<!-- app.component.html -->

<h1>Angular Data Binding Example</h1>
<app-example></app-example>

Вот пример двусторонней привязки данных с использованием [(ngModel)]:

  1. Создайте новый компонент Angular, назовем его FormComponent.
  2. В файле form.component.ts определите свойство с именем name с начальным значением:
// form.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-form',
  templateUrl: './form.component.html',
  styleUrls: ['./form.component.css']
})
export class FormComponent {
  name: string = "John";
}

В файле form.component.html создайте форму с полем ввода и используйте [(ngModel)] для двусторонней привязки данных:

<!-- form.component.html -->

<form>
  <label for="nameInput">Name:</label>
  <input type="text" id="nameInput" [(ngModel)]="name">
</form>

<p>Hello, {{ name }}!</p>

Импортируйте FormsModule в свой модуль (обычно app.module.ts), чтобы разрешить использование [(ngModel)]:

// app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'; // Import FormsModule

import { AppComponent } from './app.component';
import { FormComponent } from './form.component';

@NgModule({
  declarations: [AppComponent, FormComponent],
  imports: [BrowserModule, FormsModule], // Add FormsModule to imports
  bootstrap: [AppComponent]
})
export class AppModule {}

2. Свойства компонента. Компоненты в Angular могут иметь свойства, содержащие динамические данные. Эти свойства можно обновить из кода TypeScript компонента, и изменения будут автоматически отражены в шаблоне.

Вот пример того, как вы можете определить свойства компонента:

  1. Создайте новый компонент Angular, назовем его CounterComponent.
  2. В файле counter.component.ts определите свойство с именем count с начальным значением:
// counter.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-counter',
  templateUrl: './counter.component.html',
  styleUrls: ['./counter.component.css']
})
export class CounterComponent {
  count: number = 0;
}

В файле counter.component.html отобразите свойство count и добавьте кнопки для увеличения и уменьшения его значения:

<!-- counter.component.html -->

<div>
  <p>Count: {{ count }}</p>
  <button (click)="increment()">Increment</button>
  <button (click)="decrement()">Decrement</button>
</div>

В counter.component.ts добавьте два метода для обновления свойства count:

// counter.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-counter',
  templateUrl: './counter.component.html',
  styleUrls: ['./counter.component.css']
})
export class CounterComponent {
  count: number = 0;

  increment() {
    this.count++;
  }

  decrement() {
    this.count--;
  }
}

Теперь всякий раз, когда вы используете CounterComponent в своем приложении, вы увидите начальное значение счетчика (0), отображаемое на странице, вместе с двумя кнопками для его увеличения и уменьшения.

Например, если вы включите CounterComponent в другой шаблон:

<!-- app.component.html -->

<h1>Component Properties Example</h1>
<app-counter></app-counter>

3. Службы. Службы — это способ обмена данными и логикой между несколькими компонентами. Вы можете использовать службы для извлечения данных из API, баз данных или других источников и централизованного управления ими.

Вот пример того, как создать и использовать сервис для извлечения данных из вымышленного API и совместного использования их несколькими компонентами:

  1. Создайте новый сервис Angular, назовем его DataService.
  2. В файле data.service.ts определите метод получения данных из API:
// data.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  private apiUrl = 'https://api.example.com/data'; // Replace with your API URL

  constructor(private http: HttpClient) {}

  getData(): Observable<any> {
    return this.http.get<any>(this.apiUrl);
  }
}

В файле app.module.ts обязательно импортируйте HttpClientModule для использования службы HttpClient в файле DataService:

// app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http'; // Import HttpClientModule

import { AppComponent } from './app.component';

@NgModule({
  declarations: [AppComponent],
  imports: [BrowserModule, HttpClientModule], // Add HttpClientModule to imports
  bootstrap: [AppComponent]
})
export class AppModule {}

Теперь вы можете использовать DataService в любом компоненте для получения данных из API. Например, в DataComponent вы можете внедрить DataService и вызвать метод getData():

// data.component.ts

import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-data',
  templateUrl: './data.component.html',
  styleUrls: ['./data.component.css']
})
export class DataComponent implements OnInit {
  data: any;

  constructor(private dataService: DataService) {}

  ngOnInit() {
    this.dataService.getData().subscribe((response) => {
      this.data = response;
    });
  }
}

В data.component.html вы можете отображать данные, полученные из API:

<!-- data.component.html -->

<div *ngIf="data">
  <h2>Data from API:</h2>
  <ul>
    <li *ngFor="let item of data">{{ item.name }}</li>
  </ul>
</div>

4. Observables: Angular использует RxJS, который обеспечивает концепцию Observables. Наблюдаемые объекты используются для обработки асинхронных операций и управления потоками данных. Они обычно используются при работе с данными из HTTP-запросов или соединений WebSocket.

Вот пример того, как использовать Observables с RxJS в Angular для обработки асинхронного извлечения данных с помощью HTTP-запроса:

Импортируйте необходимые модули и сервисы в файл data.service.ts:

// data.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { catchError } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class DataService {
  private apiUrl = 'https://api.example.com/data'; // Replace with your API URL

  constructor(private http: HttpClient) {}

  getData(): Observable<any> {
    return this.http.get<any>(this.apiUrl).pipe(
      catchError((error) => {
        console.error('Error fetching data:', error);
        return [];
      })
    );
  }
}

В файле data.component.ts используйте каналы DataService и async для обработки асинхронного извлечения данных:

// data.component.ts

import { Component } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-data',
  templateUrl: './data.component.html',
  styleUrls: ['./data.component.css']
})
export class DataComponent {
  data$ = this.dataService.getData();

  constructor(private dataService: DataService) {}
}

В data.component.html используйте канал async для обработки подписки на Observable и отображения данных:

<!-- data.component.html -->

<div *ngIf="data$ | async as data">
  <h2>Data from API:</h2>
  <ul>
    <li *ngFor="let item of data">{{ item.name }}</li>
  </ul>
</div>

В этом примере переменная data$ содержит Observable, возвращаемый методом getData() в файле DataService. Канал async автоматически подписывается на этот Observable, обрабатывает асинхронное извлечение данных и обновляет шаблон последними данными по мере их поступления.

5. Библиотеки управления состоянием. Для более сложных приложений вы можете рассмотреть возможность использования библиотек управления состоянием, таких как NgRx или Akita. Эти библиотеки помогают управлять состоянием вашего приложения предсказуемым и эффективным способом, особенно при работе с крупномасштабными проектами.

Вот пример того, как использовать NgRx, популярную библиотеку управления состоянием для Angular, для управления состоянием простого приложения:

  1. Установите необходимые зависимости:
  • Магазин NgRx: основная библиотека управления состоянием для управления состоянием приложения.
  • NgRx Store Devtools: расширение браузера для отладки состояния NgRx.
  • Эффекты NgRx: библиотека для обработки побочных эффектов, таких как выполнение HTTP-запросов.
npm install @ngrx/store @ngrx/store-devtools @ngrx/effects --save
  1. Создайте новое хранилище NgRx для управления состоянием приложения. В этом примере мы создадим простое приложение-счетчик.
  2. В файле app.module.ts настройте хранилище NgRx и эффекты:
// app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StoreModule } from '@ngrx/store';
import { StoreDevtoolsModule } from '@ngrx/store-devtools';
import { EffectsModule } from '@ngrx/effects';
import { HttpClientModule } from '@angular/common/http';

import { AppComponent } from './app.component';
import { counterReducer } from './counter.reducer';
import { CounterEffects } from './counter.effects';

@NgModule({
  declarations: [AppComponent],
  imports: [
    BrowserModule,
    HttpClientModule,
    StoreModule.forRoot({ count: counterReducer }),
    StoreDevtoolsModule.instrument(),
    EffectsModule.forRoot([CounterEffects])
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

Создайте файл с именем counter.actions.ts для определения действий, представляющих изменения состояния:

// counter.actions.ts

import { createAction } from '@ngrx/store';

export const increment = createAction('[Counter] Increment');
export const decrement = createAction('[Counter] Decrement');
export const reset = createAction('[Counter] Reset');

Создайте файл с именем counter.reducer.ts, чтобы определить редьюсер, который обрабатывает изменения состояния на основе действий:

// counter.reducer.ts

import { createReducer, on } from '@ngrx/store';
import { increment, decrement, reset } from './counter.actions';

export const initialState = 0;

export const counterReducer = createReducer(
  initialState,
  on(increment, (state) => state + 1),
  on(decrement, (state) => state - 1),
  on(reset, () => initialState)
);

Создайте файл с именем counter.effects.ts для обработки побочных эффектов. В этом примере мы просто запишем действия в консоль:

// counter.effects.ts

import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { tap } from 'rxjs/operators';
import * as CounterActions from './counter.actions';

@Injectable()
export class CounterEffects {
  logActions$ = createEffect(() =>
    this.actions$.pipe(
      ofType(CounterActions.increment, CounterActions.decrement, CounterActions.reset),
      tap((action) => console.log('Action:', action))
    )
  );

  constructor(private actions$: Actions) {}
}

В app.component.ts используйте хранилище NgRx, чтобы связать состояние с шаблоном:

// app.component.ts

import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import { increment, decrement, reset } from './counter.actions';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  count$: Observable<number>;

  constructor(private store: Store<{ count: number }>) {
    this.count$ = store.select('count');
  }

  increment() {
    this.store.dispatch(increment());
  }

  decrement() {
    this.store.dispatch(decrement());
  }

  reset() {
    this.store.dispatch(reset());
  }
}

В app.component.html отобразите счетчик и кнопки для взаимодействия с состоянием:

<!-- app.component.html -->

<h1>NgRx Counter Example</h1>

<div>
  <p>Count: {{ count$ | async }}</p>
  <button (click)="increment()">Increment</button>
  <button (click)="decrement()">Decrement</button>
  <button (click)="reset()">Reset</button>
</div>

6. Динамические компоненты: Angular позволяет динамически создавать компоненты и управлять ими во время выполнения. Эта функция полезна, когда вам нужно визуализировать различные компоненты на основе взаимодействия с пользователем или условий данных.

Вот пример динамического создания компонентов и управления ими:

Создайте два динамических компонента, которые вы хотите визуализировать динамически. Для этого примера создадим DynamicComponentA и DynamicComponentB :

// dynamic-component-a.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-dynamic-component-a',
  template: '<p>Dynamic Component A</p>',
})
export class DynamicComponentA {}

// dynamic-component-b.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-dynamic-component-b',
  template: '<p>Dynamic Component B</p>',
})
export class DynamicComponentB {}

В основном компоненте (назовем его AppComponent) создайте контейнер для динамических компонентов:

<!-- app.component.html -->

<h1>Dynamic Components Example</h1>

<div #dynamicComponentContainer></div>

<button (click)="loadDynamicComponentA()">Load Component A</button>
<button (click)="loadDynamicComponentB()">Load Component B</button>

В файл TypeScript AppComponent импортируйте необходимые модули и ComponentFactoryResolver для создания динамических компонентов:

// app.component.ts

import { Component, ViewChild, ViewContainerRef, ComponentFactoryResolver } from '@angular/core';
import { DynamicComponentA } from './dynamic-component-a.component';
import { DynamicComponentB } from './dynamic-component-b.component';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  @ViewChild('dynamicComponentContainer', { read: ViewContainerRef }) container: ViewContainerRef;

  constructor(private componentFactoryResolver: ComponentFactoryResolver) {}

  loadDynamicComponentA() {
    this.container.clear();

    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(DynamicComponentA);
    const componentRef = this.container.createComponent(componentFactory);
  }

  loadDynamicComponentB() {
    this.container.clear();

    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(DynamicComponentB);
    const componentRef = this.container.createComponent(componentFactory);
  }
}

В этом примере мы используем ComponentFactoryResolver для создания и добавления динамических компонентов. Декоратор @ViewChild позволяет нам получить доступ к элементу dynamicComponentContainer в шаблоне, который служит заполнителем для динамических компонентов.

Обязательно добавьте эти динамические компоненты в массив entryComponents в файле AppModule. Этот шаг необходим для того, чтобы Angular правильно скомпилировал и связал динамические компоненты.

// app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { DynamicComponentA } from './dynamic-component-a.component';
import { DynamicComponentB } from './dynamic-component-b.component';

@NgModule({
  declarations: [AppComponent, DynamicComponentA, DynamicComponentB],
  imports: [BrowserModule],
  providers: [],
  bootstrap: [AppComponent],
  entryComponents: [DynamicComponentA, DynamicComponentB], // Add dynamic components here
})
export class AppModule {}

Теперь, когда вы запустите приложение, вы увидите две кнопки «Загрузить компонент A» и «Загрузить компонент B». Нажатие этих кнопок будет динамически создавать и отображать соответствующие компоненты внутри div dynamicComponentContainer на основе действий пользователя.

7. Директивы NgIf и NgFor: Angular предоставляет структурные директивы, такие как *ngIf и *ngFor, для обработки динамического рендеринга элементов в шаблоне на основе определенных условий или циклического просмотра наборов данных.

  1. Директива NgIf. Директива *ngIf позволяет условно отображать элементы в шаблоне на основе заданного выражения. Если выражение оценивается как истинное, элемент отображается; в противном случае он удаляется из DOM.
// app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  isLoggedIn: boolean = false;

  toggleLoginStatus() {
    this.isLoggedIn = !this.isLoggedIn;
  }
}
<!-- app.component.html -->

<button (click)="toggleLoginStatus()">Toggle Login Status</button>

<div *ngIf="isLoggedIn">
  <p>Welcome, you are logged in!</p>
</div>

<div *ngIf="!isLoggedIn">
  <p>Please log in to continue.</p>
</div>

В этом примере, когда вы нажимаете кнопку «Переключить статус входа», переменная isLoggedIn переключается, и соответствующее сообщение будет отображаться в зависимости от ее значения.

2. Директива NgFor. Директива *ngFor позволяет циклически перемещаться по коллекции и отображать элементы для каждого элемента коллекции.

// app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  fruits: string[] = ['Apple', 'Banana', 'Orange'];
}
<!-- app.component.html -->

<h2>List of Fruits:</h2>

<ul>
  <li *ngFor="let fruit of fruits">{{ fruit }}</li>
</ul>

В этом примере массив fruits содержит список фруктов, а директива *ngFor перебирает массив, отображая элемент списка для каждого фрукта.

Комбинируя эти методы, вы можете эффективно управлять динамическими данными в своем приложении Angular. Не забывайте учитывать сложность вашего приложения и выбирать наиболее подходящий подход к обработке динамических данных на основе конкретных требований и вариантов использования.

Управление навигацией с маршрутизацией

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

Вот пример того, как управлять навигацией с маршрутизацией:

  1. Создайте компоненты, к которым вы хотите перейти. Для этого примера создадим три компонента: HomeComponent, AboutComponent и ContactComponent.
  2. Настройте конфигурацию маршрутизации в файле app-routing.module.ts. Импортируйте компоненты и определите маршруты с помощью метода RouterModule.forRoot():
// app-routing.module.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { HomeComponent } from './home.component';
import { AboutComponent } from './about.component';
import { ContactComponent } from './contact.component';

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'about', component: AboutComponent },
  { path: 'contact', component: ContactComponent },
  { path: '**', redirectTo: '' } // Redirect to the home page for any other unknown route
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {}

Импортируйте AppRoutingModule в свой AppModule:

// app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';

import { AppComponent } from './app.component';
import { HomeComponent } from './home.component';
import { AboutComponent } from './about.component';
import { ContactComponent } from './contact.component';
import { AppRoutingModule } from './app-routing.module';

@NgModule({
  declarations: [AppComponent, HomeComponent, AboutComponent, ContactComponent],
  imports: [BrowserModule, AppRoutingModule],
  bootstrap: [AppComponent]
})
export class AppModule {}

В app.component.html создайте ссылки для навигации между компонентами с помощью директивы routerLink:

<!-- app.component.html -->

<h1>Navigation Example</h1>

<nav>
  <a routerLink="/">Home</a>
  <a routerLink="/about">About</a>
  <a routerLink="/contact">Contact</a>
</nav>

<router-outlet></router-outlet>

<router-outlet></router-outlet> выступает в качестве заполнителя для содержимого компонента, которое должно отображаться на основе текущего маршрута.

Теперь при запуске приложения вы увидите в шаблоне навигационные ссылки «Главная», «О программе» и «Контакты». Нажав на эти ссылки, вы перейдете к соответствующим компонентам и отобразите их содержимое.