Я изо всех сил пытаюсь найти способ сделать это. В родительском компоненте шаблон описывает a 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), заключается в том, что компонент 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]' ... })
а затем используйте его как
источник
[]
? Вы добавляли компонент вdeclarations: [...]
модуль? Если компонент зарегистрирован в другом модуле, добавляли ли вы этот другой модульimports: [...]
в модуль, в котором вы хотите использовать компонент?setAttribute
это не мой код. Но я понял это, мне нужно было использовать фактический тег верхнего уровня в моем шаблоне в качестве тега для моего компонента вместоng-container
нового рабочего использования<ul smMenu class="nav navbar-nav" [submenus]="root?.Submenus" [title]="root?.Title"></ul>
<ul class="nav navbar-nav"> <li-navigation [navItems]="menuItems"></li-navigation> </ul>
я использовал это (после изменения li-navigation на селектор атрибутов)<ul class="nav navbar-nav" li-navigation [navItems]="menuItems"></ul>
Вам нужен ViewContainerRef, и внутри компонента my-result сделайте что-то вроде этого:
html:
<ng-template #template> <tr> <td>Lisa</td> <td>333</td> </tr> </ng-template>
ts:
@ViewChild('template') template; constructor( private viewContainerRef: ViewContainerRef ) { } ngOnInit() { this.viewContainerRef.createEmbeddedView(this.template); }
источник
@ViewChild('template', {static: true}) template;
my-result
необходимо иметь возможность создавать новыхmy-result
братьев и сестер. Итак, представьте, что у вас есть иерархия, вmy-result
которой каждая строка может иметь «дочерние» строки. В этом случае использование селектора атрибутов не сработает, поскольку первый селектор входит вtbody
, а второй не может входитьtbody
ни во внутренний, ни вtr
.вы можете попробовать использовать новый 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>
источник
Используйте эту директиву в своем элементе
@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 вы вызываете элемент карты как обычно, например:
<div class="cards-container"> <card></card> </div>
Результат будет:
<div class="cards-container"> <div class="card" remove-wrapper> This is my card component </div> </div>
источник
Селекторы атрибутов - лучший способ решить эту проблему.
Итак, в вашем случае:
<table> <thead> <tr> <th>Name</th> <th>Time</th> </tr> </thead> <tbody my-results> </tbody> </table>
мои результаты ts
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() { } }
мои результаты 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() { } }
мой-результат html
<td>{{ entry.name }}</td> <td>{{ entry.time }}</td>
См. Рабочий stackblitz: https://stackblitz.com/edit/angular-xbbegx
источник
Другой вариант в настоящее время -
ContribNgHostModule
доступный из@angular-contrib/common
пакета .После импорта модуля вы можете добавить его
host: { ngNoHost: '' }
в свой@Component
декоратор, и никакие элементы упаковки не будут отображаться.источник