template标签_Angular组件去除自定义标签

写Angular组件会在Component装饰器上定义一个selector,作为自定义组件的最外层的标签,那么我能不能去掉这层标签?把这个自定义标签换成普通的HTML标签,如div、span等(前面是我自己摸索的实现,后来发现官方有支持)。

说明

这里以代办项组件为例进行说明,首先我看下组件定义

@Component({
    selector: 'app-check-list',
    templateUrl: './check-list-item.component.html'
})
export class CheckListItemComponent implements OnInit, OnDestroy {
    checked: boolean;

    constructor(
        private elementRef: ElementRef,
        private viewContainerRef: ViewContainerRef,
        private cdr: ChangeDetectorRef
    ) {}

    ngOnInit() {
    }

    onChange(event: any) {
        const checked = event.target.checked;
        // ... some logic 
    }

    ngOnDestroy() {
    }
}

正常情况这个组件渲染出来的dom结构应该如下图所示:

bd78e9c838c72aac936081a79411f46d.png


来看下组件的模板定义如下:

<div #container class="check-item" [attr.data-slate-object]="node.object" [attr.data-key]="node.key">
    <span contentEditable="false" style="margin-right: 0.75em;">
        <input
            type="checkbox"
            [checked]="checked"
            (click)="onClick($event)"
            (change)="onChange($event)"
        />
    </span>
    <span
        contentEditable=""
        class="check-item-content"
    >Slide to the left</span>
</div>

现在我用模板最外层的div 标签来代替app-check-list标签。

用DIV替换自定义标签

第一步

用ViewChild获取模板中的div元素,#container是Angular中取模板元素的标识

@ViewChild('container')
container: ElementRef<any>;

第二步

获取最外层标签的原生dom,可以通过构造函数注入的方式取到

constructor(
    private elementRef: ElementRef
) {}
// this.elementRef.nativeElement 即为最终的<app-check-list>标签

第三步

使用replaceWith将<app-check-list>替换为<div>,可以在ngOnInit构造函数中操作,这个时候模板中的dom已经完成了渲染

ngOnInit() {
    this.elementRef.nativeElement.replaceWith(this.container.nativeElement);
}

ab1b4f1849ece6b0f33ea17dec67fc0e.png


注意

这个时候虽然用div替换了,但是留下了一个问题,就是在组件销毁的时候div元素不会自动的删除,因为我们手动操作DOM导致组件树与HTML标签的对应关系遭到破坏,所以当组件销毁的时候不能自动连带着把对应的DOM也移除,所以这里需要在组件销毁的时候手动移除DOM,实现如下:

ngOnDestroy() {
    this.container.nativeElement.remove();
}

问题

采用这种方式是否对Angular的模板绑定产生影响,也就是模板还能自动否响应TS中数据的变化?Yes,感觉Angular控制数据绑定的粒度不在组件,而是更小的粒度,或是模板或是具体的绑定数据的标签,所以这块不会失去Angular模板绑定的特性。 这种方式是否可以大范围的应用?
这种实现方式虽没有太大的问题,但是代码仍是比较繁琐,是否应用要看有没有具体的需求,如果只为DOM标签的整洁确实没有太大的必要。
我这边主要是为实现多层嵌套的表格做准备,如果table tr td每一层都由单独的组件实现业务逻辑,那么每一层都多出一个额外的标签,这是无法接受的,通常这样的需求在组件库(如:ng-zorro、material design)中都是通过指令的方式解决,并且组件也可以作为指令的方式使用,就是在模板中直接将组件宿主到一个div或者其它HTML标签上,但是这些都是在Angular模板环境中可以实现的,我这边有特殊的要求,需要通过动态创建组件的方式使用组件,所以没法直接用指令。 HostBinding、HostLinstener
显然这两个装饰器没法使用了,因为最外层的dom已经不存在了,还好可以通过其它方式实现。

对比Angular模板与React的render

React写HTML模板也是在JS中,并且模板内容可以使用()包裹作为函数的输入输出参数,这样是非常灵活方便的,几乎没有动态创建组件的概念,同样的需求在Angular中的实现就显得相当繁琐,但是Angular的模板也有很多优点,感觉最核心的就是模板与逻辑的分离,并且模板的开发思路也与传统的页面制作一致。

太年轻

https://stackoverflow.com/questions/35733699/angular2-replace-host-element-with-components-template

因为要发文章特意去Google搜了一下,发现Angular本身提供这个需求的支持,上面的需求只需要改变下Component装饰器上的selector写法就可以了,代码如下

@Component({
    selector: 'div[spe-check-list-item]',
    templateUrl: './check-list-item.component.html'
})

这个也可以解释的通,把div作为宿主,然后在div上挂载我的组件指令。

如此简单,我竟然自己走了这么弯路,论Google搜索的重要性!!!

结束

如果有别的思路或者问题,欢迎赐教

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值