为什么无法随机找到名称为 'username' 的控件?
Why Cannot find control with name: 'username' only randomly?
我有一个 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 中的一个 getter:
get latestUsername(): string {
return localStorage.getItem( LoginService.LATESTUSERNAME_KEY ) || ''
}
stacktrace 中的 FormControlName.ngOnChanges
让我认为 ng 是 运行 控件正确初始化之前的更改检测,但是由于没有订阅或类似的东西,我看不到什么可能会触发它。
我在 Selenium 测试期间观察到错误(在某些测试中可靠地存在,在其他测试中间歇性地存在),但是在手动单击时遇到过一两次错误。该效果仅在升级到 Angular 8.2.14(从 7)
后出现
查看 source code of FormControlName
and thinking about when ngOnChanges() can run,我估计 ngOnChanges()
在 ngOnInit()
完成表单构建之前 运行。
为了诊断这个问题,我会在 ngOnChanges()
和 ngOnInit()
上乱扔垃圾 console.log()
来追踪发生了什么。
要解决这个问题(如果这是原因),应该像使用 *ngIf
保护表单一样简单,以确保指令不会过早触发更改检测。理想情况下,您还可以使用 OnPush
更改检测策略。
试试这个:
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]
});
}
我有一个 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 中的一个 getter:
get latestUsername(): string {
return localStorage.getItem( LoginService.LATESTUSERNAME_KEY ) || ''
}
stacktrace 中的 FormControlName.ngOnChanges
让我认为 ng 是 运行 控件正确初始化之前的更改检测,但是由于没有订阅或类似的东西,我看不到什么可能会触发它。
我在 Selenium 测试期间观察到错误(在某些测试中可靠地存在,在其他测试中间歇性地存在),但是在手动单击时遇到过一两次错误。该效果仅在升级到 Angular 8.2.14(从 7)
查看 source code of FormControlName
and thinking about when ngOnChanges() can run,我估计 ngOnChanges()
在 ngOnInit()
完成表单构建之前 运行。
为了诊断这个问题,我会在 ngOnChanges()
和 ngOnInit()
上乱扔垃圾 console.log()
来追踪发生了什么。
要解决这个问题(如果这是原因),应该像使用 *ngIf
保护表单一样简单,以确保指令不会过早触发更改检测。理想情况下,您还可以使用 OnPush
更改检测策略。
试试这个:
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]
});
}