Угловой ввод не запускается должным образом при использовании OnPush

#angular #angular-reactive-forms #controlvalueaccessor

#угловой #угловые реактивные формы #controlvalueaccessor

Вопрос:

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

 @Component({
  selector: 'app-project',
  templateUrl: './project.component.html',
  styleUrls: ['./project.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ProjectComponent implements ControlValueAccessor, OnInit {

  @Input('companyNumber') set companyNumber(companyNumber: string) {
    this._companyNumber = companyNumber;
  }

  ctrlErrorStateMatcher = new CtrlErrorStateMatcher();
  private _companyNumber: string;

  constructor(
    private readonly _costCenterService: CostCenterService,
    @Optional() @Self() public ngControl: NgControl,

  ) {
    if (this.ngControl != null) {
      // Setting the value accessor directly (instead of using the providers) to avoid running into a circular import.
      this.ngControl.valueAccessor = this;
    }
  }

  onTouched = (_value?: any) => { };
  onChanged = (_value?: any) => { };

  writeValue(val: string): void {
    if (val) {
      this.ngControl.control?.setValue(val);
    }
  }

  registerOnChange(fn: any): void {
    this.onChanged = fn;
  }

  registerOnTouched(fn: any): void {
    this.onTouched = fn;
  }

  ngOnInit() {
    this.ngControl.control.setAsyncValidators(this.validate.bind(this));
    this.ngControl.control.updateValueAndValidity();
  }
  validate(control: AbstractControl): Observable<ValidationErrors | null> {
    const isValid = this._costCenterService
      .validateProject(control.value, this._companyNumber)
      .pipe(
        map(response => {
          return response ? null : { inValidProjectNumber: true };
        })
      );
    return isValid;
  }
}

export class CtrlErrorStateMatcher implements ErrorStateMatcher {
  isErrorState(control: FormControl): boolean {
    return !!(control amp;amp; control.invalid);
  }
}
  

Так что все это отлично работает, когда я МЕНЯЮ значение во входных данных (а не просто запускаю (размытие)).. но когда @Input изменяется, он не обновляет состояние (запускается validate())…

если я затем изменю значение во входных данных, это сработает … удаление OnPush устраняет проблему, но это не то, что я хочу сделать..

Обновление: шаблон

 <mat-form-field appearance="outline">
  <mat-label>Label</mat-label>
  <input matInput #projectInput [value]="ngControl.value" [formControl]="ngControl?.control"
    (change)=" onChanged($event.target.value)" (blur)="onTouched()" [errorStateMatcher]="ctrlErrorStateMatcher" />
  <mat-error *ngIf="ngControl.hasError('inValidProjectNumber')">
    Error
  </mat-error>
</mat-form-field>
  

Комментарии:

1. Вам нужно будет передать свой ввод в onChanged функцию

2. @MikeS Я добавил, как выглядит шаблон … как вы можете видеть, я уже использую функцию OnChanged :/

3. Используйте событие ввода вместо изменения (input)=» OnChanged($event.target.value)»

4. Проверит это .. но я не вижу, как это будет иметь какое-либо значение.. изменение запускается, как и ожидалось

5. Нет .. это не имело никакого значения @Gorynych

Ответ №1:

Я обнаружил эту проблему с git https://github.com/angular/angular/issues/12378

поэтому для меня решением было добавить канал завершения и использовать markForCheck ()

   validate(control: AbstractControl): Observable<ValidationErrors | null> {
    const isValid = this._costCenterService
      .validateProject(control.value, this._companyNumber)
      .pipe(
        map(response => {
          return response ? null : { inValidProjectNumber: true };
        }),
        finalize(() => this.changeDetectorRef.markForCheck())
      );
    return isValid;
  }