写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](https://i-blog.csdnimg.cn/blog_migrate/ea45aec3f21713cdda7018acae93c05f.jpeg)
来看下组件的模板定义如下:
<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](https://i-blog.csdnimg.cn/blog_migrate/36448113fcd27189e602726bb8b43405.jpeg)
注意
这个时候虽然用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搜索的重要性!!!
结束
如果有别的思路或者问题,欢迎赐教