использовать formControlName для настраиваемого компонента ввода в реактивной форме

Существует настраиваемый компонент ввода, который используется в реактивной форме с проверкой:

@Component({
    moduleId: module.id.toString(),
    selector: 'custom-select',
    templateUrl: 'custom-select.component.html',
    styleUrls: ['custom-select.component.css']
})
export class CustomSelectComponent {
    @Input() public items: SelectModel[];
    public model: SelectModel;
    constructor(private customSelectService: CustomSelectService) {
        this.customSelectService.Selected.subscribe((data: SelectModel) => {
            this.model = data;
        });
    }
    public newSelect(select: SelectModel): void {
        this.customSelectService.updateSelected(select);
    }
}

который отлично работает, я использую custom-select в реактивной форме и хочу проверить его, как показано ниже:

<custom-select id="country" [items]="selectItems" formControlName="country"></custom-select>
<div *ngIf=" myFrom.controls['country'].invalid && (myFrom.controls['country'].dirty 
             || myFrom.controls['country'].touched) " class="ha-control-alert">
    <div *ngIf="myFrom.controls['country'].hasError('required')">Country is required</div>
</div>

так я объявляю форму в компоненте

this.myFrom = this.formBuilder.group({
    country: [null, Validators.required],
})

но когда я добавляю formControlName для проверки, появляется сообщение об ошибке: Нет средства доступа к значению для элемента управления формой с именем: 'страна'. Как мне с этим справиться?


person Paridokht    schedule 10.09.2017    source источник
comment
value accessor необходимо реализовать в настраиваемых входах. можешь опубликовать custom-select   -  person Rajez    schedule 10.09.2017
comment
Вам необходимо реализовать ControlValueAccessor, тогда он будет работать с обоими типами форм. @Rajez это уже в посте   -  person jonrsharpe    schedule 10.09.2017
comment
@Rajez Я уже писал, возможно, плохое форматирование   -  person Paridokht    schedule 10.09.2017
comment
@jonrsharpe как мне это реализовать ???, я имею в виду, что должно быть реализовано в его методах ??   -  person Paridokht    schedule 10.09.2017
comment
Пожалуйста, прочтите документацию, на которую я уже ссылался, в которой рассказывается, какие методы следует реализовать, и приводятся примеры реализации.   -  person jonrsharpe    schedule 10.09.2017
comment
blog.oughttram.io/ angular / 2016/07/27 / он показывает, как создавать настраиваемые элементы управления формой   -  person Rajez    schedule 10.09.2017
comment
@jonrsharpe Мне что-то не хватает в этой ссылке на документы Angular? Я не вижу примеров. У меня эта проблема с большинством документов Angular ... несуществующие примеры и отсутствие реальной, описательной помощи. По сути, это просто дамп intellisense, который дает мой редактор, и ничего больше :( Я что-то делаю не так? Последняя ссылка Раджеза была идеальной (спасибо!).   -  person pbarranis    schedule 10.07.2018


Ответы (2)


ШАГИ

  1. Добавьте поставщика для NG_VALUE_ACCESSOR в декоратор

  2. Реализуйте ControlValueAccessor в классе

  3. Создайте функцию onChange следующим образом: onChange = (value: boolean) = ›{};

  4. Добавьте методы registerOnChange, writeValue и registerOnTouched интерфейса ControlValueAccessor

  5. В методе, в котором будет изменено значение select, вызовите функцию onChange, передав в качестве параметра значение select.

     import ...
     import { Component, forwardRef } from '@angular/core';
     import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
    
    
     @Component({
         moduleId: module.id.toString(),
         selector: 'custom-select',
         templateUrl: 'custom-select.component.html',
         styleUrls: ['custom-select.component.css'],
         // STEP 1
         providers: [{
           provide: NG_VALUE_ACCESSOR,
           multi: true,
           useExisting: forwardRef(() => CustomSelectComponent)
         }]
     })
     // STEP 2
     export class CustomSelectComponent implements ControlValueAccessor {
         // STEP 3
         onChange = (value: SelectModel) => {};
         @Input() public items: SelectModel[];
         public model: SelectModel;
         constructor(private customSelectService: CustomSelectService) {
             this.customSelectService.Selected.subscribe((data: SelectModel) => {
                 this.model = data;
             });
         }
         public newSelect(select: SelectModel): void {
             // STEP 5
             this.onChange(select);
             this.customSelectService.updateSelected(select);
         }
         // STEP 4
         registerOnChange(fn: (value: SelectModel) => void): void {
             this.onChange = fn;
         }
         writeValue() {}
         registerOnTouched(){}
     }
    

Не забудьте добавить formControlName в селектор.

person Julio Cesar Brito Gomes    schedule 10.04.2021

Добавьте это к поставщикам ребенка:

viewProviders: [{
  provide: ControlContainer, 
  useExisting: FormGroupDirective 
}]

Причина, по которой он терпит неудачу, в первую очередь заключается в том, что он не смотрит в родительскую область для FormGroup, поэтому он просто передает его дочернему элементу.

Затем вам нужно заставить его думать, что у вашего элемента управления есть valueAccessor, который ему не нужен, потому что вы только что создали своего рода «прокладку».

export const NOOP_VALUE_ACCESSOR: ControlValueAccessor = 
{
    writeValue(): void {},
    registerOnChange(): void {},
    registerOnTouched(): void {}
};

Затем в конструкторе для вашего ребенка:

constructor(@Self() @Optional() public ngControl: NgControl) 
{
    if (this.ngControl)
    {
        this.ngControl.valueAccessor = NOOP_VALUE_ACCESSOR;
    }
}

Затем вы можете использовать formControlName как обычно внутри дочернего элемента.

Если вам нужно передать formControlName из родительского компонента (что вы, вероятно, сделали бы, если бы он задумывался как многоразовый элемент управления), просто создайте свойство string @Input и сделайте это в дочернем:

    <input matInput [placeholder]="desc" 
           [formControlName]="formControlName">
person Simon_Weaver    schedule 12.07.2021
comment
Спасибо stackoverflow.com/a/65177817/16940 за часть этого ответа NOOP. - person Simon_Weaver; 13.07.2021