Angular2 :渲染一个没有包装标签的组件
Angular2 : render a component without its wrapping tag
我正在努力寻找一种方法来做到这一点。在父组件中,模板描述table
及其thead
元素,但将呈现tbody
委托给另一个组件,如下所示:
<table>
<thead>
<tr>
<th>Name</th>
<th>Time</th>
</tr>
</thead>
<tbody *ngFor="let entry of getEntries()">
<my-result [entry]="entry"></my-result>
</tbody>
</table>
每个 myResult 组件都呈现自己的 tr
标记,大致如下所示:
<tr>
<td>{{ entry.name }}</td>
<td>{{ entry.time }}</td>
</tr>
我没有将其直接放在父组件中的原因是 myResult 组件实际上比此处显示的要复杂得多,因此我想将其行为放在单独的组件和文件中。
生成的 DOM 看起来很糟糕。我相信这是因为它是无效的,因为tbody
只能包含tr
元素(参见 MDN(,但我生成的(简化(DOM 是:
<table>
<thead>
<tr>
<th>Name</th>
<th>Time</th>
</tr>
</thead>
<tbody>
<my-result>
<tr>
<td>Bob</td>
<td>128</td>
</tr>
</my-result>
</tbody>
<tbody>
<my-result>
<tr>
<td>Lisa</td>
<td>333</td>
</tr>
</my-result>
</tbody>
</table>
有什么方法可以使用子组件来封装表行的渲染而无需包装<my-result>
标签,从而呈现相同的内容?
我看过ng-content
,DynamicComponentLoader
,ViewContainerRef
,但据我所知,它们似乎没有提供解决方案。
您可以使用属性选择器
@Component({
selector: '[myTd]'
...
})
然后像这样使用它
<td myTd></td>
你需要"ViewContainerRef",在my-result组件中做这样的事情:
.html
:
<ng-template #template>
<tr>
<td>Lisa</td>
<td>333</td>
</tr>
</ng-template>
.ts
:
@ViewChild('template', { static: true }) template;
constructor(
private viewContainerRef: ViewContainerRef
) { }
ngOnInit() {
this.viewContainerRef.createEmbeddedView(this.template);
}
你可以尝试使用新的CSS display: contents
这是我的工具栏 SCSS:
:host {
display: contents;
}
:host-context(.is-mobile) .toolbar {
position: fixed;
/* Make sure the toolbar will stay on top of the content as it scrolls past. */
z-index: 2;
}
h1.app-name {
margin-left: 8px;
}
和 html:
<mat-toolbar color="primary" class="toolbar">
<button mat-icon-button (click)="toggle.emit()">
<mat-icon>menu</mat-icon>
</button>
<img src="/assets/icons/favicon.png">
<h1 class="app-name">@robertking Dashboard</h1>
</mat-toolbar>
和使用中:
<navigation-toolbar (toggle)="snav.toggle()"></navigation-toolbar>
属性选择器是解决此问题的最佳方法。
所以在你的情况下:
<table>
<thead>
<tr>
<th>Name</th>
<th>Time</th>
</tr>
</thead>
<tbody my-results>
</tbody>
</table>
我的搜索结果
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'my-results, [my-results]',
templateUrl: './my-results.component.html',
styleUrls: ['./my-results.component.css']
})
export class MyResultsComponent implements OnInit {
entries: Array<any> = [
{ name: 'Entry One', time: '10:00'},
{ name: 'Entry Two', time: '10:05 '},
{ name: 'Entry Three', time: '10:10'},
];
constructor() { }
ngOnInit() {
}
}
my-results html
<tr my-result [entry]="entry" *ngFor="let entry of entries"><tr>
我的 - 结果 TS
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: '[my-result]',
templateUrl: './my-result.component.html',
styleUrls: ['./my-result.component.css']
})
export class MyResultComponent implements OnInit {
@Input() entry: any;
constructor() { }
ngOnInit() {
}
}
my-result html
<td>{{ entry.name }}</td>
<td>{{ entry.time }}</td>
查看工作堆栈闪电战:https://stackblitz.com/edit/angular-xbbegx
在元素上使用此指令
@Directive({
selector: '[remove-wrapper]'
})
export class RemoveWrapperDirective {
constructor(private el: ElementRef) {
const parentElement = el.nativeElement.parentElement;
const element = el.nativeElement;
parentElement.removeChild(element);
parentElement.parentNode.insertBefore(element, parentElement.nextSibling);
parentElement.parentNode.removeChild(parentElement);
}
}
用法示例:
<div class="card" remove-wrapper>
This is my card component
</div>
在父 HTML 中,您可以像往常一样调用 card 元素,例如:
<div class="cards-container">
<card></card>
</div>
输出将是:
<div class="cards-container">
<div class="card" remove-wrapper>
This is my card component
</div>
</div>
现在的另一个选择是@angular-contrib/common
包中提供的ContribNgHostModule
。
导入模块后,您可以将host: { ngNoHost: '' }
添加到@Component
装饰器中,并且不会渲染任何包装元素。
对 @Shlomi Aharoni 答案的改进。通常,使用 Renderer2 来操作 DOM 以保持 Angular 在循环中是一种很好的做法,因为出于其他原因,包括安全性(例如 XSS 攻击(和服务器端渲染。
指令示例
import { AfterViewInit, Directive, ElementRef, Renderer2 } from '@angular/core';
@Directive({
selector: '[remove-wrapper]'
})
export class RemoveWrapperDirective implements AfterViewInit {
constructor(private elRef: ElementRef, private renderer: Renderer2) {}
ngAfterViewInit(): void {
// access the DOM. get the element to unwrap
const el = this.elRef.nativeElement;
const parent = this.renderer.parentNode(this.elRef.nativeElement);
// move all children out of the element
while (el.firstChild) { // this line doesn't work with server-rendering
this.renderer.appendChild(parent, el.firstChild);
}
// remove the empty element from parent
this.renderer.removeChild(parent, el);
}
}
组件示例
@Component({
selector: 'app-page',
templateUrl: './page.component.html',
styleUrls: ['./page.component.scss'],
})
export class PageComponent implements AfterViewInit {
constructor(
private renderer: Renderer2,
private elRef: ElementRef) {
}
ngAfterViewInit(): void {
// access the DOM. get the element to unwrap
const el = this.elRef.nativeElement; // app-page
const parent = this.renderer.parentNode(this.elRef.nativeElement); // parent
// move children to parent (everything is moved including comments which angular depends on)
while (el.firstChild){ // this line doesn't work with server-rendering
this.renderer.appendChild(parent, el.firstChild);
}
// remove empty element from parent - true to signal that this removed element is a host element
this.renderer.removeChild(parent, el, true);
}
}
这对我有用,它可以避免 ExpressionChangedAfterItHasBeenCheckedError 错误。
子组件:
@Component({
selector: 'child-component'
templateUrl: './child.template.html'
})
export class ChildComponent implements OnInit {
@ViewChild('childTemplate', {static: true}) childTemplate: TemplateRef<any>;
constructor(
private view: ViewContainerRef
) {}
ngOnInit(): void {
this.view.createEmbeddedView(this.currentUserTemplate);
}
}
父组件:
<child-component></child-component>
- 如何使用javascript将任何手机号码包装到锚标签中
- 如何使用jquery将每4个元素包装在一个
- 标签中
- 我怎样才能把p中的一个单词包装在a标签中呢
- Ace编辑器:如何防止某些标签被P包装
- javascript AngularJS在包装标签时自动更改字体大小
- 使用 jQuery 将一个 HTML 标记包装在另一个标签中
- 如何将 jQuery 内容包装在 H 标签中
- 将单词包装在标签中,保留标记
- JQuery,剥离所有标签,包装新标签并克隆和附加
- 搜索术语,将其包装到标签中
- 我可以将 CKEditor 的编辑块包装在几个标签中以模仿上下文吗?
- 为什么这个网站返回包装在jQuery标签中的自动完成数据
- 检测字符串中的“模式”并将其包装在javascript(angular)中标签
- 如果对 JSONP 请求的服务器响应未正确包装在函数中,是否有任何方法可以覆盖“无效标签”错误
- 强制点击<img>标记以选择包装<a>标签
- 为什么用Javascript RegEx在文本周围包装标签会将标签显示为文本
- 如何在Javascript或jQuery的注释中包装标签
- 如何包装<标签内
- 标签
- Angular2 :渲染一个没有包装标签的组件
- 停止d3圆包装标签重叠