Директивы атрибутов — формы Angular

Директивы атрибутов — формы Angular

От автора: в этой статье мы рассмотрим, как создаются в Angular директивы, которые взаимодействуют с Элементами управления форм Angular. Мы сделаем это, создав директиву атрибута, которая перехватывает элемент управления базой и изменяет границу элемента узла директивы.

Директива будет поддерживать как шаблонные, так и реактивные формы.

API директив

Начнем с создания директивы и определения ее API. Имена @Input имеют префикс селектора директивы. Мы переименуем их, чтобы имена переменных были короткими и более читаемыми, мы установим некоторые интуитивные значения по умолчанию.

@Directive({ selector: '[appValidationBorder]'
})
export class ValidationBorderDirective { private readonly colors: { VALID: string; INVALID: string; PENDING: string; DISABLED: string; }; @Input('appValidationBorderWidth') borderWidth: string; @Input('appValidationBorderStyle') borderStyle: string;
}

Словарь colors содержит возможные состояния валидации формы, такие как ключи, и будет использоваться для изменения стиля границы.

NgControl

Чтобы создать действительно многоразовую директиву, нам нужно поддерживать как шаблонные, так и реактивные формы. Но что конкретно мы вводим? Пользователь может использовать любое из следующих действий:

NgModel (шаблонные формы)

<input [(ngModel)]="value" appValidationBorder>

FormControlName (реактивные формы)

<input formControlName="value" appValidationBorder>

FormControlDirective (реактивные формы)

<Input [formControl]="value" appValidationBorder>

К счастью для нас, все они представлены как NgControl.

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

constructor(@Self() ngControl: NgControl) {}

Используя NgControl, мы получаем доступ к таким свойствам, как значение элемента управления, статус валидации, ошибки и многим другим.

Большинство свойств попадают в класс AbstractControl, с которым вы, вероятно, знакомы, если раньше работали с Angular Forms.

Привязка хостов

Теперь, когда у нас есть доступ к элементу управления формы, давайте используем его для отображения или скрытия границы с помощью декоратора @HostBinding.

@HostBinding('style.border-style')
get borderStyleCss() { return this.showBorder ? this.borderStyle : null;
} @HostBinding('style.border-width')
get borderWidthCss() { return this.showBorder ? this.borderWidth : null;
} @HostBinding('style.border-color')
get borderColorCss() { return this.showBorder ? this.colors[this.ngControl.status] : null;
} get showBorder() { return this.ngControl.dirty || this.ngControl.touched;
}

Мы будем отображать границу только после того, как пользователь взаимодействует с элементом управления формы.
Для этого мы проверим свойства NgControl dirty и touched. Проверяя dirty и touched, мы не даем отображать границы, пока пользователь не:

изменит значение, переключая элемент управления на dirty,

коснется элемента управления, устанавливая для элемента управления touched.

Универсальная конфигурация

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

Во-первых, давайте создадим интерфейс конфигурации:

export interface ValidationBorderConfig { borderWidth: string; borderStyle: string; colors: { VALID: string; INVALID: string; PENDING: string; DISABLED: string; };
}

Затем мы реализуем статический метод forRoot класса ValidationBorderModule. Он предоставит конфигурацию валидации в токене VALIDATION_BORDER_CONFIG.

@NgModule({ imports: [CommonModule, FormsModule], declarations: [ValidationBorderDirective], exports: [ValidationBorderDirective]
})
export class ValidationBorderModule { static forRoot(config: ValidationBorderConfig): ModuleWithProviders { return { ngModule: ValidationBorderModule, providers: [{ provide: VALIDATION_BORDER_CONFIG, useValue: config }] }; }
}

Наконец, давайте задействуем конфигурацию внутри директивы с использованием декоратора @Inject.

constructor( @Self() private ngControl: NgControl, @Inject(VALIDATION_BORDER_CONFIG) config: ValidationBorderConfig ) { this.colors = config.colors; this.borderWidth = config.borderWidth; this.borderStyle = config.borderStyle;
}

С внедренным методом forRoot у нас есть чистый способ настройки директивы. Мы можем импортировать его один раз в app.module, и он будет настраивать каждую директиву, используемую во всем приложении.

@NgModule({ imports: [ ValidationBorderModule.forRoot({ borderStyle: 'solid', borderWidth: '3px', colors: { VALID: 'green', INVALID: 'red', PENDING: 'yellow', DISABLED: 'silver' } }) ],
})
export class AppModule {}

Шаблонный пример

@Component({ selector: 'app-template-forms-example', template: ` <input [(ngModel)]="value" required appValidationBorder> `
})
export class TemplateFormsExampleComponent { value = '';
}

Реактивный пример

@Component({ selector: 'app-reactive-forms-example', template: ` <input [formControl]="formControl" appValidationBorder> `,
})
export class ReactiveFormsExampleComponent { formControl = new FormControl('', Validators.required);
}

Заключение

Нам удалось создать гибкую директиву атрибутов, которая работает как с шаблонами, так и с реактивными формами Angular. В процессе мы научились использовать NgControl для включения ближайшей директивы формы и применять @HostBinding для отображения статуса формы путем стилизации границы элемента. Мы реализовали удобный способ настройки директивы с помощью метода forRoot.

@Directive({ selector: '[appValidationBorder]'
})
export class ValidationBorderDirective { private readonly colors: { VALID: string; INVALID: string; PENDING: string; DISABLED: string; }; @Input('appValidationBorderWidth') borderWidth: string; @Input('appValidationBorderStyle') borderStyle: string; @HostBinding('style.border-style') get borderStyleCss() { return this.showBorder ? this.borderStyle : null; } @HostBinding('style.border-width') get borderWidthCss() { return this.showBorder ? this.borderWidth : null; } @HostBinding('style.border-color') get borderColorCss() { return this.showBorder ? this.colors[this.ngControl.status] : null; } get showBorder() { return this.ngControl.dirty || this.ngControl.touched; } constructor( @Self() private ngControl: NgControl, @Inject(VALIDATION_BORDER_CONFIG) config: ValidationBorderConfig ) { this.colors = config.colors; this.borderWidth = config.borderWidth; this.borderStyle = config.borderStyle; }
}

github | stackblitz

Автор: Tomasz Kula

Источник: https://netbasal.com/

Редакция: Команда webformyself.