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()
, доступен только для чтения и может быть изменен только через шаблон.
Преобразование входных данных
Модельные входы не поддерживают преобразование входных данных, что возможно с сигнальными входами.
Это перевод оригинального материала