代码之家  ›  专栏  ›  技术社区  ›  Dolf Andringa

如何使用动态组件从角度5中的指令引用父组件

  •  1
  • Dolf Andringa  · 技术社区  · 6 年前

    我在跟踪 this tutorial 以角度创建动态形式。这个 full code for the tutorial is on github 一切都很好地工作,但是我想在父组件中添加一些内容,它使用 Directive 具有 ComponentFactoryResolver 要动态创建子组件,可以访问子组件。

    这是指令代码:

    import { ComponentFactoryResolver, ComponentRef, Directive, Input, OnChanges, OnInit, Type, ViewContainerRef } from '@angular/core';
    import { FormGroup } from '@angular/forms';
    
    import { FormButtonComponent } from '../form-button/form-button.component';
    import { FormInputComponent } from '../form-input/form-input.component';
    import { FormSelectComponent } from '../form-select/form-select.component';
    
    import { Field } from '../../models/field.interface';
    import { FieldConfig } from '../../models/field-config.interface';
    
    const components: {[type: string]: Type<Field>} = {
      button: FormButtonComponent,
      input: FormInputComponent,
      select: FormSelectComponent
    };
    
    @Directive({
      selector: '[dynamicField]'
    })
    export class DynamicFieldDirective implements Field, OnChanges, OnInit {
      @Input()
      config: FieldConfig;
    
      @Input()
      group: FormGroup;
    
      component: ComponentRef<Field>;
    
      constructor(
        private resolver: ComponentFactoryResolver,
        private container: ViewContainerRef
      ) {}
    
      ngOnChanges() {
        if (this.component) {
          this.component.instance.config = this.config;
          this.component.instance.group = this.group;
        }
      }
    
      ngOnInit() {
        if (!components[this.config.type]) {
          const supportedTypes = Object.keys(components).join(', ');
          throw new Error(
            `Trying to use an unsupported type (${this.config.type}).
            Supported types: ${supportedTypes}`
          );
        }
        const component = this.resolver.resolveComponentFactory<Field>(components[this.config.type]);
        this.component = this.container.createComponent(component);
        this.component.instance.config = this.config;
        this.component.instance.group = this.group;
        //let parentCmp = ......;
        //parentCmp.addField(this.component.instance);
      }
    }
    

    自从 指令 在创建子组件的地方,我假设这是我可以访问父组件和子组件的地方,并且可以将子组件传递给父组件。这就是我想对这两个评论 Ngoninit中的行:

    let parentCmp = ......;
    parentCmp.addField(this.component.instance);
    

    这是使用指令的组件。

    import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
    import { FormGroup, FormBuilder } from '@angular/forms';
    
    import { FieldConfig } from '../../models/field-config.interface';
    
    @Component({
      exportAs: 'dynamicForm',
      selector: 'dynamic-form',
      styleUrls: ['dynamic-form.component.scss'],
      template: `
        <form
          class="dynamic-form"
          [formGroup]="form"
          (submit)="handleSubmit($event)">
          <ng-container
            *ngFor="let field of config;"
            dynamicField
            [config]="field"
            [group]="form">
          </ng-container>
        </form>
      `
    })
    export class DynamicFormComponent implements OnChanges, OnInit {
      @Input()
      config: FieldConfig[] = [];
      fields: object;
    
      @Output()
      submit: EventEmitter<any> = new EventEmitter<any>();
    
      form: FormGroup;
    
      get controls() { return this.config.filter(({type}) => type !== 'button'); }
      get changes() { return this.form.valueChanges; }
      get valid() { return this.form.valid; }
      get value() { return this.form.value; }
    
      constructor(private fb: FormBuilder) {}
    
      ngOnInit() {
        this.form = this.createGroup();
      }
    
      ngOnChanges() {
        if (this.form) {
          const controls = Object.keys(this.form.controls);
          const configControls = this.controls.map((item) => item.name);
    
          controls
            .filter((control) => !configControls.includes(control))
            .forEach((control) => this.form.removeControl(control));
    
          configControls
            .filter((control) => !controls.includes(control))
            .forEach((name) => {
              const config = this.config.find((control) => control.name === name);
              this.form.addControl(name, this.createControl(config));
            });
    
        }
      }
    
      addField(field){
        this.fields[field.key] = field;
      }
    
      createGroup() {
        const group = this.fb.group({});
        this.controls.forEach(control => group.addControl(control.name, this.createControl(control)));
        return group;
      }
    
      createControl(config: FieldConfig) {
        const { disabled, validation, value } = config;
        return this.fb.control({ disabled, value }, validation);
      }
    
      handleSubmit(event: Event) {
        event.preventDefault();
        event.stopPropagation();
        this.submit.emit(this.value);
      }
    
      setDisabled(name: string, disable: boolean) {
        if (this.form.controls[name]) {
          const method = disable ? 'disable': 'enable';
          this.form.controls[name][method]();
          return;
        }
    
        this.config = this.config.map((item) => {
          if (item.name === name) {
            item.disabled = disable;
          }
          return item;
        });
      }
    
      setValue(name: string, value: any) {
        this.form.controls[name].setValue(value, {emitEvent: true});
      }
    }
    
    1 回复  |  直到 6 年前
        1
  •  0
  •   Dolf Andringa    6 年前

    解决方案是在指令中使用@output。使用指令中的输出,可以创建父组件可以订阅的偶数发射器。这样,指令就可以将数据传回父组件。在我的例子中,我在将子组件传递给它的指令中创建了一个输出,如下所示:

    import { ComponentFactoryResolver, EventEmitter, ComponentRef, Directive, Input, OnChanges, OnInit, Type, ViewContainerRef } from '@angular/core';
    
    @Directive({
      selector: '[dynamicField]'
    })
    export class DynamicFieldDirective implements Field, OnChanges, OnInit {
      @Input()
      config: FieldConfig;
    
      @Input()
      group: FormGroup;
    
      @Output fieldAdded: EventEmitter<any> = new EventEmitter<any>();
    
      component: ComponentRef<Field>;
    
      constructor(
        private resolver: ComponentFactoryResolver,
        private container: ViewContainerRef
      ) {}
    
      ngOnChanges() {
        if (this.component) {
          this.component.instance.config = this.config;
          this.component.instance.group = this.group;
        }
      }
    
      ngOnInit() {
        if (!components[this.config.type]) {
          const supportedTypes = Object.keys(components).join(', ');
          throw new Error(
            `Trying to use an unsupported type (${this.config.type}).
            Supported types: ${supportedTypes}`
          );
        }
        const component = this.resolver.resolveComponentFactory<Field>(components[this.config.type]);
        this.component = this.container.createComponent(component);
        this.component.instance.config = this.config;
        this.component.instance.group = this.group;
        this.fieldAdded.emit(this.component.instance);
      }
    }
    

    然后,在父组件中,我可以将函数绑定到该指令的输出事件,该指令随后处理传递给它的子组件,如下所示:

    @Component({
      exportAs: 'dynamicForm',
      selector: 'dynamic-form',
      styleUrls: ['dynamic-form.component.scss'],
      template: `
        <form
          class="dynamic-form"
          [formGroup]="form"
          (submit)="handleSubmit($event)">
          <ng-container
            *ngFor="let field of config;"
            dynamicField
            [config]="field"
            [group]="form"
            (fieldAdded) = "addField($event)"
            >
          </ng-container>
        </form>
      `
    })
    export class DynamicFormComponent implements OnChanges, OnInit {
      @Input()
      config: FieldConfig[] = [];
      fields: object = {};
    
      addField(field: any){
        this.fields[field.key] = field;
      }
    

    此时,父组件有一个this.fields属性,该属性包含所有子组件实例,可以对它们进行处理。