Как получить FormGroup из ng-content
Не могу реализовать следующую задачу: есть кастомная раскрывающаяся панель, в которую вкладывается различный контент (по типу mat-expansion-panel), контент проецируется через ng-content, необходимо реализовать визуализацию наличия не валидных контролов вложенных форм на закрытой панели. При этом нужно понимать, что степень вложенности форм может быть различной (компонент в компоненте), возможно наличие n-количества форм, динамическая генерация форм. @ContentChildren, как оказалось, здесь не поможет, так как различная глубина вложенности. Соответственно, nativeElement нельзя использовать.
Пример Есть компонент, в котором используется panel с вложенным panel-content, в котором может создаваться formGroup.
<panel>
<panel-header>
<div class="title">{{subgroup.name}}</div>
</panel-header>
<panel-content>
<simple-group-card [group]="subgroup"></simple-group-card>
</panel-content>
</panel>
В свою очередь в panel компонент проецируются через ng-content дочерние компоненты panel-header и panel-content.
<ng-content select="panel-header"></ng-content>
<div class="panel-content" *ngIf="promoted">
<ng-content select="panel-content"></ng-content>
</div>
В panel компоненте через декоратор @ContentChild получаю PanelContentComponent
@ContentChild(PanelContentComponent, {static: true})
private _content;
Получаю объект компонента (в консоли), в котором содержится FormGroup
PanelContentComponent {__ngContext__: LEmbeddedView_SimpleGroupCardComponent_ng_container_1_ng_container_1(75)}
__ngContext__: LEmbeddedView_SimpleGroupCardComponent_ng_container_1_ng_container_1(75)
0: simple-group-card
1: TView {type: 2, blueprint: LViewBlueprint(75), queries: TQueries_, viewQuery: null, template:
...
28: FormGroup {_hasOwnPendingAsyncValidator: false, _parent: null, pristine: true, touched: false, _onCollectionChange: ƒ, …}
Как получить ссылку на эту FormGroup? Данный подход не работает:
@ContentChild(PanelContentComponent, {static: true, read: FormGroup})
private _content;
Необходимо узнать, присутствует ли FormGroup в компоненте
Ответы (1 шт):
Опишу, как я решил данный вопрос. Добавил класс, реализующий хранение состояния. Также добавил токен для передачи инстанса класса дочерним компонентам:
export const CONTROLS_STATE_TOKEN = new InjectionToken<IControlsState>('CONTROLS_STATE_TOKEN');
export interface IControlsState {
isInvalid: boolean;
addControl(control: AbstractControl): void;
removeControl(control: AbstractControl): void;
}
export class ControlsState implements IControlsState {
private _controls: AbstractControl[] = [];
get isInvalid(): boolean {
return this._controls.some(control => control.invalid);
}
remove(control: AbstractControl): void {
const index = this._controls.indexOf(control);
if (index > -1) {
this._controls.splice(index, 1);
}
}
add(control: AbstractControl): void {
if (!this._controls.includes(control)) {
this._controls.push(control);
}
}
}
В родительском компоненте, в котором необходимо получить информацию о вложенных контролах, использую провайдер фабрику, создаю экземпляр класса:
providers: [{
provide: CONTROLS_STATE_TOKEN,
useFactory: (): ControlsState=> new ControlsState()
}]
и инжектирую токен в конструкторе
constructor(
@Inject(CONTROLS_STATE_TOKEN)
private _сontrolsState: ControlsState
) {}
получаю информацию об инвалидных контролах
public get isInvalid(): boolean {
return this._сontrolsState.isInvalid;
}
Во всех компонентах с контролами (у нас это библиотека кастомных ControlValueAccessor компонентов) также инжектирую токен в конструктор и добавляю контрол компонента в ControlsState:
constructor(
@Optional()
@Inject(CONTROLS_STATE_TOKEN)
private _сontrolsState: ControlsState,
private _control: NgControl
) {}
ngOnInit(): void {
this._сontrolsState?.add(this._control.control);
}
ngOnDestroy(): void {
this._сontrolsState?.remove(this._control.control);
}
Теперь родительский компонент получает состояние всех контролов из дочерних компонентов, не зависимо от глубины вложенности и наличия/отсутствия форм.
Данный подход очень удобен, подойдёт для реализации различной логики.