【问题标题】:Angular 4 dynamically loaded components using attribute directive are wrapped with divAngular 4 使用属性指令动态加载的组件用 div 包装
【发布时间】:2017-05-04 15:40:27
【问题描述】:

我正在尝试根据配置选项动态创建一些组件。更具体地说,我想要一些工具栏,它可以包含不同的“工具栏项目”。

根据角度指南:https://angular.io/docs/ts/latest/cookbook/dynamic-component-loader.html 我已经创建了专门的指令

@Directive({
    selector: '[mte-toolbar-item-host]'
})
export class ToolbarItemDirective {

    constructor(public viewContainerRef: ViewContainerRef) {
    }
}

这将用于指示组件的“注入”点。

我有主ToolbarComponent

@Component({
    selector: "mte-toolbar",
    templateUrl: "./toolbar.component.html",
    styleUrls: ["./toolbar.component.css"],
    providers: [ToolbarService]
})
export class ToolbarComponent implements AfterViewInit {
    toolbar: Toolbar;

    @ViewChild(ToolbarItemDirective)
    toolbarItemHost: ToolbarItemDirective;

    constructor(private toolbarService: ToolbarService, private componentFactoryResolver: ComponentFactoryResolver) {
    }

    ngAfterViewInit(): void {
        this.toolbar = new Toolbar(this.toolbarService.getToolbarItems());
        for (let i in this.toolbar.items) {
            let toolbarItem = this.toolbar.items[i];
            let toolbarItemFactory = this.componentFactoryResolver.resolveComponentFactory(toolbarItem.componentType);
            let componentRef = this.toolbarItemHost.viewContainerRef.createComponent(toolbarItemFactory);
            componentRef.instance.toolbarItem = toolbarItem;
        }
    }
}

使用适当的模板

<ul class="nav nav-pills">
    <li role="presentation" mte-toolbar-item-host></li>
</ul>

目前,toolbarService 正在返回以下组件列表:

export const DEFAULT_TOOLBAR_ITEMS: ToolbarItem[] = [
    new GlyphToolbarItem(GlyphToolbarItemComponent, "example-component-id-1", null, "glyphicon-log-in"),
    new GlyphToolbarItem(GlyphToolbarItemComponent, "example-component-id-2", null, "glyphicon-floppy-remove"),
    new GlyphToolbarItem(GlyphToolbarItemComponent, "example-component-id-3", null, "glyphicon-log-in"),
    new GlyphToolbarItem(GlyphToolbarItemComponent, "example-component-id-4", null, "glyphicon-paste")
];

@Injectable()
export class ToolbarService {

    getToolbarItems(): ToolbarItem[] {
        return DEFAULT_TOOLBAR_ITEMS;
    }

}

GlyphToolbarItem:

export class GlyphToolbarItem extends ToolbarItem {
    glyphIcon: string;

    constructor(componentType: Type<any>, id: string, command: AbstractCommand, glyphIcon: string) {
        super(componentType, id, command);
        this.glyphIcon = glyphIcon;
    }
}

GlyphToolbarItemComponent:

@Component({
    selector: '[mte-glyph-toolbar-item]',
    templateUrl: './glyph-toolbar-item.component.html'
})
export class GlyphToolbarItemComponent {
     toolbarItem: ToolbarItem;
}

它的模板

<a class="btn btn-default btn-sm glyphicon {{toolbarItem.glyphIcon}}"></a>

我期待收到类似的东西:

<ul _ngcontent-c2="" class="nav nav-pills"><!---->
    <li role="presentation" mte-glyph-toolbar-item="" ng-version="4.0.3">
        <a class="btn btn-default btn-sm glyphicon glyphicon-log-in"></a>
    </li>
    <li role="presentation" mte-glyph-toolbar-item="" ng-version="4.0.3">
        <a class="btn btn-default btn-sm glyphicon glyphicon-floppy-remove"></a>
    </li>
    <li role="presentation" mte-glyph-toolbar-item="" ng-version="4.0.3">
        <a class="btn btn-default btn-sm glyphicon glyphicon-log-in"></a>
    </li>
    <li role="presentation" mte-glyph-toolbar-item="" ng-version="4.0.3">
        <a class="btn btn-default btn-sm glyphicon glyphicon-paste"></a>
    </li>
</ul>

但相反,我收到了用 &lt;div&gt; 包装的动态组件:

<ul _ngcontent-c2="" class="nav nav-pills"><!---->
<div mte-glyph-toolbar-item="" ng-version="4.0.3">
    <a class="btn btn-default btn-sm glyphicon glyphicon-log-in"></a>
</div>
<div mte-glyph-toolbar-item="" ng-version="4.0.3">
    <a class="btn btn-default btn-sm glyphicon glyphicon-floppy-remove"></a>
</div>
<div mte-glyph-toolbar-item="" ng-version="4.0.3">
    <a class="btn btn-default btn-sm glyphicon glyphicon-log-in"></a>
</div>
<div mte-glyph-toolbar-item="" ng-version="4.0.3">
    <a class="btn btn-default btn-sm glyphicon glyphicon-paste"></a>
</div>

为什么我收到的是用&lt;div&gt; 而不是&lt;li&gt; 元素(似乎完全被忽略了)包裹的动态组件?这是实现这一目标的正确方法吗?

【问题讨论】:

    标签: angular typescript


    【解决方案1】:

    我认为你必须要 GlyphToolbarItemComponent:

    @Component({
        selector: 'li[mte-glyph-toolbar-item]',
        template: '<a class="btn btn-default btn-sm glyphicon {{toolbarItem.glyphIcon}}"></a>'
    })
    export class GlyphToolbarItemComponent {
      @HostBinding('attr.role') role ="presentation";
      toolbarItem: ToolbarItem;
    }
    

    并删除空 li + 将 mte-toolbar-item-host 移动到 ul>ng-container

    <ul class="nav nav-pills">
      <ng-container mte-toolbar-item-host></ng-container>
    </ul>
    

    另外我认为 Angular 不喜欢你在 ngAfterViewInit 中创建这些组件

    我创建了https://plnkr.co/edit/8cjlzooO1Oh2CWydxbSN?p=info

    【讨论】:

    • 完成了这项工作。你是对的,我没有发现 Angular 早些时候在 ngAfterViewInit 上抱怨 - 更改为 ngOnInit 并且一切正常。将 mte-toolbar-item-host 移动到 ul 将不起作用,因为动态组件将作为同级而不是作为子级加载。
    • 请检查plnkr.co/edit/8cjlzooO1Oh2CWydxbSN?p=info - 我搬到了 ul 并且它可以工作
    • 我可以看到li 元素放在ul 旁边,而不是在其中
    • AAAA
    • ...
  • 你是对的。我改为
  • 【解决方案2】:

    从哪个元素获取ViewContainerRef 元素并不重要。 createComponent 将添加一个兄弟并使用动态创建的组件的选择器作为元素。因为选择器中没有给出任何元素,默认使用&lt;div&gt;

    如果你改变选择器

    selector: '[mte-glyph-toolbar-item]',
    

    selector: 'li[mte-glyph-toolbar-item]',
    

    selector: 'li',
    

    你会得到一个&lt;li&gt; 元素。

    【讨论】:

      猜你喜欢
      相关资源
      最近更新 更多
      热门标签