我已经创建了一个组件,该组件使用Angular material 表示 Select 框.此组件接受FormGroup作为输入,并在放置在另一个组件中时正常工作.我需要启用此组件的添加和删除使用按钮.我已经设法使用ViewContainerRef.createComponent使其工作,并可以传递动态填充SELECT内容所需的参数.但是,我遇到错误‘Error Error:找不到名为’Control2‘的控件.’

Parent component

import { STEPPER_GLOBAL_OPTIONS } from "@angular/cdk/stepper";
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
import { FormBuilder, FormControl, FormGroup } from "@angular/forms";

@Component({
  selector: "gcr-create",
  template: `<gcr-select-form
                [formGroup]="form"
                formControlName="control1"
                otherParam="OK"
              ></gcr-select-form>
            </div>
            <ng-template #containerMoreSelect></ng-template>
              <button mat-stroked-button (click)="click()">Add More</button>`,
  styles: [``],
  providers: [
    {
      provide: STEPPER_GLOBAL_OPTIONS,
      useValue: { showError: true },
    },
  ],
})
export class ParentComponent implements OnInit {
  @ViewChild("containerMoreSelect", {
    read: ViewContainerRef,
  })
  containerMoreSelect!: ViewContainerRef;
  
  public form!: FormGroup;

  constructor() {}
  ngOnInit(): void {
    this.formInit();
  }

  formInit() {
    this.form = new FormGroup({
      control1: new FormControl(),
    });
  }

  click() {
    this.form.addControl("control2", new FormControl());
    const component = this.containerMoreSelect.createComponent(SelectReteComponent);
    component.setInput("formGroup", this.form);
    component.setInput("formControlName", "control2");
    component.setInput("otherParam", "OK");
  }
}

Child component

import {
  Component,
  EventEmitter,
  Input,
  OnChanges,
  Output,
  SimpleChanges,
} from "@angular/core";
import {
  AbstractControl,
  ControlContainer,
  FormGroup,
  FormGroupDirective,
} from "@angular/forms";
import { MatSelectChange } from "@angular/material/select";
import { ValueText } from "../models/value-text.model";

@Component({
  selector: "gcr-select-form",
  template: `<mat-form-field>
    <mat-label>Select what you prefer</mat-label>
    <mat-select
      [formControlName]="formControlName"
      (selectionChange)="getSelection($event)">
      <mat-option></mat-option>
      <mat-option *ngFor="let value of values" [value]="value.id">
        {{ value.text}}
      </mat-option>
    </mat-select>
  </mat-form-field>`,
  styles: ``,
  viewProviders: [
    {
      provide: ControlContainer,
      useExisting: FormGroupDirective,
    },
  ],
})
export class SelectReteComponent implements OnChanges {
  @Input() formGroup!: FormGroup;
  @Input() formControlName!: string;
  @Input() otherParam!: string;
  @Output() selected= new EventEmitter<ValueText>();
  constructor(private ValuesService: ValuesRepositoryService) {}

  /* Data are loaded from DB */

  ngOnChanges(changes: SimpleChanges) {
    /* Other stuff */
    console.log("formGroup" + this.formGroup.controls); // I can see all control, control2 included
    console.log("formControlName " + this.formControlName); // It shows control2 correctly
    console.log("otherParam" + this.otherParam); // It shows "OK"
  }

  getSelection(event: MatSelectChange) {
    console.log("event.value " + event.value);
    this.selected.emit({
      value: event.value,
      text: event.source.triggerValue,
    });
  }
}

先谢谢你, 文森佐

我试图通过传递FormGroup来加载子组件,我希望FormControl能够被识别.

推荐答案

请避免使用@Input@Output的指令,您正在使用formControlName作为输入,这会导致错误.

对于这个场景,最好是*ngFor,而不是视图容器ref,因为它更简单,但我提供了这两种方法供您查看.

使用ngFor

    <div formArrayName="selectArr">
      <gcr-select-form *ngFor="let control of selectArrControls" [control]="control"
      ></gcr-select-form>
    </div>

对于这个特定的场景,如果我们将控件作为输入传递,我们将能够正确地绑定SELECT.同样,你最好 Select formArray个而不是独一无二的控件,因为它更容易维护!

我们还需要做useExisting: forwardRef(() => FormGroupDirective),来防止Angular 抛出那个错误,当我们使用forwardRef时,它指的是尚未定义的引用.

请在下面找到工作示例!

双手

import { bootstrapApplication } from '@angular/platform-browser';
import 'zone.js';
import { STEPPER_GLOBAL_OPTIONS } from '@angular/cdk/stepper';
import { Component, OnInit, ViewChild, ViewContainerRef } from '@angular/core';
import {
  FormArray,
  FormBuilder,
  FormControl,
  FormGroup,
  ReactiveFormsModule,
} from '@angular/forms';
import { SelectReteComponent } from './ children / children .component';
import { provideAnimations } from '@angular/platform-browser/animations';
import { CommonModule } from '@angular/common';

