移除 Angular 组件创建的宿主 HTML 元素选择器

Posted

技术标签:

【中文标题】移除 Angular 组件创建的宿主 HTML 元素选择器【英文标题】:Remove the host HTML element selectors created by angular component 【发布时间】:2016-03-20 17:27:10 【问题描述】:

在 Angular 2 中,svg-rect 是一个创建 rect 的组件,如下所示,

<svg   x="0" y="0">
    <g id="svgGroup">
        <svg-rect>
        <!--template bindings=-->
            <rect x="10" y="10"   fill="red" stroke="#000" stroke-></rect>
        <!--template bindings=-->
        </svg-rect>
        <svg-rect>
        <!--template bindings=-->
            <rect x="10" y="10"   fill="red" stroke="#000" stroke-></rect>
        <!--template bindings=-->
        </svg-rect>
    </g>
</svg>

但这不会渲染 rect 因为创建了特殊的元素标签。如果 svg-rect 标签被移除,它会呈现 rect

<svg   x="0" y="0">
    <g id="svgGroup">
        <!--template bindings=-->
        <rect x="10" y="10"   fill="red" stroke="#000" stroke-></rect>
        <!--template bindings=-->
        <!--template bindings=-->
        <rect x="10" y="10"   fill="red" stroke="#000" stroke-></rect>
        <!--template bindings=-->
    </g>
</svg>

在 Angular 1.x 中,有 replace: 'true' 可以删除带有编译输出的指令标签。我们可以在 angular2 中实现相同的功能吗?

【问题讨论】:

【参考方案1】:

与其尝试摆脱宿主元素,不如将其转换为有效的 SVG 但在其他方面不受影响:而不是您的 元素选择器

selector: "svg-rect"

及其在模板中对应的元素:

template: `...<svg-rect>...</svg-rect>...`

切换到属性选择器

selector: "[svg-rect]"

并将该属性添加到组元素标记中:

template: `...<g svg-rect>...</g>...`

这将扩展为:

<svg   x="0" y="0">
    <g id="svgGroup">
        <g svg-rect>
        <!--template bindings=-->
            <rect x="10" y="10"   fill="red" stroke="#000" stroke-></rect>
        <!--template bindings=-->
        </g>
        <g svg-rect>
        <!--template bindings=-->
            <rect x="10" y="10"   fill="red" stroke="#000" stroke-></rect>
        <!--template bindings=-->
        </g>
    </g>
</svg>

这是有效的 SVG,它将被渲染。 Plnkr

【讨论】:

这适用于任何需要有效元素的地方,我刚刚将它用于出现相同问题的表头 的小组件。但是,有一个查询,您可以为此链接到任何文档吗? @David - 你不会在手册中找到它(还),但向你保证它是有福的:这是用户提出有时(svg 和表格)你需要的情况的线程使用属性选择器来修改结构:github.com/angular/angular/issues/11280。作为回应,在此处添加了功能(实际上是恢复了):github.com/angular/angular/pull/11808。 但是如果我想在一个 元素中添加多个 元素怎么办(手动,而不是 *ngFor) 如何向属性选择器添加属性? 【参考方案2】:

另一种删除我使用的组件主机的方法。

指令remove-host

//remove the host of avatar to be rendered as svg
@Directive(
    selector: '[remove-host]'
)
class RemoveHost 
    constructor(private el: ElementRef) 
    

    //wait for the component to render completely
    ngOnInit() 
        var nativeElement: htmlElement = this.el.nativeElement,
            parentElement: HTMLElement = nativeElement.parentElement;
        // move all children out of the element
        while (nativeElement.firstChild) 
            parentElement.insertBefore(nativeElement.firstChild, nativeElement);
        
        // remove the empty element(the host)
        parentElement.removeChild(nativeElement);
    

使用该指令;&lt;avatar [name]="hero.name" remove-host&gt;&lt;/avatar&gt;

remove-host 指令中,nativeElement 的所有子元素都插入到宿主之前,然后移除宿主元素。

Sample Example Gist根据用例,可能存在一些性能问题。

【讨论】:

