在Angular2中构建包装指令(包装一些内容/组件)

17

我在使用Angular2构建指令方面还比较新手。我想创建一个弹出指令,它将使用一些CSS类来包装内容。

内容

内容可以是纯文本和标题,例如:

<div class="data">
    <h2>Header</h2>
    Content to be placed here.
</div>

然后我想给它一个指令属性,比如:popup

<div class="data" popup>
    <h2>Header</h2>
    Content to be placed here.
</div>

该指令应该做的是将 div 包装在内,比如说:
<div class="some class">
    <div class="some other class">
        <div class="data">
            <h2>Header</h2>
            Content to be placed here.
        </div>
    </div>
</div>

我所描述的情况,是属性指令还是结构指令?

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({
  selector: `[popup]`
})

export class PopupDirective {


}
2个回答

24

另一个回答与此相关但不同。

如果要更接近此问题,请参见:如何在ng-content周围有条件地包装div - 我的解决方案适用于Angular 4,但链接的问题提供了一些关于如何在Angular 2中实现这一点的提示。

我通过组件和指令的组合来解决这个问题。我的组件看起来像这样:

import { Component, Input, TemplateRef } from '@angular/core';

@Component({
  selector: 'my-wrapper-container',
  template: `
<div class="whatever">
  <ng-container *ngTemplateOutlet="template"></ng-container>
</div>
`
})
export class WrapperContainerComponent {
  @Input() template: TemplateRef<any>;
}

我的指令像这样:

import { Directive, OnInit, Input, TemplateRef, ComponentRef, ComponentFactoryResolver, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[myWrapperDirective]'
})
export class WrapperDirective implements OnInit {

  private wrapperContainer: ComponentRef<WrapperContainerComponent>;

  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainerRef: ViewContainerRef,
    private componentFactoryResolver: ComponentFactoryResolver
  ) { }

  ngOnInit() {
    const containerFactory = this.componentFactoryResolver.resolveComponentFactory(WrapperContainerComponent);
    this.wrapperContainer = this.viewContainerRef.createComponent(containerFactory);
    this.wrapperContainer.instance.template = this.templateRef;
  }
}
为了能够动态加载你的组件,你需要将你的组件列为模块内的一个entryComponent
@NgModule({
  imports: [CommonModule],
  declarations: [WrapperContainerComponent, WrapperDirective],
  exports: [WrapperContainerComponent, WrapperDirective],
  entryComponents: [WrapperContainerComponent]
})
export class MyModule{}

所以最后的HTML代码是:

<some_tag *myWrapperDirective />

其呈现效果为:

<my-wrapper-container>
  <div class="whatever">
    <some_tag />
  </div>
</my-wrapper-container>

1
@Input设置方法中的控制从哪里来?此外,SubmissionGroup又是从哪里来的?我找不到任何关于它的文档。 - Finnur Eiríksson
1
哇,这是由于我从实际项目中复制粘贴的一些错误。等我有时间了,我会把它整理好。 - Dan Field
感谢 @n00dl3 对此进行的清理 - 这里有一些仓促复制和粘贴的工作,我只是一直在追踪这个问题,我现在没有太多时间在 Angular 4 上工作。 - Dan Field
1
快接近了... 当你使用 *myWrapperDirective 而不是 *myWraperDirective 时会有所帮助 :) 惊讶的是它没有报错,没有识别到那个名称。 - Tyler Rick
1
看起来当您以这种方式包装输入时,元素引用无法正常工作。例如,对于错误,这总是显示为空: `<input type="text" name="age" #ageRef="ngModel" [(ngModel)]="age" required *myWrapperDirective /><pre>{{ageRef?.errors | json}}</pre>` 如果您同时将<input>和使用元素引用的代码包装在同一个<div *myWrapperDirective>中,则可以正常工作...但是,如果您需要在父组件中始终使用元素引用,则无法正常工作。有什么办法可以将元素引用“导出”以供指令外部使用吗? - Tyler Rick
显示剩余4条评论

9
你可以通过使用组件属性选择器和 Angular 2 内容投影 <ng-content> 实现此目的。
@Component({
  selector: 'my-app',
  template: `
    <div class="app"> 
        <div class="data" myWrapper>
            <h2>Header</h2>
            Content to be placed here.
        </div> 
    </div>
  `
})
export class AppComponent {}


@Component({
  selector: '[myWrapper]',
  template: `
    <div class="my-class">
      <div class="my-sub-class">
          <ng-content></ng-content>
      </div>
    </div>
  `
})
export class MyComponent {

}

3
显然,这仅适用于可以接受内容的元素。对于输入元素而言,它是无效的。 - user776686
7
实际上,这似乎并没有做到 @Mikkel 所要求的。这将包裹 [myWrapper] 放置在其上的元素内部,而不是将该模板围绕着它所放置的信息进行包裹。 - Jessy
这个很好用。我在Angular 7中使用过。但是还有一件事,根据Angular风格指南,不建议将[compname]作为指令使用,而应该将其作为组件使用。 - Swapnil Mhaske
1
这不是被要求的内容 - 这个指令包装的是元素的内容,而不是具有指令的实际组件。在你的例子中,<div class="data" myWrapper> 应该被包装。 - Antoniossss

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接