@Component({
  selector: 'app-root',
  standalone: true,
  template: `
  <form [formGroup]="form">
    <div formArrayName="selectArr">
              <gcr-select-form [control]="selectArrControls[0]"
              ></gcr-select-form>
            <ng-template #containerMoreSelect></ng-template>
            </div>
</form>
              <button mat-stroked-button (click)="click()">Add More</button>`,
  styles: [``],
  imports: [SelectReteComponent, ReactiveFormsModule, CommonModule],
  providers: [
    {
      provide: STEPPER_GLOBAL_OPTIONS,
      useValue: { showError: true },
    },
  ],
})
export class App {
  @ViewChild('containerMoreSelect', {
    read: ViewContainerRef,
  })
  containerMoreSelect!: ViewContainerRef;

  public form: FormGroup = new FormGroup({
    selectArr: new FormArray([new FormControl()]),
  });

  constructor() {}
  ngOnInit(): void {
    this.formInit();
  }

  formInit() {}

  get selectArr(): FormArray {
    return this.form.get('selectArr') as FormArray;
  }

  get selectArrControls(): FormControl[] {
    return this.selectArr?.controls as FormControl[];
  }

  click() {
    const array = this.selectArr;
    array.push(new FormControl());
    const component =
      this.containerMoreSelect.createComponent(SelectReteComponent);
    component.setInput(
      'control',
      this.selectArrControls[this.selectArrControls.length - 1]
    );
    component.setInput('otherParam', 'OK');
  }
}

bootstrapApplication(App, {
  providers: [provideAnimations()],
});

children

import { CommonModule } from '@angular/common';
import {
  Component,
  EventEmitter,
  forwardRef,
  Input,
  OnChanges,
  Output,
  SimpleChanges,
} from '@angular/core';
import {
  AbstractControl,
  ControlContainer,
  FormArray,
  FormControl,
  FormGroup,
  FormGroupDirective,
  ReactiveFormsModule,
} from '@angular/forms';
import { MatSelectChange, MatSelectModule } from '@angular/material/select';

@Component({
  selector: 'gcr-select-form',
  imports: [MatSelectModule, ReactiveFormsModule, CommonModule],
  standalone: true,
  template: `
  <mat-form-field>
    <mat-label>Select what you prefer</mat-label>
    <mat-select
      [formControl]="control"
      (selectionChange)="getSelection($event)">
      <mat-option></mat-option>
      <mat-option *ngFor="let value of values" [value]="value.id">
        {{ value.text}}
      </mat-option>
    </mat-select>
  </mat-form-field>`,
  styles: ``,
  viewProviders: [
    {
      provide: ControlContainer,
      useExisting: forwardRef(() => FormGroupDirective),
    },
  ],
})
export class SelectReteComponent implements OnChanges {
  @Input() control!: FormControl;
  @Input() formControlName!: number;
  @Input() otherParam!: string;
  @Output() selected = new EventEmitter<any>();
  values = [
    { id: 1, text: 'test' },
    { id: 2, text: 'test2' },
    { id: 3, text: 'test3' },
  ];
  form!: FormGroup;
  constructor(private controlContainer: ControlContainer) {}

  /* Data are loaded from DB */

  ngOnChanges(changes: SimpleChanges) {
    /* Other stuff */
    // console.log('formGroup' + this.formGroup.controls); // I can see all control, control2 included
    // console.log('formControlName ' + this.formControlName); // It shows control2 correctly
    // console.log('otherParam' + this.otherParam); // It shows "OK"
  }

  getSelection(event: MatSelectChange) {
    console.log('event.value ' + event.value);
    this.selected.emit({
      value: event.value,
      text: event.source.triggerValue,
    });
  }
}

stackblitz

Angular相关问答推荐

Angular独立组件(使用LoadComponents)-懒惰加载服务不工作

Primeng 12Angular 12自定义库:错误符号字段集声明于...在编译保存后未从Primeng/fieldset中导出

从、管道和映射可观察到的逻辑?

确保我的角库S服务构建提供独立的友好提供功能

如何使用formBuilder.Group()从角形表单中删除选定的选项?

如何使用独立组件在ANGLE(16+)中实现嵌套布线

将 autoSpy 与 ng-mocks 一起使用时,如何重置 jasmine 间谍调用?

Angular14:支持旧版浏览器的官方方式是什么?

在父组件的子组件中使用 ng-template

如何在本地处理错误并跳过 Angular HTTP 拦截器?

Angular 从 13.3.8 恢复到 13.3.7

如何在 cypress 测试中实现拖放?

angular material日期 Select 器中的日期不正确

如何在Angular2中基于特定的构建环境注入不同的服务?

Angular2 SVG xlink:href

ngx-toastr,Toast 未在 Angular 7 中显示

如何从 EventEmitter 函数返回值?

Angular:找不到不同的支持对象[object Object]

Angular2中是否有像window.onbeforeunload这样的生命周期钩子?

如何在 Angular 2 中动态添加和删除表单字段