Компонентная архитектура

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

Структура компонента

Каждый компонент в Angular состоит из нескольких ключевых частей:

  1. Класс компонента — содержит логику, обработку событий и взаимодействие с данными.
  2. Шаблон (HTML) — описание внешнего вида компонента.
  3. Стили (CSS) — оформление компонента, определяющее его визуальное представление.
  4. Метаданные компонента — аннотация, содержащая информацию о том, как компонент должен работать, включая выбор шаблона и стилей, а также директивы и зависимости.

Пример базового компонента:

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

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

Здесь:

  • selector — имя тега, который будет использоваться для вставки компонента в шаблон.
  • templateUrl — путь к файлу HTML, в котором находится разметка компонента.
  • styleUrls — массив путей к файлам CSS, которые будут применяться к компоненту.

Жизненный цикл компонента

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

  1. ngOnInit — вызывается один раз, когда компонент инициализируется. Это идеальное место для инициализации данных или вызова API.
  2. ngOnChanges — вызывается при изменении входных данных компонента.
  3. ngDoCheck — используется для выполнения пользовательских проверок изменений.
  4. ngAfterContentInit — вызывается после того, как Angular вставит содержимое компонента в DOM.
  5. ngAfterViewInit — вызывается после того, как Angular инициализирует представление компонента.
  6. ngOnDestroy — вызывается перед уничтожением компонента, это подходящее место для очистки ресурсов.

Пример использования жизненного цикла:

import { Component, OnInit, OnDestroy } from '@angular/core';

@Component({
  selector: 'app-example',
  templateUrl: './example.component.html',
  styleUrls: ['./example.component.css']
})
export class ExampleComponent implements OnInit, OnDestroy {
  constructor() { }

  ngOnInit(): void {
    console.log('Component initialized');
  }

  ngOnDestroy(): void {
    console.log('Component destroyed');
  }
}

Взаимодействие компонентов

Компоненты могут обмениваться данными с помощью входных и выходных параметров.

Входные данные (Input)

Чтобы передать данные в компонент, используется декоратор @Input. Это позволяет родительскому компоненту передавать значения дочернему компоненту.

Пример:

// Родительский компонент
@Component({
  selector: 'app-parent',
  template: `<app-child [childData]="parentData"></app-child>`
})
export class ParentComponent {
  parentData = 'Data from Parent';
}

// Дочерний компонент
@Component({
  selector: 'app-child',
  template: `<p>{{ childData }}</p>`
})
export class ChildComponent {
  @Input() childData: string;
}

Выходные данные (Output)

Для отправки данных от дочернего компонента к родительскому используется декоратор @Output, который вместе с объектом EventEmitter позволяет генерировать события.

Пример:

// Родительский компонент
@Component({
  selector: 'app-parent',
  template: `<app-child (childEvent)="handleChildEvent($event)"></app-child>`
})
export class ParentComponent {
  handleChildEvent(event: string) {
    console.log(event);
  }
}

// Дочерний компонент
@Component({
  selector: 'app-child',
  template: `<button (click)="sendEvent()">Click Me</button>`
})
export class ChildComponent {
  @Output() childEvent = new EventEmitter<string>();

  sendEvent() {
    this.childEvent.emit('Data from Child');
  }
}

Модульность и вложенные компоненты

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

Пример структуры модуля с компонентами:

@NgModule({
  declarations: [AppComponent, ChildComponent],
  imports: [BrowserModule],
  bootstrap: [AppComponent]
})
export class AppModule { }

Директивы и компоненты

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

Пример создания директивы:

@Directive({
  selector: '[appHighlight]'
})
export class HighlightDirective {
  constructor(private el: ElementRef) {
    el.nativeElement.style.backgroundColor = 'yellow';
  }
}

Службы и инъекция зависимостей

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

Пример сервиса:

@Injectable({
  providedIn: 'root',
})
export class DataService {
  getData() {
    return ['Angular', 'React', 'Vue'];
  }
}

Компонент, использующий этот сервис:

@Component({
  selector: 'app-example',
  templateUrl: './example.component.html',
  styleUrls: ['./example.component.css']
})
export class ExampleComponent {
  data: string[];

  constructor(private dataService: DataService) {
    this.data = this.dataService.getData();
  }
}

Преимущества компонентной архитектуры

  1. Повторное использование — компоненты можно использовать в разных частях приложения, что значительно повышает гибкость и удобство работы с кодом.
  2. Масштабируемость — приложения на Angular можно легко масштабировать, добавляя новые компоненты и модули.
  3. Тестируемость — каждый компонент можно тестировать независимо, что упрощает процесс написания юнит-тестов.
  4. Чистота кода — разделение на компоненты помогает поддерживать чистоту и модульность кода, что облегчает его поддержку и развитие.

Заключение

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