Вопросы подготовлены непосредственно для того, чтобы определить уровень разработчика, насколько глубоко, поверхностно или сносно он знает Angular. Вопросы для собеседования на знание JavaScript или Web-стека хорошо освещены в других местах, поэтому ниже будет добавлен список ресурсов по этой теме:
Fundamentals:
Frontend:
- Front-end Job Interview Questions
- The Best Frontend JavaScript Interview Questions
- Frontend Guidelines Questionnaire
- Подготовка к интервью на Front-end разработчика
Angular:
В чем отличие фреймворка от библиотеки (приведите примеры и отличия)?
Какие популярные CSS, JS библиотеки вы знаете?
Знаете ли вы как браузер обрабатывает index.html (расскажите про Critical Rendering Path)?
Какие типы данных есть в JavaScript?
Как устроена память в JavaScript (memory heap, memory stack)?
Что такое this и расскажите про область видимости?
В чем отличие var от const, let?
Объясните, как работает наследование прототипов, что такое цепочка прототипов, и когда появилось ключевое слова class в JS?
Что такое структура данных и какие виды вы знаете (Стек, etc)?
Что такое Promise и для чего используется в JS?
Что такое call-stack, task-queue (приведите примеры работы)?
Что такое макро и микро задачи в JS?
Назовите основные принципы ООП?
Что такое класс и интерфейс?
Что такое конструктор класса?
Расскажите про стек TCP/IP, а также более подробно про, что такое HTTP и какую роль он играет при разработке приложений?
Что такое REST API, как происходит взаимодействие (расскажите про основные коды ошибок, заголовки пакетов и способы их отправки)?
Зачем нам нужны определения типов, где есть JavaScript c динамической типизацией?
Что такое пользовательский тип данных
Что такое Union Type (тип объединения) и для чего используется?
Поддерживает ли TypeScript перегрузку методов?
Возможна ли перегрузка конструктора в TypeScript?
Поддерживает ли TypeScript перегрузку методов (конструкторов)?
Что такое декоратор и какие виды декораторов вы знаете?
Декоратор — способ добавления метаданных к объявлению класса. Это специальный вид объявления, который может быть присоединен к объявлению класса, методу, методу доступа, свойству или параметру.
Декораторы используют форму @expression, где expression - функция, которая будет вызываться во время выполнения с информацией о декорированном объявлении.
И, чтобы написать собственный декоратор, нам нужно сделать его factory и определить тип:
- ClassDecorator
- PropertyDecorator
- MethodDecorator
- ParameterDecorator
Декоратор класса
Вызывается перед объявлением класса, применяется к конструктору класса и может использоваться для наблюдения, изменения или замены определения класса. Expression декоратора класса будет вызываться как функция во время выполнения, при этом конструктор декорированного класса является единственным аргументом. Если класс декоратора возвращает значение, он заменит объявление класса вернувшимся значением.
export function logClass(target: Function) {
// Сохранение ссылки на оригинальный конструктор
const original = target;
// Функция генерирует экземпляры класса
function construct(constructor, args) {
const c: any = function () {
return constructor.apply(this, args);
};
c.prototype = constructor.prototype;
return new c();
}
// Определение поведения нового конструктора
const f: any = function (...args) {
console.log(`New: ${original["name"]} is created`);
//New: Employee создан
return construct(original, args);
};
// Копирование прототипа, чтобы оператор intanceof работал
f.prototype = original.prototype;
// Возвращает новый конструктор, переписывающий оригинальный
return f;
}
@logClass
class Employee {}
let emp = new Employee();
console.log("emp instanceof Employee");
//emp instanceof Employee
console.log(emp instanceof Employee);
//true
Декоратор свойства
Объявляется непосредственно перед объявлением метода. Будет вызываться как функция во время выполнения со следующими двумя аргументами:
- target - прототип текущего объекта, т.е. если Employee является объектом, Employee.prototype
- propertyKey - название свойства
function logParameter(target: Object, propertyName: string) {
// Значение свойства
let _val = this[propertyName];
// Геттер свойства
const getter = () => {
console.log(`Get: ${propertyName} => ${_val}`);
return _val;
};
// Сеттер свойства
const setter = (newVal) => {
console.log(`Set: ${propertyName} => ${newVal}`);
_val = newVal;
};
// Удаление свойства
if (delete this[propertyName]) {
// Создает новое свойство с геттером и сеттером
Object.defineProperty(target, propertyName, {
get: getter,
set: setter,
enumerable: true,
configurable: true,
});
}
}
class Employee {
@logParameter
name: string;
}
const emp = new Employee();
emp.name = "Mohan Ram";
console.log(emp.name);
// Set: name => Mohan Ram
// Get: name => Mohan Ram
// Mohan Ram
Декоратор метода
Объявляется непосредственно перед объявлением метода. Будет вызываться как функция во время выполнения со следующими двумя аргументами:
- target - прототип текущего объекта, т.е. если Employee является объектом, Employee.prototype
- propertyName - название свойства
- descriptor - дескриптор свойства метода т.е. - Object.getOwnPropertyDescriptor (Employee.prototype, propertyName)
export function logMethod(
target: Object,
propertyName: string,
propertyDescriptor: PropertyDescriptor): PropertyDescriptor {
const method = propertyDescriptor.value;
propertyDescriptor.value = function (...args: any[]) {
// Конвертация списка аргументов greet в строку
const params = args.map(a => JSON.stringify(a)).join();
// Вызов greet() и получение вернувшегося значения
const result = method.apply(this, args);
// Конвертация результата в строку
const r = JSON.stringify(result);
// Отображение в консоли деталей вызова
console.log(`Call: ${propertyName}(${params}) => ${r}`);
// Возвращение результата вызова
return result;
}
return propertyDescriptor;
}
class Employee {
constructor(
private firstName: string,
private lastName: string
) {
}
@logMethod
greet(message: string): string {
return `${this.firstName} ${this.lastName} says: ${message}`;
}
}
const emp = new Employee('Mohan Ram', 'Ratnakumar');
emp.greet('hello');
//Call: greet("hello") => "Mohan Ram Ratnakumar says: hello"
Декоратор параметра
Объявляется непосредственно перед объявлением метода. Будет вызываться как функция во время выполнения со следующими двумя аргументами:
- target - прототип текущего объекта, т.е. если Employee является объектом, Employee.prototype
- propertyKey - название свойства
- index - индекс параметра в массиве аргументов
function logParameter(target: Object, propertyName: string, index: number) {
// Генерация метаданных для соответствующего метода
// для сохранения позиции декорированных параметров
const metadataKey = `log_${propertyName}_parameters`;
if (Array.isArray(target[metadataKey])) {
target[metadataKey].push(index);
} else {
target[metadataKey] = [index];
}
}
class Employee {
greet(@logParameter message: string): void {
console.log(`hello ${message}`);
}
}
const emp = new Employee();
emp.greet("world");
Что такое Angular?
Angular — это платформа для разработки мобильных и десктопных веб-приложений. Наши приложения теперь представляют из себя «толстый клиент», где управление отображением и часть логики перенесены на сторону браузера. Так сервер уделяет больше времени доставке данных, плюс пропадает необходимость в постоянной перерисовке. С Angular мы описываем структуру приложения декларативно, а с TypeScript начинаем допускать меньше ошибок, благодаря статической типизации. В Angular присутствует огромное количество возможностей из коробки. Это может быть одновременно и хорошо и плохо, в зависимости от того, что вам необходимо.
Какие плюсы можно выделить:
- Поддержка Google, Microsoft
- Инструменты разработчика (CLI)
- Typescript из коробки
- Реактивное программирование с RxJS
- Единственный фреймворк с Dependency Injection из коробки
- Шаблоны, основанные на расширении HTML
- Кроссбраузерный Shadow DOM из коробки (либо его эмуляция)
- Кроссбраузерная поддержка HTTP, WebSockets, Service Workers
- Не нужно ничего дополнительно настраивать. Больше никаких оберток. jQuery плагины и D3 можно использовать на прямую
- Более современный фреймворк, чем AngularJS (на уровне React, Vue)
- Большое комьюнити
Минусы:
- Выше порог вхождения из-за Observable (RxJS) и Dependency Injection
- Чтобы все работало хорошо и быстро нужно тратить время на дополнительные оптимизации (он не супер быстрый, по умолчанию, но быстрее AngularJS во много раз)
- Если вы планируете разрабатывать большое enterprise-приложение, то в этом случае, у вас нет архитектуры из коробки - нужно добавлять Mobx, Redux, MVVM, CQRS/CQS или другой state-менеджер, чтобы потом не сломать себе мозг
- Angular-Universal имеет много подводных камней
- Динамическое создание компонентов оказывается нетривиальной задачей
В чем разница между AngularJS и Angular?
AngularJS является фреймворком, который может помочь вам в разработке Single Page Application. Он появился в 2009 году и с годами выяснилось, что имел много проблем. Angular (Angular 2+) же в свою очередь направлен на тоже самое, но дает больше преимуществ по сравнению с AngularJS 1.x, включая лучшую производительность, ленивую загрузку, более простой API, более легкую отладку.
Что появилось в Angular:
- Angular ориентирован на мобильные платформы и имеет лучшую производительность
- Angular имеет встроенные сервисы для поддержки интернационализации
- AngularJS проще настроить, чем Angular
- AngularJS использует контроллеры и $scope
- Angular имеет много способов определения локальных переменных
- В Angular новый синтаксис структурных директив (camelCase)
- Angular работает напрямую с свойствами и событиями DOM элементов
- Одностороннее связывание данных через [property]
- Двустороннее связывание данных через [(property)]
- Новый механизм DI, роутинга, запуска приложения
Основные преимущества Angular:
- Обратная совместимость Angular 2, 4, 5, ..
- TypeScript с улучшенной проверкой типов
- Встроенный компилятор с режимами JIT и AOT (+сокращение кода)
- Встроенные анимации
Какой должна быть структура каталогов компонентов любого Angular приложения и почему?
Что такое MVVM и в чем разница перед MVC?
MVVM - шаблон проектирования архитектуры приложения. Состоит из 3 ключевых блоков: Model, View, ViewModel.
Отличие от MVС заключаются в:
Привязка данных между View и ViewModel может быть односторонней или двусторонней (one-way, two-way data-binding).
Что такое интерполяция в Angular?
Разметка интерполяции с внедренными выражениями используется в Angular для присвоения данных текстовым нодам и значения аттрибутов. Например:
<a href="img/{{username}}.jpg">Hello {{username}}!</a>
Какие способы использования шаблонов в Angular вы знаете?
В чем разница между структурной и атрибутной директивой, назовите встроенные директивы?
(ng-template, NgIf, NgFor, NgSwitch, etc)
(NgStyle, NgClass, etc).
Для чего нужны директивы ng-template, ng-container, ng-content?
<template>
— это механизм для отложенного рендера клиентского контента, который не отображается во время загрузки, но может быть инициализирован при помощи JavaScript.
Template можно представить себе как фрагмент контента, сохранённый для последующего использования в документе. Хотя парсер и обрабатывает содержимое элемента template
во время загрузки страницы, он делает это только чтобы убедиться в валидности содержимого; само содержимое при этом не отображается.
<ng-template>
- является имплементацией стандартного элемента template, данный элемент появился с четвертой версии Angular, это было сделано с точки зрения совместимости со встраиваемыми на страницу template элементами, которые могли попасть в шаблон ваших компонентов по тем или иным причинам.
Пример:
<div class="lessons-list" *ngIf="lessons else loading">...</div>
<ng-template #loading>
<div>Loading...</div>
</ng-template>
<ng-container>
- это логический контейнер, который может использоваться для группировки узлов, но не отображается в дереве DOM как узел (node).
На самом деле структурные директивы (*ngIf, *ngFor, …) являются синтаксическим сахаром для наших шаблонов. В реальности, данные шаблоны трансформируются в такие конструкции:
<ng-template [ngIf]="lessons" [ngIfElse]="loading">
<div class="lessons-list">
...
</div>
</div>
<ng-template #loading>
<div>Loading...</div>
</ng-template>
Но что делать, если я хочу применить несколько структурных директив? (спойлер: к сожалению, так нельзя сделать)
<div class="lesson" *ngIf="lessons" *ngFor="let lesson of lessons">
<div class="lesson-detail">{{lesson | json}}</div>
</div>
Uncaught Error: Template parse errors:
Can't have multiple template bindings on one element. Use only one attribute
named 'template' or prefixed with *
Но можно сделать так:
<div *ngIf="lessons">
<div class="lesson" *ngFor="let lesson of lessons">
<div class="lesson-detail">{{lesson | json}}</div>
</div>
</div>
Однако, чтобы избежать необходимости создавать дополнительный div, мы можем вместо этого использовать директиву ng-container:
<ng-container *ngIf="lessons">
<div class="lesson" *ngFor="let lesson of lessons">
<div class="lesson-detail">{{lesson | json}}</div>
</div>
</ng-container>
Как мы видим, директива ng-container предоставляет нам элемент, в котором мы можем использовать структурную директиву, без необходимости создавать дополнительный элемент.
Еще пара примечательных примеров, если все же вы хотите использовать ng-template вместо ng-container, по определенным правилам вы не сможете использовать полную конструкцию структурных директив.
Вы можете писать либо так:
<div class="mainWrap">
<ng-container *ngIf="true">
<h2>Title</h2>
<div>Content</div>
</ng-container>
</div>
Либо так:
<div class="mainWrap">
<ng-template [ngIf]="true">
<h2>Title</h2>
<div>Content</div>
</ng-template>
</div>
На выходе, при рендеринге будет одно и тоже:
<div class="mainWrap">
<h2>Title</h2>
<div>Content</div>
</div>
<ng-content>
- позволяет внедрять родительским компонентам html-код в дочерние компоненты.
Здесь на самом деле, немного сложнее уже чем с ng-template, ng-container. Так как ng-content решает задачу проецирования контента в ваши веб-компоненты. Веб-компоненты состоят из нескольких отдельных технологий. Вы можете думать о Веб-компонентах как о переиспользуемых виджетах пользовательского интерфейса, которые создаются с помощью открытых веб-технологий. Они являются частью браузера и поэтому не нуждаются во внешних библиотеках, таких как jQuery или Dojo. Существующий Веб-компонент может быть использован без написания кода, просто путем импорта выражения на HTML-страницу. Веб-компоненты используют новые или разрабатываемые стандартные возможности браузера.
Давайте представим ситуацию от обратного, нам нужно параметризовать наш компонент. Мы хотим сделать так, чтобы на вход в компонент мы могли передать какие-либо статичные данные. Это можно сделать несколькими способами.
comment.component.ts:
@Component({
selector: "comment",
template: `
<h1>Комментарий:</h1>
<p>{{ data }}</p>
`,
})
export class CommentComponent {
@Input() data: string = null;
}
app.component.html
<div *ngFor="let message of comments">
<comment [data]="message"></comment>
</div>
Но можно поступить и другим путем.
comment.component.ts:
@Component({
selector: "comment",
template: `
<h1>Комментарий:</h1>
<ng-content></ng-content>
`,
})
export class CommentComponent {}
app.component.html
<div *ngFor="let message of comments">
<comment>
<p>{{message}}</p>
</comment>
</div>
Конечно, эти примеры плохо демонстрируют подводные камни, свои плюсы и минусы. Но второй способ демонстрирует подход при работе, когда мы оперируем независимыми абстракциями и можем проецировать контент внутрь наших компонентов (подход веб-компонентов).
Что такое директива и как создать собственную?
Директивы бывают трех видов: компонент, структурные и атрибутные (см. выше).
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective { }
Декоратор определяет селектор атрибута [appHighlight], [] - указывают что это селектор атрибута. Angular найдет каждый элемент в шаблоне с этим атрибутом и применит к ним логику директивы.
@Directive({
selector: "[appHighlight]",
})
export class HighlightDirective {
constructor(el: ElementRef) {
el.nativeElement.style.backgroundColor = "yellow";
}
}
Необходимо указать в конструкторе ElementRef, чтобы через его свойство nativeElement иметь прямой доступ к DOM элементу, который должен быть изменен.
Теперь, используя @HostListener, можно добавить обработчики событий, взаимодействующие с декоратором.
@Component()
class EtcComponent {
@HostListener("mouseenter")
public onMouseEnter(): void {
this.highlight("yellow");
}
@HostListener("mouseleave")
public onMouseLeave(): void {
this.highlight(null);
}
private highlight(color: string): void {
this.el.nativeElement.style.backgroundColor = color;
}
}
Напишем UnlessDirective, которая будет противоположна NgIf.
Необходимо использовать @Directive, и импортировать Input, TemplateRef, и ViewContainerRef. Они вам понадобятся при воздании любой структурной директивы.
import { Directive, Input, TemplateRef, ViewContainerRef } from "@angular/core";
@Directive({ selector: "[appUnless]" })
export class UnlessDirective {}
В конструкторе мы получаем доступ к view container и содержимое .
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef) { }
Наша директива предполагает работу с true/false. Для этого нужно свойство appUnless, добавленное через @Input.
@Directive({ selector: "[appUnless]" })
export class UnlessDirective {
@Input() public set appUnless(condition: boolean) {
if (!condition && !this.hasView) {
this.viewContainer.createEmbeddedView(this.templateRef);
this.hasView = true;
} else if (condition && this.hasView) {
this.viewContainer.clear();
this.hasView = false;
}
}
}
Что такое директива, компонент, модуль, сервис, пайп в Angular и для чего они нужны?
- Директива - см. выше.
- Компонент контролирует участок экрана, т.н. view.
- Сервис это класс с узкой, четко определенной целью. Это может быть значение, функция, запрос, etc. Главное в них то, что они повторно используются, отделяя чистую функциональность компонента.
- Пайп преобразует отображение значений в шаблоне, к примеру отображение дат в разных локалях или изменяют в отображении регистр строк.
Расскажите об основных параметрах @NgModule, @Component, @Directive, @Injectable, @Pipe
Декораторы динамически подключают дополнительное поведение к объекту. Они помечают класс и предоставляют конфигурационные метаданные.
- directiveProperty - наименование свойства @Input, которое будет использоваться в дочернем компоненте для вывода в шаблоне и использования в самом классе.
- bindingProperty - наименование свойства, из которого будет производится чтение и запись в directiveProperty. Не обязательное. При отсутсвии параметра значение будет браться из directiveProperty
@Component({
selector: "child-component",
template: `Name {{ name }} Id {{ id }}`,
inputs: ["name", "id: parentId"],
})
export class ChildComponent {}
@Component({
selector: "parent-component",
template: `
<child-component
[name]="parentName"
[parentId]="parentIdValue"
></child-component>
`,
})
export class Parent {
public parentIdValue = "123";
public parentName = "Name";
}
@Component({
selector: "child-dir",
outputs: ["bankNameChange"],
template: `<input (input)="bankNameChange.emit($event.target.value)" />`,
})
class ChildDir {
bankNameChange: EventEmitter<string> = new EventEmitter<string>();
}
@Component({
selector: "main",
template: `
{{ bankName }}
<child-dir (bankNameChange)="onBankNameChange($event)"></child-dir>
`,
})
class MainComponent {
bankName: string;
onBankNameChange(bankName: string) {
this.bankName = bankName;
}
}
Что такое динамические компоненты и как их можно использовать в Angular?
Динамические компоненты - компоненты, которые добавляются на страницу во время выполнения приложения (runtime). Динамические компоненты можно использовать в тех случаях, когда компонент можно отобразить не сразу при загрузке страницы. Например: диалоговые окна, нотификации, контент в табах.
Для того, чтобы использовать динамические компоненты, необходимо убедиться, что:
- добавлен элемент ("якорь") - ng-container/ng-template - на нужной странице/в шаблоне, куда будет помещен динамический компонент. Именно в этот элемент будет загружаться динамический компонент.
- в классе компонента определено свойство для хранения ng-container/ng-template. Например:
@Component({ template: `<div> <ng-container #dynamicContent></ng-container> </div> `, }) export class AppComponent { @ViewChild("dynamicContent", { read: ViewContainerRef }) public dynamicContainer: ViewContainerRef; }
- при загрузке страницы ng-container/ng-template определен и загружен. Проверить загрузку и определение "якоря" можно в хуке ngAfterViewInit()
В динамический компонент можно внедрить зависимости. Зависимости могут понадобится для общения основного и динамического компонентов. Перед внедрением зависимости нужно создать injector. Создание injector похоже на определение параметра providers в @NgModule. Пример создания Injector:
// класс, который будет использоваться в constructor
export abstract class IDynamicComponentProps {
public abstract onClickDynamicComponent(): void;
public abstract items: Array<string>;
}
// Использование зависимости в динамическом компоненте
@Component({
template: `
<span *ngFor='let item of dynamicItems'>{{item}}</span>
<button (click)='onClick()'></button>
`
})
export class DynamicComponent {
public dynamicItems: Array<string> =
this.dynamicComponentProps.items;
constructor(
private dynamicComponentProps: IDynamicComponentProps,
) {}
public onClick(): void {
this.dynamicComponentProps.onClickDynamicComponent();
}
}
// Создание инжектора в сервисе или родительском компоненте
@Component({
...
})
export class ParentComponent {
public onClickHandler: EventEmitter<number> = new EventEmitter();
public parentItems: Array<string> = ['str1', 'str2'];
constructor(
private injector: Injector,
) {}
public createInjector() {
const injector: Injector = Injector.create(
[
{
provide: IDynamicComponentProps,
useValue:{
onClickDynamicComponent: () => { this.onClickHandler.emit(0) },
items: this.parentItems
}
}
],
this.injector
);
}
}
- Добавить в шаблон "якорь" для компонента, объявить переменную для работы с этим элементом
- Очистить содержимое динамического компонента (при необходимости)
- Создать ComponentFactory с помощью resolveComponentFactory()
- Вызвать метод из созданного ComponentFactory для создания компонента на странице.
Примечание: Для динамического компонента не обязательно создавать Injector. Обязательным параметром для метода createComponent является только ComponentFactory.
Ниже указана последовательность действий, реализованная кодом. В примере используется Основной компонент (MainComponent), динамический компонент (DynamicCompoent) и сервис для рендера (MainComponentService)
//основной компонент
@Component({
selector: "main-component",
template: `<h1>Dynamic Component Example</h1>
<ng-container #dynamicComponent></ng-container> `,
})
export class MainComponent {
@ViewChild("dynamicComponent", { read: ViewContainerRef })
public dynamicContainer: ViewContainerRef;
public parentItems: Array<string> = ["str1", "str2"];
constructor(private mainComponentService: MainComponentService) {
this.mainComponentService.onClickHandler().subscribe((x) => console.log(x));
// console - 0 form dynamic component
}
public ngAfterViewInit(): void {
this.mainComponentService.render(this.dynamicContainer, this.parentItems);
}
}
// класс для DI
export abstract class IDynamicComponentProps {
public abstract onClickDynamicComponent(): void;
public abstract items: Array<string>;
}
// сервис для рендера
@Injectable()
export class MainComponentService {
public onClickHandler: EventEmitter<number> = new EventEmitter();
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private injector: Injector
) {}
public render(container: ViewContainerRef, parentItems: Array<string>): void {
if (!isUndefined(container)) {
container.clear();
}
const injector: Injector = Injector.create(
[
{
provide: IDynamicComponentProps,
useValue: {
onClickDynamicComponent: () => {
this.onClickHandler.emit(0);
},
items: parentItems,
},
},
],
this.injector
);
const factory = this.componentFactoryResolver.resolveComponentFactory(
DynamicCompoent
);
container.createComponent(factory, 0, injector);
}
}
//динамический компонент
@Component({
template: `
<span *ngFor="let item of dynamicItems">{{ item }}</span>
<button (click)="onClick()"></button>
`,
})
export class DynamicComponent {
public dynamicItems: Array<string> = this.dynamicComponentProps.items;
constructor(private dynamicComponentProps: IDynamicComponentProps) {}
public onClick(): void {
this.dynamicComponentProps.onClickDynamicComponent();
}
}
Как применить анимацию к компонентам?
Анимации в Angular построены на основе функциональности CSS. При работе с анимациями нужно иметь ввиду, что применять анимацию можно только к тем свойствам, которые можно анимировать.
Перед началом создания анимаций нужно:
- Подключить модуль BrowserAnimationsModule в основной модуль приложения (root)
- Подключить функции для анимации в нужном компоненте:
import {
trigger,
state,
style,
animate,
transition,
// ...
} from "@angular/animations";
@Component({
selector: 'app-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.css'],
animations: [
// animation triggers go here
]
})
Анимация состоит из:
- триггера - событие, по которому возникает анимация. Для инициализации триггера используется функция trigger(). В параметрах функции нужно указать событие, которое будет указано в компоненте и к которому будет привязана анимация. Так же, указывается массив из функций state() и transition()
- состояний в конце перехода - стили, которые будут применятся к элементу в конце анимации. Для объявления состояний используется функция state(). В функции нужно указать название состояния, указать стили состояния с помощью функции style(). Если анимация отключена ([@.disabled]='true'), то стили конечных состояний нужно прописать.
- промежуточных состояний - стилей, которые применяются к элементу между окончательными состояниями. С помощью промежуточных состояний можно анимировать переходы. Для этого используется функция transition(). В функции нужно прописать выражение, в котором указано направление между состояниями и функции для определения стилей между состояниями, анимации.
Для объявления триггера, нужно прописать функцию trigger() в метаданных компонента, в свойстве animations. Первым параметром нужно указать событие, которое будет привязано в шаблоне к элементу. Вторым параметром нужно указать состояние state() и анимации в transition(). Например:
@Component({
selector: "example",
animations: [
trigger("toggle", [
state(
"open",
style({
height: "200px",
opacity: 1,
backgroundColor: "yellow",
})
),
state(
"closed",
style({
height: "100px",
opacity: 0.5,
backgroundColor: "green",
})
),
transition("open => closed", [animate("1s")]),
transition("closed => open", [animate("0.5s")]),
//...
]),
],
template: `<div [@toggle]="isOpen ? 'open' : 'closed'"></div>`,
})
export class ExampleComponent {}
При работе с переходами можно указывать не только состояния, указанные в функции state(). Анимации в Angular поддерживают следующие состояния:
- * - любое состояние. Полезно для определения переходов, которые применяются независимо от начального или конечного состояния HTML-элемента. Можно использовать конструкцию * => *, для того, чтобы определить переходы тем состояниям, у которых не назначена анимация. Эту конструкцию можно добавить после того, как будут перечислены конкретные переходы состояний.
- void - состояние, когда элемент появляется в DOM или удаляется из него. Например, при ngIf. Void входит в состояние *.
animations: [
trigger("openClose", [
state(
"open",
style({
height: "200px",
opacity: 1,
backgroundColor: "yellow",
})
),
state(
"closed",
style({
height: "100px",
opacity: 0.5,
backgroundColor: "green",
})
),
transition("open => closed", [animate("1s")]),
transition("closed => open", [animate("0.5s")]),
transition("* => closed", [animate("1s")]),
transition("* => open", [animate("0.5s")]),
transition("* => open", [animate("1s", style({ opacity: "*" }))]),
transition("* => *", [animate("1s")]),
]),
];
Два вышеперечисленных состояния можно использовать вместе - void => * и * => void. У этих конструкций есть алиасы - :enter (void => *) и :leave (* => void). Например:
const animation = trigger("eventTrigger", [
transition("void => *", [
style({ opacity: 0 }),
animate("5s", style({ opacity: 1 })),
]),
// or
transition(":enter", [
style({ opacity: 0 }),
animate("5s", style({ opacity: 1 })),
]),
//-------------//
transition("* => void", [animate("5s", style({ opacity: 0 }))]),
//or
transition(":leave", [animate("5s", style({ opacity: 0 }))]),
]);
Для работы с переходами можно использовать числовые и булевы значения. При работе с числовыми значениями, можно использовать алиасы :increment и :decrement. С булевыми значениями можно просто прописать true/false. Например:
@Component({
selector: "toggle",
animations: [
trigger("isOpen", [
state("true", style({ height: "*" })),
state("false", style({ height: "0px" })),
transition("true <=> false", [animate("1s")]),
]),
],
template: `<div [@isOpen]="isOpen ? true : false"></div>`,
})
export class ToggleComponent {
public isOpen: boolean = true;
}
@Component({
template: `<ul class="heroes" [@filterAnimation]="heroTotal"></ul>`,
animations: [
trigger("filterAnimation", [
transition(":enter, * => 0, * => -1", []),
transition(":increment", [
query(
":enter",
[
style({ opacity: 0, width: "0px" }),
stagger(50, [
animate("300ms ease-out", style({ opacity: 1, width: "*" })),
]),
],
{ optional: true }
),
]),
transition(":decrement", [
query(":leave", [
stagger(50, [
animate("300ms ease-out", style({ opacity: 0, width: "0px" })),
]),
]),
]),
]),
],
})
export class HeroListPageComponent implements OnInit {
public heroTotal: number = -1;
}
Примечание: хорошей практикой является перенос анимаций в отдельные файлы *.animation.ts. Эта практика уменьшит размер файла компонента, обеспечит декомпозицию, даст возможность переиспользования анимаций.
Гайд ангуляра по переиспользованию анимаций
Анимацию можно принудительно отключить как в отдельном компоненте, так и во всем приложении.
Для отключения анимации в компоненте нужно указать [@.disabled]='isDisabled' в нужной ноде компонента. Например:
<div [@.disabled]="isDisabled"></div>
Для отключения анимации во всем приложении, нужно указать @HostBinding('@.disabled') в корневом компоненте. Например:
@Component({
selector: "app-root",
templateUrl: "app.component.html",
styleUrls: ["app.component.css"],
animations: [
// animation go here
],
})
export class AppComponent {
@HostBinding("@.disabled")
public animationsDisabled = true;
}
Можно указывать конкретные значения стилей в определенный промежуток времени с помощью keyframes()
Есть возможность запускать анимации параллельно, указав их в функции group(). Запускать последовательно с помощью функции sequence().
Анимацию можно применять к конкретному селектору, который можно указать в параметрах фукнции query(). Так же. можно управлять анимациями дочерних элементов с помощью animateChild() (только для анимаций, описанных с помощью Angular)
Объясните механизм загрузки (bootstrap) Angular-приложения в браузере?
Запуск Angular приложения начинается с файла main.ts. Этот файл содержит в себе примерно следующее:
import { platformBrowserDynamic } from "@angular/platform-browser-dynamic";
import { AppModule } from "./app/app.module";
const platform = platformBrowserDynamic();
platform.bootstrapModule(AppModule);
platformBrowserDynamic запускает AppModule. После этого, начинает работать логика в AppModule.
В AppModule обычно задается компонент, который будет использоваться для отображения при загрузке. Компонент находится в параметре bootstrap
@NgModule({
imports: [BrowserModule, FormsModule],
declarations: [AppComponent],
bootstrap: [AppComponent],
})
export class AppModule {}
Как происходит взаимодействие компонентов в Angular (опишите components view)?
Взаимодействие компонентов может быть:
- между родительским и дочерним компонентом - селектор одного компонента объявлен в шаблоне другого
- между компонентами одного уровня - селекторы компонентов не вложенные
-
@Input()/@Output() декораторы свойств - используются между дочерним и родительским компонентами. В @Input() можно получить значение из родителя. Через @Output() отправить данные из дочернего в родительский компонент.
В шаблоне родительского компонента ставится селектор дочернего. В селекторе дочернего компонента прописываются атрибуты, через которые будут передаваться данные в переменные @Input()/@Output(). Для обозначения @Input свойства в селекторе нужно прописать . Для обозначения @Output свойства в селекторе нужно прописать .
В классе родительского компонента нужно обозначить public свойства/методы, которые будут прописаны в атрибутах дочернего селектора.
В классе дочернего компонента нужно прописать public свойства с декораторами @Input()/@Output(). Названия свойств должны совпадать с именами в атрибутах дочернего селектора. В @Input() можно передать значения как обычных типов данных (string, number, Array и тп), так и потоки (Subject, Observable). В @Output обычно используется EventEmitter. Через него можно отправить значения в функцию родительского компонента, которая прописана в атрибуте селектора.
Пример
@Component({ selector: "parent", template: ` <div> <child [count]="value" (increment)="onInstement($event)"></child> </div> `, }) export class ParentComponent { public value: number = 0; public onIncrement(value: number): void { // actions with child's value } } @Component({ selector: "child", template: ` <div> <button type="button" (click)="onClickIncrement()">+1</button> </div> `, changeDetection: ChangeDetectionStrategy.OnPush, //см пункт "Какие существуют стратегии обнаружения изменений?" }) export class ChildComponent { @Input() public count: number; @Output() public increment: EventEmitter<number> = new EventEmitter(); public onClickIncrement(): void { const result = this.count++; this.increment.emit(result); } }
-
@ViewChild() директива - получение доступа к свойствам дочернего компонента. В родительском шаблоне нужно указать селектор дочернего. Так же, в родительском компоненте нужно обозначить public свойство с директивой @ViewChild().
По умолчанию, доступ к свойствам @ViewChild() можно получить в хуке ngAfterViewInit(). Так же, нужно учитывать свойство static при использовании @ViewChild(). static параметр указывает, когда можно получить доступ к ViewChild() - до или после change detection. Это может понадобится, когда @ViewChild используется в циклах (*ngFor) или доступен только по условию (*ngIf). Если static = false, то доступ можно получить до change detection в хуке ngAfterViewInit().
Примеры
@Component({ selector: "parent", template: `<child #childRef *ngIf="isShowChild"></child>`, }) export class ParentComponent { @ViewChild("childRef", { static: false }) public viewChild: ChildComponent; public isShowChild: boolean = false; public ngAfterViewInit(): void { console.log(this.viewChild.title); } } @Component({ selector: "parent", template: `<child #childRef></child>`, }) export class ParentComponent { @ViewChild("childRef", { static: false }) public viewChild: ChildComponent; public ngAfterViewInit(): void { console.log(this.viewChild.title); } }
-
Через сервис - передача данных между компонентами через единый сервис. Этим способом можно взаимодействовать с компонентами одного уровня. Так же, можно избавиться от иерархии зависимостей и не использовать всплывающие события (Output)
Необходимо создать общий сервис, который объявляется в параметре providers в общем модуле соединяемых компонентов. В сервисе можно создать public свойства и методы для передачи данных. Можно использовать Observable и Subjects для передачи данных. Пример:
@Injectable() export class CountService { private count$: BehaviorSubject<number> = new BehaviorSubject(0); public get value$(): Observable<number> { return this.count$.asObservable(); } public get value(): number { return this.count$.getValue(); } public setState(value: number): void { this.count$.next(value); } public reset(): void { this.count$.next(0); } } @Component({ selector: "counter", template: ` value = {{ counter.value$ | async }} <br/> <button type='button' (click)='counter.setState(counter.value + 1)>+1</button> <button type='button' (click)='counter.setState(counter.value - 1)>-1</button> <button type='reset' (click)='counter.reset()>reset</button> `, }) export class CounterComponent { constructor(private counter: CountService) {} }
Каков жизненный цикл у компонентов?
После создания компонента или директивы через вызов конструктора, Angular вызывает методы жизненного цикла в следующей последовательности в строго определенные моменты:
Что такое Shadow DOM и как с ним работать в Angular?
Что такое Data Binding и какие проблемы связанные с ним вы знаете?
Добавление специальной разметки сообщает Angular как соединять обе стороны. Следующая диаграмма показывает четыре формы привязки данных.
Односторонние:
Двусторонняя в основном используется в template-driven forms, сочетает в себе параметр и ивент. Вот пример, использующий привязку с директивой ngModel.
<input [(ngModel)]="hero.name">
Здесь значение попадает в input из компонента, как при привязке значения, но при изменении юзером значения новое передается в компонент и переопределяется.
Как вы используете одностороннюю и двухстороннюю привязку данных?
В чем преимущества и недостатки Regular DOM (Angular) перед Virtual DOM (React)?
Что такое ngZone?
NgZone - это сервис, который является обёрткой над zone.js, для выполнения кода внутри или вне зоны Angular. Этот сервис создаёт зону с именем angular для автоматического запуска обнаружения изменений, когда выполняются следующие условия:
Наиболее распространённое применение NgZone — это оптимизация производительности посредством выполнения асинхронной логики вне зоны Angular (метод runOutsideAngular
), тем самым не вызывая обнаружение изменений или обработку ошибок. Или наоборот, данный сервис может использоваться для выполнения логики внутри зоны (метод run
), что в конечном итоге приведёт к тому, что Angular снова вызовет обнаружение изменений и при необходимости перерисует представление.
Как обновлять представление, если ваша модель данных обновляется вне 'зоны'?
- Используя метод
ApplicationRef.prototype.tick
, который запуститchange detection
на всем дереве компонентов.
import { Component, ApplicationRef, NgZone } from "@angular/core";
@Component({
selector: "app-root",
template: ` <h1>Hello, {{ name }}!</h1> `,
})
export class AppComponent {
public name: string = null;
constructor(private app: ApplicationRef, private zone: NgZone) {
this.zone.runOutsideAngular(() => {
setTimeout(() => {
this.name = window.prompt("What is your name?", "Jake");
this.app.tick();
}, 5000);
});
}
}
- Используя метод
NgZone.prototype.run
, который также запуститchange detection
на всем дереве.
import { Component, NgZone } from "@angular/core";
import { SomeService } from "./some.service";
@Component({
selector: "app-root",
template: ` <h1>Hello, {{ name }}!</h1> `,
providers: [SomeService],
})
export class AppComponent {
public name: string = null;
constructor(private zone: NgZone, private service: SomeService) {
this.zone.runOutsideAngular(() => {
this.service.getName().then((name: string) => {
this.zone.run(() => (this.name = name));
});
});
}
}
Метод run
под капотом сам вызывает tick
, а параметром принимает функцию, которую нужно выполнить перед tick
. То есть:
this.zone.run(() => (this.name = name));
// идентично
this.name = name;
this.app.tick();
- Используя метод
ChangeDetectorRef.prototype.detectChanges
, который запуститchange detection
на текущем компоненте и дочерних.
import { Component, NgZone, ChangeDetectorRef } from "@angular/core";
@Component({
selector: "app-root",
template: ` <h1>Hello, {{ name }}!</h1> `,
})
export class AppComponent {
public name: string = null;
constructor(private zone: NgZone, private ref: ChangeDetectorRef) {
this.zone.runOutsideAngular(() => {
this.name = window.prompt("What is your name?", "Jake");
this.ref.detectChanges();
});
}
}
Что такое EventEmitter и как подписываться на события?
Используется в директивах и компонентах для подписки на пользовательские ивенты синхронно или асинхронно, и регистрации обработчиков для этих ивентов.
Что такое Change Detection, как работает Change Detection Mechanism?
Change Detection - процесс синхронизации модели с представлением. В Angular поток информации однонаправленный, даже при использовании ngModel
для реализации двустороннего связывания, которая является синтаксическим сахаром поверх однонаправленного потока.
Change Detection Mechanism - продвигается только вперед и никогда не оглядывается назад, начиная с корневого (рут) компонента до последнего. В этом и есть смысл одностороннего потока данных. Архитектура Angular приложения очень проста — дерево компонентов. Каждый компонент указывает на дочерний, но дочерний не указывает на родительский. Односторонний поток устраняет необходимость $digest
цикла.
Какие существуют стратегии обнаружения изменений?
Всего есть две стратегии - Default
и OnPush
. Если все компоненты используют первую стратегию, то Zone
проверяет все дерево независимо от того, где произошло изменение. Чтобы сообщить Angular, что мы будем соблюдать условия повышения производительности нужно использовать стратегию обнаружения изменений OnPush
. Это сообщит Angular, что наш компонент зависит только от входных данных и любой объект, который передается ему должен считаться immutable. Это все построено на принципе автомата Мили, где текущее состояние зависит только от входных значений.
Сколько Change Detector(ов) может быть во всем приложении?
У каждого компонента есть свой Change Detector, все Change Detector(ы) наследуются от AbstractChangeDetector.
Основное отличие constructor от ngOnInit?
Конструктор сам по себе является фичей самого класса, а не Angular. Основная разница в том, что Angular запустит ngOnInit
, после того, как закончит настройку компонента, то есть, это сигнал, благодаря которому свойства @Input()
и другие байндинги, и декорируемые свойства доступны в ngOnInit
, но не определены внутри конструктора, по дизайну.
Для чего нужен RxJS и какую проблему он решает?
Что такое Observable?
Observable— это наблюдатель, который подписывается и реагирует на все события до момента отписки.
В чём разница между Observable и Promise?
Promise обрабатывает одно значение по завершению асинхронной операции, вне зависимости от ее исхода, и не поддерживают отмену операции.
Observable же является потоком, и позволяет передавать как ноль, так и несколько событий, когда callback вызывается для каждого события.
В чём разница между Observable и BehaviorSubject/Subject (Higher Order Observables)?
Subjects - специальные Observable. Представьте, что есть спикер с микрофоном, который выступает в комнате, полной людей. Это и есть Subjects, их сообщение передается сразу нескольким получателям. Обычные же Observables можно сравнить с разговором 1 на 1.
- Subject - является multicast, то есть может передавать значение сразу нескольким подписчикам.
- BehaviorSubject - требует начальное значение и передает текущее значение новым подпискам.
В чем разница между Subject, BehaviorSubject, ReplaySubject, AsyncSubject?
- Subject - не хранит свои предыдущие состояния, зритель получает информацию только тогда, когда Subject сгенерирует новое событие, используя метод
.next()
. - BehaviorSubject - при подписке поведенческий Subject уведомляет своего зрителя о последнем произошедшем в нём событии или, если в Subject-е не происходило событий, создаёт для зрителя событие с изначальной информацией, которая передаётся при создании Subject-а.
- ReplaySubject - при подписке повторяющийся Subject уведомляет своего нового зрителя о всех произошедшем в нём событиях с момента создания. Для оптимизации при создании повторяющегося Subject-а можно передать число последних событий, которые будут повторяться для каждого нового зрителя. Стоит отметить, что создание ReplaySubject-а c числом повторяющихся событий равное 1 эквивалетно созданию BehaviorSubject-а.
- AsyncSubject - асинхронный Subject уведомляет своих зрителей только о последнем произошедшем событии и только когда Subject успешно завершается. Если AsyncSubject завершится ошибкой, его зрители будут уведомлены только об ошибке.
В чём разница между операторами switchMap, mergeMap, concatMap?
- switchMap - отменит подписку на Observable, возвращенный ее аргументом project, как только он снова вызовет ее в новом элементе.
- mergeMap - в отличие от switchMap позволяет реализовать одновременно несколько внутренних подписок.
- concatMap - последовательно обрабатывает каждое событие, в отличие от mergeMap.
Как бы вы кешировали наблюдаемые данные из потоков (stream)?
Что такое Dependency Injection?
Это важный паттерн шаблон проектирования приложений. В Angular внедрение зависимостей реализовано из-под капота.
Зависимости - это сервисы или объекты, которые нужны классу для выполнения своих функций. DI -позволяет запрашивать зависимости от внешних источников.
Что такое Singleton Service и с какой целью его используют в Angular?
Это сервисы, объявленные в приложении и имеющие один экземпляр на все приложение. Его можно объявить двумя способами:
Как можно определить свой обработчик ErrorHandler, Logging, Cache в Angular?
Что такое управление состоянием приложения?
В чем отличие между NGRX, NGXS, Akita и какую проблему они решают?
Какими способами можно взаимодействовать с API бэкенда, что требуется для проксирования запросов?
Взаимодействие с API
В экосистеме ангуляр существует пакет для общения с сервером
(@angular/common/http), которого достаточно для повседневной разработки. Его интерфейс основан на rxjs потоках, поэтому его легко использовать для работы с потоками данных в приложении.
Кроме этого, как и в ванильной версии javascript, можно использовать XMLHttpRequest, fetch API, axios(или многие другие библиотеки), но их использование вместо встроенного клиента, считается неоправданным и всячески возбраняется.
Существуют и другие способы взаимодействия с сервером(см. Веб-сокеты), но для них не существует каноничных встроенных библиотек, поэтому используются сторонние библиотеки или собственные реалиации. Хорошей практикой здесь будет привести интерфейс построенный на промисах и обратных вызовах(callback) к интерфейсу основанному на rxjs потоках, для естественного использования в экосистеме Angular.
Proxy
Чтобы тестировать взаимодействие приложения с сервером, который должен находиться на том же домене, используется функциональность в Angular CLI для этого нужно создать файл с конфигурацией прокси, где будут указаны:
- Контекст для проксирования
- Ссылка на работающий экземпляр API
- secure: false для работы в тестовой среде без сертификатов
Также большинство серверов не настроены для работы с разными доменами(CORS), поэтому для корректной работы на API сервере, необходимо явно указать с какого домена(ов) можно принимать запросы.
Что такое HTTP Interceptors?
Interceptor (перехватчик) - просто причудливое слово для функции, которая получает запросы / ответы до того, как они будут обработаны / отправлены на сервер. Нужно использовать перехватчики, если имеет смысл предварительно обрабатывать многие типы запросов одним способом. Например нужно для всех запросов устанавливать хедер авторизации `Bearer`:
token.interceptor.ts
import { Injectable } from "@angular/core";
import {
HttpInterceptor,
HttpRequest,
HttpHandler,
HttpEvent,
} from "@angular/common/http";
import { Observable } from "rxjs/Observable";
@Injectable()
export class TokenInterceptor implements HttpInterceptor {
public intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
const token = localStorage.getItem("token") as string;
if (token) {
req = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`,
},
});
}
return next.handle(req);
}
}
И регистрируем перехватчик как синглтон в провайдерах модуля:
app.module.ts
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { HTTP_INTERCEPTORS } from "@angular/common/http";
import { AppComponent } from "./app.component";
import { TokenInterceptor } from "./token.interceptor";
@NgModule({
imports: [BrowserModule],
declarations: [AppComponent],
bootstrap: [AppComponent],
providers: [
{
provide: HTTP_INTERCEPTORS,
useClass: TokenInterceptor,
multi: true, // <--- может быть зарегистрирован массив перехватчиков
},
],
})
export class AppModule {}
Как использовать Json Web Tokens для аутентификации при разработке на Angular?
Как обрабатываются атаки XSS и CSRF в Angular?
Что такое роутинг и как его создать в Angular?
Роутинг позволяет реализовать навигацию от одного view приложения к другому при работе пользователя с приложением.
Это реализовано через взаимодействие с адресной строкой, Angular Router интерпретирует ее как инструкцию по переходу между view. Возможна передача параметров вспомогательному компоненту для конкретизирования предоставляемого контента. Навигация может осуществлять по ссылкам на странице, кнопкам или другим элементам, как кнопки "вперед-назад" в браузере.
Для создания роутинга первым делом необходимо импортировать "RouterModule" и "Routes" в AppModule.
Затем необходимо реализовать конфигурацию по приложению, определить path и относящие к ним компоненты, и в метод RouterModule.forRoot() передать конфигурацию.
Наконец необходимо добавить routerLink в шаблон.
Каков жизненный цикл у Angular Router?
- NavigationStart - начало навигации. Возникает во время нажатия на директиву router link, вызове функций navigate и navigateByUrl
- RoutesRecognized - cопоставление URL-адресов и редиректы. Роутер сопоставляет URL-адрес навигации из первого события с одним из свойств path в конфигурации, применяя любые редиректы по-пути.
- GuardsCheckStart, GuardsCheckEnd - функции, которые использует роутер для определения может ли он выполнить навигацию. Пример:
// router configuration const config = { path: "users", /* ... */ canActivate: [CanActivateGuard], }; class Guard { // router guard implementation canActivate( route: ActivatedRouteSnapshot, state: RouterStateSnapshot ): boolean { return this.auth.isAuthorized(route.queryParams.login); } }
Если вызов
isAuthorized(route.queryParams.login)
возвращает true, guard завершится успехом. В противном случае guard упадет, роутер сгенерирует событиеNavigationCancel
и отменит всю дальнейшую навигацию.Другие guard включают
canLoad
(должен ли модуль быть лениво загружен или нет).canActivateChild
иcanDeactivate
(которые полезны для предотвращения ухода пользователя со страницы, например, при заполнении формы). - ResolveStart, ResolveEnd - функции, которые мы можем использовать для подгрузки данных во время навигации. Например:
// router configuration const config = { path: "users", /* ... */ resolve: { users: UserResolver }, }; // router resolver implementation export class UserResolver implements Resolve<Observable<any>> { constructor(private userService: MockUserDataService) {} resolve(): Observable<any> { return this.userService.getUsers(); } }
Результат, то есть данные, будет положен в
data
объект сервисаActivatedRoute
с ключомusers
. Данная информация может быть прочитаны с помощью подписки наdata
observable
.export class UsersComponent implements OnInit { public users = []; constructor(private route: ActivatedRoute) {} ngOnInit() { this.route.data .pipe(first()) .subscribe((data) => (this.users = data.users)); } }
- ActivationStart, ActivationEnd, ChildActivationStart, ChildActivationEnd - события, во время которых активируются компоненты и отображаются их с помощью .Роутер может извлечь необходимую информацию о компоненте из дерева ActivatedRouteSnapshots. Он был построен в предыдущие шаги навигационного цикла.
- Updating the URL - последний шаг в навигационном цикле — это обновление URL-адреса в address bar
Что такое ленивая загрузка (Lazy-loading) и для чего она используется?
В чем разница между Routing и Navigation?
Как загрузить данные до того как активируется роут?
Что такое FormGroup и FormControl и для чего они используются?
Они используются для создания и работы с формами.
Что такое реактивные формы в Angular?
Как применять валидацию для простых и сложных форм?
Можно использовать встроенные валидаторы, либо создать свои. Валидаторы добавляются
В чем разница между Angular CLI и Webpack Development Environment?
Что такое JIT и AOT, в чем их отличия и каковы сферы применения?
Angular приложение можно скомпилировать с помощью команд ng serve и ng build. При этом, можно работать с разными видами компиляции:
- JIT - (Just-In-Time compilation) - компиляция "на лету", динамическая компиляция. В Angular используется по умолчанию.
- AOT - (Ahead-Of-Time compilation) - компиляции перед исполнением.
Основные различия:
Параметры | JIT | AOT |
Когда компилируется | в момент запуска приложения в браузере | в момент сборки приложения |
Рекомендуется использовать для | локальной разработки | создания продуктовой сборки |
Как включить | Не нужно выставлять дополнительных флагов | Нужно добавить флаг --aot или --prod |
Скорость | Скорость компиляции быстрее, загрузка в браузере дольше | Скорость компиляции дольше, загрузка в браузере быстрее |
Размер бандла | Бандл имеет большой размер из-за включенного в бандл компилятора. | Бандл имеет небольшой размер, тк содержит полностью скомпилированный и оптимизированный код. |
Выявление ошибок | Ошибки отобразятся во время запуска приложения в браузере | Выявление ошибок во время компиляции |
Работа с файлами | Может компилировать только измененные файлы отдельно | Компилирует сразу все файлы приложения |
Что такое Unit-тестирование, интеграционное, e2e-тестирование (End-to-End) и как оно применяется в Angular?
Что такое Karma, Jasmine (зачем их используют совместно при разработке на Angular)?
В чем разница между Jest и Karma?
В чем разница между Protractor и Cypress?
Как протестировать входные параметры и всплывающие события компонентов?
Требования к написанию кода на TypeScript
На самом деле требования бывают разные и зависят от команды к команде. Самые эффективные для себя считаю использование модификаторов доступа и принудительного указания типов данных для всех переменных, методов и членов класса, которые вы используете в коде. Желательно все необходимые правила конвенции кода настраивать в ESLint.
// my.ts
export interface My {}
// my-impl.ts
export class MyImp implements My {
public field: string;
public myMethod(): void {
// ...
}
private myProtectedMethod(): Date {
return new Date();
}
private myPrivateMethod(): MyClassImpl {
// ...
return this;
}
}