Angular 17: model()
Новое дополнение к Angular - это функция model, которая улучшает двустороннее связывание данных с использованием сигналов. Эта функция упрощает управление данными, предоставляя записываемые сигналы, доступные через пары ввода/вывода в директивах и компонентах.
Двустороннее связывание с сигналом
Использование функции model() в Angular открывает дверь к безупречным возможностям двустороннего связывания, особенно когда в работе используются сигналы. Давайте проиллюстрируем это на практическом примере с компонентом пагинации и потребителем данных:
@Component({
selector: 'app-pagination',
standalone: true,
template: `{{ page() }}`,
})
export class PaginationComponent {
page = model(1);
}
В данном сценарии компонент PaginationComponent предоставляет модель страницы с помощью функции model. По умолчанию устанавливается значение 1.
@Component({
selector: 'app-todos',
standalone: true,
template: `
<app-pagination [(page)]="currentPage" />
`,
})
export class TodosComponent {
currentPage = signal(1);
}Компонент TodosComponent включает в себя компонент PaginationComponent и привязывает его сигнал currentPage к модели страницы компонента PaginationComponentс использованием синтаксиса banana-in-a-box ([(page)]="currentPage").
Замечательно то, что здесь происходит двусторонняя синхронизация между сигналом страницы компонента PaginationComponent и сигналом currentPage компонента TodosComponent. Когда один из сигналов обновляется, Angular гарантирует, что соответствующее значение в другом компоненте также обновляется.
Двунаправленная связка данных
Функция model()легко интегрируется с несигнальными значениями, сохраняя привычный опыт двусторонней привязки:
@Component({
selector: 'app-todos',
standalone: true,
template: `
<app-pagination [(page)]="currentPage" />
`,
})
export class TodosComponent {
currentPage = 1;
}Несмотря на то, что currentPage является обычным числовым свойством, Angular легко управляет двусторонней привязкой данных между компонентами.
Еще о двунаправленной связке
Одной из интересных особенностей является его совместимость с пользовательскими реализациями двусторонней привязки. Рассмотрим следующий пример:
@Component({
selector: 'app-pagination',
standalone: true,
template: `{{ page }}`,
})
export class PaginationComponent {
@Input() page = 1;
@Output() pageChange = new EventEmitter<number>();
}@Component({
selector: 'app-todos',
standalone: true,
template: `
<app-pagination [(page)]="currentPage" />
`,
})
export class TodosComponent {
currentPage = signal(1);
}
В этой настройке PaginationComponent использует настраиваемую двустороннюю привязку с page в качестве входного и pageChange в качестве выходного сигнала. Несмотря на этот настраиваемый подход, Angular без проблем интегрируется с ним. Сигнал currentPage в TodosComponent без проблем связывается с свойством page в PaginationComponent, обеспечивая двунаправленный поток данных.
Однонаправленное связывание данных с model()
Когда возникает необходимость, выбирайте однонаправленную привязку без сигналов:
@Component({
selector: 'app-todos',
standalone: true,
template: `
<app-pagination [page]="currentPage" />
`,
})
export class TodosComponent {
currentPage = 1;
}
Реагирование на изменения модели
Для реагирования на изменения модели подпишитесь на соответствующее событие, которое генерируется компонентом. Название события следует конвенции, где оно производится из имени свойства модели с добавлением постфикса "Change". Например, если ваше свойство модели называется page, соответствующее событие будет называться pageChange:
@Component({
selector: 'app-todos',
standalone: true,
template: `
<app-pagination [page]="currentPage"
(pageChange)="onChange($event)" />
`,
})
export class TodosComponent {
currentPage = 1;
onChange(page: number) {}
}Использование псевдонимов и обязательных параметров
Подобно Signal Inputs, функция model()поддерживает расширенные параметры, такие как псевдоним и обязательный параметр:
@Component({
selector: 'app-pagination',
standalone: true,
template: `{{ page() }}`,
})
export class PaginationComponent {
page = model(1, { alias: 'currentPage' });
id = model.required<string>();
}@Component({
selector: 'app-todos',
standalone: true,
template: `
<app-pagination [(currentPage)]="currentPage" />
<app-pagination [currentPage]="currentPage()"
(currentPageChange)="onChange()" />
`,
})
export class TodosComponent {
currentPage = signal(1);
}Обновление моделей и директив
@Component({
selector: 'app-select',
standalone: true,
templateUrl: './select.component.html',
})
export class SelectComponent {
placeholder = model('Select an option');
options = model<string[]>([]);
}@Directive({
selector: '[appUsersOptions]',
standalone: true,
})
export class UsersOptionsDirective {
select = inject(SelectComponent);
ngOnInit() {
this.select.placeholder.set('Select a user');
setTimeout(() => {
this.select.options.set(['John', 'Jane', 'Doe']);
}, 1000);
}
}В компоненте SelectComponent используется функция model для определения заполнителя и массива опций. Тем временем, директива UsersOptionsDirective устанавливает плейсхолдер в "Выберите пользователя" и обновляет массив опций после небольшой задержки.
Сравнение функций model() и input()
В Angular функции input() и model() используются для определения входных сигналов на основе сигналов, но они имеют различные характеристики:
Определение вывода
Функция model() устанавливает как вход, так и выход.
Тип сигналаModelSignal, используемый функцией model(), является WritableSignal, позволяющим изменять значения с помощью методов set и update из любого места. В отличие от этого, InputSignal, используемый функцией input(), доступен только для чтения и может быть изменен только через шаблон.
Преобразование входных данных
Модельные входы не поддерживают преобразование входных данных, что возможно с сигнальными входами.
Это перевод оригинального материала