可能是性能问题,这是我最初的想法。但是目前在我的材料设计轻组件中使用这种方式来替换宿主元素,否则会出现显示 html 的问题 @Kuncevich 很高兴知道我的回答有所帮助,当元素 ChangeDetection 被触发为 shadow DOM !== actual DOM 时,会产生性能瓶颈。但是如果没有很多元素,那么这不会那么大。希望;) 我们需要一些Angular1的类比replace:true***.com/questions/22497706/…有人说这个功能实际上是在angular2中但非官方支持的。 @Kuncevic 恰恰相反。它仍在 Angular1 中,但在那里已弃用。这就是为什么他们一开始不想在 Angular2 中添加它。事实证明,许多问题值得付出努力。 如果您考虑将这种技术与正在更改数据的ngFor 一起使用,您会惊讶于 UI 无法正确更新。【参考方案3】:

类似的东西呢:

:host  display: contents; 

将它放在宿主组件的 css(或 scss)文件中将导致组件的框不呈现。

N.B:它以前对我有用——但显然我会担心浏览器的兼容性,特别是如果您正在开发以支持旧浏览器。我也很确定这并不完全超出“实验”阶段。文档还指出,这可能会导致可访问性问题。

【讨论】:

这样一个简短、简单、有效的答案 这很好用,没有额外的解决方法。希望 Angular 会在某个时候添加不呈现宿主元素的选项。 这对我不起作用。也许他们在较新版本的 Angular 中删除了此功能。 如果有选择器期望直接后代,这不一定有效。 虽然没有删除父元素,但至少不会在视觉上修改您的布局。【参考方案4】:

还有另一种方法,我们可以从组件中获取组件的模板。 首先,我们创建组件,我们希望从浏览器渲染中移除它的标签(我们不是在这里试图移除标签。)

@Component(
  selector: 'tl-no-tag',
  template: `
    <template #tmp>
      <p>value</p>
    </template>`,
  styleUrls: []
)
export class TlNoTagComponent 
  @ViewChild('tmp') tmp: any;
  value = 5;

然后,在另一个组件的模板中,我们写:

<tl-no-tag #source></tl-no-tag> <!-- This line can be placed anywhere -->
<template [ngTemplateOutlet]="source.tmp"></template> <!-- This line will be placed where needed -->

然后,我们在浏览器中有这样的东西:

<tl-no-tag></tl-no-tag>
<p>5</p>

所以,我们将&lt;p&gt;value&lt;/p&gt; 带出TlNoTagComponent。 &lt;tl-no-tag&gt;&lt;/tl-no-tag&gt; 将继续存在,但不会阻止任何 css 或 svg-thing。

【讨论】:

"模板是否有自己独立的变量作用域,模板能看到哪些变量?"看看这个(在模板内容部分):blog.angular-university.io/… 如果您要使用 *ngFor 来处理这些数据,这就是您要走的路。我的方案是将&lt;td&gt; 组合在一起。此代码完美运行。确保在tl-no-tag 上设置display: none,这样它就不会干扰您的colspan【参考方案5】:

引用Angular 1 to Angular 2 Upgrade Strategy doc:

Angular 2 不支持替换其宿主元素的指令(在 Angular 1 中替换为 true 指令)。在许多情况下,这些指令可以升级为常规组件指令。

在某些情况下,常规组件指令不起作用,在这些情况下可以使用替代方法。例如对于 svg,请参见:https://github.com/mhevery/ng2-svg

【讨论】:

非常感谢您指出这一点。我确信在 Angular 中删除宿主元素是可能的,但这显然是我们在 AngularJS 时代使用的东西:

以上是关于移除 Angular 组件创建的宿主 HTML 元素选择器的主要内容,如果未能解决你的问题,请参考以下文章

Angular 组件宿主元素的宽度和高度为 0

Angular 结构指令:用另一个组件包装宿主元素

Angular 2:指令和宿主组件之间的通信

从组件中移除 ngModel

如何在 Angular2 中为被移除的组件捕获 onDestroy

在 Angular 结构指令中使用动态组件会产生额外的 HTML 标记。如何删除或更换它?