Angular11 如何使用子组件从基础组件扩展表单

Angular 11 how to extend a Form from a base component using a child component

我的应用有很多费用。所有费用都具有相同的属性,除了少数几个。假设我有这个结构:

// Base Fee
interface IFee {
    id: string;
    name: string;
    price: string;
    date: string;
}

interface IWebFee extends IFee {
    url: string;
    links: number;
}

interface IBookFee extends IFee {
    pageCount: number;
    bookTitle: string;
}

假设我想创建一个表单来编辑 BookFee。内容投影不会工作,因为不会有任何上下文。所以我尝试创建一个嵌入式视图......但我仍然无法访问父 FormGroup 以将控件附加到它。这是我所拥有的(由于我无法从 BaseFeeFormComponent 访问 FormGroup,因此会因缺少控制而引发错误):

基本费用-form.component.ts

@Component({
    selector: 'app-base-fee-form',
    ...
    providers: [
    {
        provide: ControlContainer,
        useFactory: (comp: BaseFeeFormComponent) => comp.ngForm,
        deps: [BaseFeeFormComponent],
    },
  ],
})
export class BaseFeeFormComponent implements AfterContentInit {
    @ContentChild('feeChild') templateChild: TemplateRef<any>;
    @ViewChild('mountRef', { read: ViewContainerRef }) vcRef: ViewContainerRef;
    @ViewChild('ngForm') ngForm: FormGroupDirective;
    form: FormGroup;

    constructor(protected _fb: FormBuilder) {
        this.form = this._fb.group({
            name: [],
            price: [],
            date: [],
        });
    }

    ngAfterContentInit() {
        setTimeout(() => this.vc.createEmbeddedView(this.templateChild));
    }
}

基本费用-form.component.html

<form [formGroup]="form" #ngForm="ngForm">
    <div class="control-group">
        <span>Name: </span>
        <input type="text" formControlName="name" />
    </div>

    <div class="control-group">
        <span>Price: </span>
        <input type="text" formControlName="price" />
    </div>

    <div class="control-group">
        <span>Date: </span>
        <input type="date" formControlName="date" />
    </div>

    <div #mountRef></div>
</form>

书费-form.component.ts

@Component({
  selector: 'app-book-fee-form',
  templateUrl: './book-fee-form.component.html',
  styleUrls: ['./book-fee-form.component.css'],
  encapsulation: ViewEncapsulation.None,
})
export class BookFeeFormComponent {
  constructor(
      // private _formDirective: FormGroupDirective,
      private _fb: FormBuilder
    ) {
      // this._formDirective.form.addControl('pageCount', this._fb.control(0));
      // this._formDirective.form.addControl('bookTitle', this._fb.control(null));
  }

  ngOnInit() {}
}

书费-form.component.html

<app-base-fee-form>
  <ng-template #feeChild>
    <div class="control-group">
      <span>Page Count: </span>
      <input type="text" formControlName="pageCount" />
    </div>

    <div class="control-group">
      <span>Book Title: </span>
      <input type="text" formControlName="bookTitle" />
    </div>
  </ng-template>
</app-base-fee-form>

如何访问父 NgForm 以将所需的控件附加到现有的 FormGroup?相反,有没有更简单的方法来做到这一点?我试图避免为共享几乎相同模板和功能的每个表单创建组件。

我创建了一个 Stackblitz 来展示我的问题:StackBlitz

一种解决方案是将控件表示为对象,并动态生成它们

export type control = {
  label: string;
  type: string;
  formControlName: string;
};

base-fee-form.component.html

<form [formGroup]="form" #ngForm="ngForm">
  <ng-container *ngFor="let control of controls">
    <div class="control-group">
      <span>{{ control.label }}: </span>
      <input
        type="{{ control.type }}"
        formControlName="{{ control.formControlName }}"
      />
    </div>
  </ng-container>
</form>

在基本组件中定义常用控件

export class BaseFeeFormComponent {
  controls: control[] = [
    { label: 'Name', type: 'text', formControlName: 'name' },
    { label: 'Price', type: 'text', formControlName: 'price' },
    { label: 'Date', type: 'date', formControlName: 'date' },
  ];
  form: FormGroup;

  constructor(protected _fb: FormBuilder) {
    this.form = this._fb.group({
      name: [],
      price: [],
      date: [null],
    });
  }
}

然后extend添加新控件的基础组件

export class BookFeeFormComponent extends BaseFeeFormComponent {
  constructor(protected _fb: FormBuilder) {
    super(_fb);
    this.controls = this.controls.concat([
      { label: 'Page Count', type: 'text', formControlName: 'pageCount' },
      { label: 'Book Title', type: 'text', formControlName: 'bookTitle' },
    ]);
    this.form = this._fb.group({
      ...this.form.controls,
      pageCount: [],
      bookTitle: [],
    });
  }
}

您可以为每个组件自定义 html,或者将子组件指向基础 html

@Component({
  selector: 'app-book-fee-form',
  templateUrl: '../base-fee-form/base-fee-form.component.html',
...

Stackblitz:https://stackblitz.com/edit/angular-ivy-rwm5jw?file=src/app/book-fee-form/book-fee-form.component.ts