【问题标题】:Accessing arbitrary child in angular2在angular2中访问任意孩子
【发布时间】:2016-08-21 06:30:37
【问题描述】:

我想创建一个“my-form-group”组件,由一个标签、任何类型的输入元素(输入、复选框、...)和一个用于验证结果的 div 组成。 我想使用内容投影在标签之后插入输入。像这样的:

<form [ngFormModel]="myForm" (ngSubmit)="onSubmit()">    
  <my-form-group>
    <input type="text" 
           class="form-control"
           [ngFormControl]="myForm.controls['name']">
  </my-form-group>
<form>

组件可能如下所示:

@Component({
  selector: 'my-form-group',
  template: `
    <div class="form-group">
      <label for="name">Name<span [ngIf]="name.required"> *</span></label>
      <ng-content></ng-content>
      <div [hidden]="name.valid || name.pristine" class="alert alert-danger">
        Please check your input
      </div>
    </div>`
})
...

我想使用投影组件的状态来隐藏或显示“必需”星号和验证 div。据我所知,可以使用@ContentChild()ngAfterContentInit() 访问投影组件,但我认为,我必须有一个特殊的组件才能使用它。

如果我不知道确切的组件,访问投影组件的控制器的最佳方法是什么?

【问题讨论】:

    标签: angular angular2-template angular2-directives


    【解决方案1】:

    像这样通过你的控制

    @Component({
        selector: 'parent',
        directives: [MyFormGroup],
        template: `
                   <form [ngFormModel]="myForm" (ngSubmit)="onSubmit()">    
                       <my-form-group [control]="myForm.controls['name']" controlId="name">
                         <input type="text" 
                          class="form-control" ngControl="name">
                       </my-form-group>
                   <form>
    
                `
    })
    export class Parent {
        @ContentChildren(MyFormGroup) children: QueryList<MyFormGroup>;
        ngAfterContentInit() {
            console.log(this.children);
        }
    }
    

    在你的组件中

    @Component({
      selector: 'my-form-group',
      template: `
    <div class="form-group">
      <label for="{{controlId}}"> {{controlId}} <span [ngIf]="control.hasError('required')"> *</span></label>
      <ng-content></ng-content>
      <div [hidden]="control.valid || control.pristine" class="alert alert-danger">
        Please check your input
      </div>
    </div>`
    })
    export class MyFormGroup {
          @Input control: Control;
          @Input controlId: string;
    }
    

    现在您只需更改您使用的每个输入字段的输入,如果需要控制,它将显示 * 星号。 (希望这是你想要的)

    **代码未编译

    【讨论】:

    • 你会用什么来查询@ContentChildren()? AFAIK 只有模板变量或组件/指令类型可以用作“选择器”。
    • 您需要注入ElementRef,然后您可以使用this.elementRef.nativeElement.querySelectorAll(...) 才能使用CSS 选择器。
    • 但这不会给ComponentRef 对吗?所以使用ElementRef没有好处
    • 他是否事先知道组件类型?我以为他会这样做并想区分
    • 抱歉,我没有注意到您的更改:将控件本身作为输入注入的想法对我来说很好。这很管用,太好了 - 谢谢。
    【解决方案2】:

    您可以在需要传递模板变量的名称或组件或指令的类型的地方使用@ContentChild()
    模板变量的缺点是,您的组件的用户需要使用正确的名称来应用它,这使得您的组件更难使用。
    对于父组件(使用&lt;my-form-group&gt;)需要添加providers: [MyProjectionDirective]的组件或指令类似,同样繁琐。

    第二种方法允许解决方法

    provide(PLATFORM_DIRECTIVES, {useValue: [MyProjectionDirective], multi: true})
    

    MyProjectionDirective 有一个与投影内容匹配的选择器(如selector: 'input')时,该指令将应用于每个输入元素,您可以对其进行查询。不利的一面是,该指令随后也会应用于 Angular 应用程序中的任何其他输入元素。 您也不会得到投影组件的组件实例,而是指令。您可以告诉@ContentChild() 获取组件实例,但为此您需要提前知道类型。

    因此,使用模板变量的方法似乎是最好的选择:

    使用模板变量的示例

    @Component({
        selector: 'my-input',
        template: `
        <h1>input</h1>
        <input>
        `,
    })
    export class InputComponent {
    }
    
    @Component({
        selector: 'parent-comp',
        template: `
        <h1>Parent</h1>
        <ng-content></ng-content>
        `,
    })
    export class ParentComponent {
      @ContentChild('input') input;
    
      ngAfterViewInit() {
        console.log(this.input);
        // prints `InputComponent {}`
      }
    }
    
    
    @Component({
        selector: 'my-app',
        directives: [ParentComponent, InputComponent],
        template: `
        <h1>Hello</h1>
        <parent-comp><my-input #input></my-input></parent-comp>
        `,
    })
    export class AppComponent {
    }
    

    Plunker example

    【讨论】:

    • 如果使用 selector: 'input.someclass' 会怎样?是否允许
    • 查看 cmets 以了解您的答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-10
    • 2016-11-16
    • 1970-01-01
    • 2020-08-12
    相关资源
    最近更新 更多