Angular V11: NullInjectorError: No provider for ControlContainer
Angular V11: NullInjectorError: No provider for ControlContainer
我根据这篇文章进行了自定义输入:medium.com: dont-reinvent-the-wheel。
这是我的代码,是严格模式▼
// input.component.ts
import { Component, Input, ViewChild } from '@angular/core';
import {
ControlContainer,
ControlValueAccessor,
FormControl,
FormControlDirective,
NG_VALUE_ACCESSOR
} from '@angular/forms';
import {
FloatLabelType,
MatFormFieldAppearance
} from '@angular/material/form-field';
@Component({
selector: 'app-input',
templateUrl: './input.component.html',
styleUrls: ['./input.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: InputComponent,
multi: true
}
]
})
export class InputComponent implements ControlValueAccessor {
isDisabled!: boolean;
@Input() isRequired!: boolean;
@Input() label!: string;
@Input() placeholder!: string;
@Input() readonly!: boolean;
@Input() appearance: MatFormFieldAppearance = 'fill';
@Input() floatLabel: FloatLabelType = 'auto';
@ViewChild(FormControlDirective, { static: true })
formControlDirective!: FormControlDirective;
@Input() formControl!: FormControl;
@Input() formControlName!: string;
get control(): FormControl {
return (
this.formControl ||
this.controlContainer.control?.get(this.formControlName)
);
}
constructor(private controlContainer: ControlContainer) {}
clearInput(): void {
this.control.setValue('');
}
registerOnTouched(fn: any): void {
this.formControlDirective.valueAccessor?.registerOnTouched(fn);
}
registerOnChange(fn: any): void {
this.formControlDirective.valueAccessor?.registerOnChange(fn);
}
writeValue(obj: any): void {
this.formControlDirective.valueAccessor?.writeValue(obj);
}
setDisabledState(disabled: boolean): void {
this.isDisabled = disabled;
}
}
<!-- input.component.html -->
<div class="custom-input">
<mat-form-field
[appearance]="appearance"
[floatLabel]="floatLabel"
class="custom-input__form-field"
>
<mat-label class="custom-input__label"
>{{ label }} <span *ngIf="isRequired && label">*</span></mat-label
>
<input
class="custom-input__value"
matInput
type="text"
[formControl]="$any(control)"
[placeholder]="placeholder"
[readonly]="readonly"
/>
<button
class="custom-input__clear-btn"
matSuffix
mat-icon-button
aria-label="Clear"
*ngIf="$any(control).value && !readonly"
(click)="clearInput()"
>
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
</div>
没有编译错误但是浏览器记录了这个错误▼
ERROR Error: Uncaught (in promise): NullInjectorError: R3InjectorError(ClientsListModule)[ControlContainer ->
ControlContainer -> ControlContainer -> ControlContainer -> ControlContainer -> ControlContainer]:
NullInjectorError: No provider for ControlContainer!
NullInjectorError: R3InjectorError(ClientsListModule)[ControlContainer -> ControlContainer -> ControlContainer ->
ControlContainer -> ControlContainer -> ControlContainer]:
NullInjectorError: No provider for ControlContainer!
我在这里检查了类似的问题 and here No provider for ControlContainer,根据他们的说法,问题在于没有同时导入 ReactiveFormsModule 和 FormsModule在各自的模块中,但我正在导入它们,但我仍然看到该错误。
更新 1:
在组件中<app-input>
的使用方式如下▼
因为在某些情况下您似乎使用了 formControl
而没有 formGroup
,所以在这些情况下使用 @Optional
装饰器使 controlContainer
为 null。
constructor(@Optional() private controlContainer: ControlContainer) {}
我根据这篇文章进行了自定义输入:medium.com: dont-reinvent-the-wheel。
这是我的代码,是严格模式▼
// input.component.ts
import { Component, Input, ViewChild } from '@angular/core';
import {
ControlContainer,
ControlValueAccessor,
FormControl,
FormControlDirective,
NG_VALUE_ACCESSOR
} from '@angular/forms';
import {
FloatLabelType,
MatFormFieldAppearance
} from '@angular/material/form-field';
@Component({
selector: 'app-input',
templateUrl: './input.component.html',
styleUrls: ['./input.component.scss'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
useExisting: InputComponent,
multi: true
}
]
})
export class InputComponent implements ControlValueAccessor {
isDisabled!: boolean;
@Input() isRequired!: boolean;
@Input() label!: string;
@Input() placeholder!: string;
@Input() readonly!: boolean;
@Input() appearance: MatFormFieldAppearance = 'fill';
@Input() floatLabel: FloatLabelType = 'auto';
@ViewChild(FormControlDirective, { static: true })
formControlDirective!: FormControlDirective;
@Input() formControl!: FormControl;
@Input() formControlName!: string;
get control(): FormControl {
return (
this.formControl ||
this.controlContainer.control?.get(this.formControlName)
);
}
constructor(private controlContainer: ControlContainer) {}
clearInput(): void {
this.control.setValue('');
}
registerOnTouched(fn: any): void {
this.formControlDirective.valueAccessor?.registerOnTouched(fn);
}
registerOnChange(fn: any): void {
this.formControlDirective.valueAccessor?.registerOnChange(fn);
}
writeValue(obj: any): void {
this.formControlDirective.valueAccessor?.writeValue(obj);
}
setDisabledState(disabled: boolean): void {
this.isDisabled = disabled;
}
}
<!-- input.component.html -->
<div class="custom-input">
<mat-form-field
[appearance]="appearance"
[floatLabel]="floatLabel"
class="custom-input__form-field"
>
<mat-label class="custom-input__label"
>{{ label }} <span *ngIf="isRequired && label">*</span></mat-label
>
<input
class="custom-input__value"
matInput
type="text"
[formControl]="$any(control)"
[placeholder]="placeholder"
[readonly]="readonly"
/>
<button
class="custom-input__clear-btn"
matSuffix
mat-icon-button
aria-label="Clear"
*ngIf="$any(control).value && !readonly"
(click)="clearInput()"
>
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
</div>
没有编译错误但是浏览器记录了这个错误▼
ERROR Error: Uncaught (in promise): NullInjectorError: R3InjectorError(ClientsListModule)[ControlContainer ->
ControlContainer -> ControlContainer -> ControlContainer -> ControlContainer -> ControlContainer]:
NullInjectorError: No provider for ControlContainer!
NullInjectorError: R3InjectorError(ClientsListModule)[ControlContainer -> ControlContainer -> ControlContainer ->
ControlContainer -> ControlContainer -> ControlContainer]:
NullInjectorError: No provider for ControlContainer!
我在这里检查了类似的问题
更新 1:
在组件中<app-input>
的使用方式如下▼
因为在某些情况下您似乎使用了 formControl
而没有 formGroup
,所以在这些情况下使用 @Optional
装饰器使 controlContainer
为 null。
constructor(@Optional() private controlContainer: ControlContainer) {}