Почему не удается найти элемент управления с именем: 'username' только случайно? - PullRequest
0 голосов
/ 12 февраля 2020

У меня есть Angular реактивная форма с работами большую часть времени. Но иногда, случайно, я получаю ошибку

ERROR Error: Cannot find control with name: 'username'
    at _throwError (forms.js:2337)
    at setUpControl (forms.js:2245)
    at FormGroupDirective.push../node_modules/@angular/forms/fesm5/forms.js.FormGroupDirective.addControl (forms.js:5471)
    at FormControlName.push../node_modules/@angular/forms/fesm5/forms.js.FormControlName._setUpControl (forms.js:6072)
    at FormControlName.push../node_modules/@angular/forms/fesm5/forms.js.FormControlName.ngOnChanges (forms.js:5993)
    at checkAndUpdateDirectiveInline (core.js:21093)
    at checkAndUpdateNodeInline (core.js:29495)
    at checkAndUpdateNode (core.js:29457)
    at debugCheckAndUpdateNode (core.js:30091)
    at debugCheckDirectivesFn (core.js:30051)

Компонент представляет собой простую страницу входа в систему, HTML это

<div id="loginSection" class="middle-dashboard-box">
  <div class="login-title content-inner-title" i18n="logintitle|@@loginheader">Login</div>
  <form class="login-form" [formGroup]="loginFormGroup" (ngSubmit)="login($event)">
    <mymat-form-error></mymat-form-error>
    <div>
      <mat-form-field class="login-input input-control-width">
        <input matInput formControlName="username" i18n-placeholder="Username|@@username" placeholder="Username">
      </mat-form-field>
    </div>
    <div>
      <mat-form-field class="login-input input-control-width"> 
        <input matInput formControlName="password" type="password" i18n-placeholder="Password|@@password" placeholder="Password">
      </mat-form-field>
    </div>
    <div class="forgot-pass">
      <a href="#public/forgotpassword" i18n="forgot password|@@forgotPasswordLink">Forgot password?</a>
    </div>
    <button mat-raised-button color="primary" type="submit" class="main-btn login-btn"
      i18n="Login|Login button action@@login">Login</button>
  </form>
</div>

, а TS это

@Component( {
    templateUrl: './login.component.html',
    styleUrls: ['./login.component.scss']
} )
export class LoginComponent implements OnInit {

    loginFormGroup: FormGroup;

    constructor( private router: Router,
        private formBuilder: FormBuilder,
        private loginService: LoginService ) { }

    ngOnInit() {
        this.loginFormGroup = this.formBuilder.group( {
            username: ['', Validators.required],
            password: ['', Validators.required]
        } );
        this.loginFormGroup.controls['username'].setValue( this.loginService.latestUsername );
    }

    login( event ) {
        // do login stuff, not relevant here
    }
}

this.loginService.latestUsername указывает на получатель в LoginService:

get latestUsername(): string {
    return localStorage.getItem( LoginService.LATESTUSERNAME_KEY ) || ''
}

FormControlName.ngOnChanges в трассировке стека заставляет меня думать, что ng выполняет обнаружение изменений до правильной инициализации элемента управления, но поскольку подписка отсутствует или что-то подобное, я не вижу, что может вызвать это. Я наблюдаю ошибку во время тестов Selenium (есть достоверность в некоторых тестах, периодически в других), но она была одна или две при ручном нажатии. Эффект возникает только после обновления до Angular 8.2.14 (с 7)

Ответы [ 2 ]

1 голос
/ 12 февраля 2020

Глядя на исходный код из FormControlName и думая о , когда ngOnChanges () может запустить , я считаю ngOnChanges() запущенным до того, как ngOnInit() завершит построение формы .

Чтобы диагностировать это, я бы засорял ngOnChanges() и ngOnInit() с console.log(), чтобы отследить, что происходит.

Чтобы обойти проблему (если это причина) , это должно быть так же просто, как защитить форму с помощью *ngIf, чтобы убедиться, что директивы не вызывают преждевременное обнаружение изменений. В идеале вы также можете использовать OnPush стратегию обнаружения изменений.

0 голосов
/ 12 февраля 2020

Попробуйте это:

constructor(
    private router: Router,
    private formBuilder: FormBuilder,
    private loginService: LoginService
) {
    this.loginFormGroup = this.formBuilder.group({
        username: ['', Validators.required],
        password: ['', Validators.required]
    });
}

ngOnInit() {
    this.loginFormGroup.get('username').setValue(this.loginService.latestUsername);
}

Обновление:

Хорошая практика - содержать конструктор в чистоте, для этого вы можете создать функцию для создайте форму как этот пример:

constructor(
    private router: Router,
    private formBuilder: FormBuilder,
    private loginService: LoginService
) {
    this._createForm();
}

ngOnInit() {
    this.loginFormGroup.get('username').setValue(this.loginService.latestUsername);
}

private _createForm(): void {
    this.loginFormGroup = this.formBuilder.group({
        username: ['', Validators.required],
        password: ['', Validators.required]
    });
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...