Итак, у меня проблема в том, что у меня есть базовый компонент, имеющий форму. Но я хотел бы иметь возможность добавить к нему еще одно поле. Почему? Потому что у меня есть четыре страницы с очень похожей структурой, которые отличаются максимум одним полем. У меня есть stackblitz, который тоже должен донести идею. Часть рендеринга работает хорошо и хорошо.
Но чтобы дать общее представление:
@Component({
selector: 'app-base-form',
standalone: true,
imports: [ReactiveFormsModule, CommonModule],
template: `<form
style = "display: flex; flex-direction: column; gap: 1rem"
[formGroup] = "form"
>
<input formControlName = "search" type = "text" placeholder = "Search something" />
<ng-template
[ngTemplateOutletContext] = "{ $implicit: form }"
[ngTemplateOutlet] = "seachOptionalField()!"
></ng-template>
</form>`,
})
export class BaseFormComponent {
seachOptionalField = input<TemplateRef<any>>();
fb = inject(UntypedFormBuilder);
form = this.fb.group({
search: this.fb.control(['']),
});
}
Первоначально идея заключалась в том, чтобы передать форму через контекст выхода, но программное добавление элемента управления на самом деле не повлияло на исходную форму. Также возник вопрос о том, как применить отображаемый (который будет использоваться) элемент управления формой.
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, BaseFormComponent],
template: `
<app-base-form [seachOptionalField] = "searchOptionalField"></app-base-form>
<ng-template #searchOptionalField let-form>
<input type = "text" placeholder = "We just added this" />
{{form.value | json}}
</ng-template>
`,
})
export class App {}
Вам необходимо указать соответствующий formControlName
для новых элементов управления, а также обернуть их в элемент формы внутри шаблона.
Вы также можете добавить дополнительный параметр, который определяет новую конфигурацию элементов управления формы для объекта, который мы можем деструктурировать в нашу группу форм при инициализации.
Рабочий пример ниже со stackblitz!
Полный код:
main.ts
import { Component, inject } from '@angular/core';
import { bootstrapApplication } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import 'zone.js';
import { BaseFormComponent } from './app/base-form/base-form.component';
import { ReactiveFormsModule, UntypedFormBuilder } from '@angular/forms';
@Component({
selector: 'app-root',
standalone: true,
imports: [CommonModule, BaseFormComponent, ReactiveFormsModule],
template: `
<app-base-form [seachOptionalField] = "searchOptionalField" [extraFormParams] = "extraFormParams"></app-base-form>
<ng-template #searchOptionalField let-form>
<form [formGroup] = "form">
<input type = "text" placeholder = "We just added this" formControlName = "search2"/>
</form>
{{form.value | json}}
</ng-template>
`,
})
export class App {
fb = inject(UntypedFormBuilder);
extraFormParams = {
search2: this.fb.control(['']),
};
}
bootstrapApplication(App);
базовая форма.ts
import { CommonModule } from '@angular/common';
import { Component, TemplateRef, inject, input } from '@angular/core';
import {
FormGroup,
ReactiveFormsModule,
UntypedFormBuilder,
} from '@angular/forms';
@Component({
selector: 'app-base-form',
standalone: true,
imports: [ReactiveFormsModule, CommonModule],
template: `<form
style = "display: flex; flex-direction: column; gap: 1rem"
[formGroup] = "form"
>
<input formControlName = "search" type = "text" placeholder = "Search something" />
<ng-template
[ngTemplateOutletContext] = "{ $implicit: form }"
[ngTemplateOutlet] = "seachOptionalField()!"
></ng-template>
</form>`,
})
export class BaseFormComponent {
seachOptionalField = input<TemplateRef<any>>();
extraFormParams = input<Object>({});
fb = inject(UntypedFormBuilder);
form!: FormGroup;
ngOnInit() {
this.form = this.fb.group({
search: this.fb.control(['']),
...this.extraFormParams(),
});
}
}
Проклятие! У меня была идея использовать там тег формы, но идея предоставить элементы управления в качестве входных данных просто никогда не приходила мне в голову! Иногда простые решения оказываются лучшими! Большое спасибо!