Usiłuję znaleźć sposób, aby to zrobić. W komponencie nadrzędnym szablon opisuje a table
i jego thead
element, ale deleguje renderowanie tbody
do innego komponentu, na przykład:
<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>
Każdy składnik myResult renderuje swój własny tr
znacznik, zasadniczo w następujący sposób:
<tr>
<td>{{ entry.name }}</td>
<td>{{ entry.time }}</td>
</tr>
Powodem, dla którego nie umieszczam tego bezpośrednio w komponencie nadrzędnym (unikając potrzeby komponentu myResult), jest to, że składnik myResult jest w rzeczywistości bardziej skomplikowany niż pokazano tutaj, więc chcę umieścić jego zachowanie w oddzielnym komponencie i pliku.
Wynikowy DOM wygląda źle. Uważam, że dzieje się tak, ponieważ jest nieprawidłowy, ponieważ tbody
może zawierać tylko tr
elementy (patrz MDN) , ale mój wygenerowany (uproszczony) DOM to:
<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>
Czy jest jakiś sposób, abyśmy mogli wyrenderować to samo, ale bez <my-result>
tagu opakowującego i nadal używając komponentu, który jest wyłączną odpowiedzialnością za renderowanie wiersza tabeli?
Mam spojrzał na ng-content
, DynamicComponentLoader
The ViewContainerRef
, ale nie wydaje się, aby zapewnić rozwiązanie tego ile widzę.
źródło
Odpowiedzi:
Możesz użyć selektorów atrybutów
@Component({ selector: '[myTd]' ... })
a następnie użyj go jak
źródło
[]
? Czy dodałeś komponent dodeclarations: [...]
modułu? Jeśli komponent jest zarejestrowany w innym module, czy dodałeś ten inny moduł doimports: [...]
modułu, w którym chcesz użyć komponentu?setAttribute
to nie jest mój kod. Ale rozgryzłem to, musiałem użyć rzeczywistego tagu najwyższego poziomu w moim szablonie jako tagu dla mojego komponentu, zamiastng-container
tak nowego, działającego użycia<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>
użyłem tego (po zmianie li-navigation na selektor atrybutów)<ul class="nav navbar-nav" li-navigation [navItems]="menuItems"></ul>
Potrzebujesz „ViewContainerRef” i wewnątrz komponentu my-result wykonaj coś takiego:
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); }
źródło
@ViewChild('template', {static: true}) template;
my-result
trzeba mieć możliwość tworzenia nowegomy-result
rodzeństwa. Więc wyobraź sobie, że masz hierarchię, wmy-result
której każdy wiersz może mieć wiersze „potomne”. W takim przypadku użycie selektora atrybutu nie zadziałałoby, ponieważ pierwszy selektor znajduje się w elemencietbody
, ale drugi nie może przejść do wewnętrznegotbody
ani dotr
.Użyj tej dyrektywy na swoim elemencie
@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); } }
Przykładowe użycie:
<div class="card" remove-wrapper> This is my card component </div>
aw nadrzędnym html wywołujesz element karty jak zwykle, na przykład:
<div class="cards-container"> <card></card> </div>
Wynik będzie:
<div class="cards-container"> <div class="card" remove-wrapper> This is my card component </div> </div>
źródło
możesz spróbować użyć nowego css
display: contents
oto mój scss paska narzędzi:
: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; }
i 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>
i w użyciu:
<navigation-toolbar (toggle)="snav.toggle()"></navigation-toolbar>
źródło
Selektory atrybutów to najlepszy sposób rozwiązania tego problemu.
Więc w twoim przypadku:
<table> <thead> <tr> <th>Name</th> <th>Time</th> </tr> </thead> <tbody my-results> </tbody> </table>
my-results 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() { } }
my-results html
<tr my-result [entry]="entry" *ngFor="let entry of entries"><tr>
my-result 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>
Zobacz działający stackblitz: https://stackblitz.com/edit/angular-xbbegx
źródło
Inną opcją w dzisiejszych czasach jest
ContribNgHostModule
udostępnienie z@angular-contrib/common
pakietu .Po zaimportowaniu modułu możesz dodać go
host: { ngNoHost: '' }
do@Component
dekoratora i żaden element opakowujący nie będzie renderowany.źródło