Angular2: візуалізує компонент без його тегу обтікання


100

Я намагаюся знайти спосіб це зробити. У батьківському компоненті шаблон описує 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, але вони , здається, не забезпечують рішення цієї проблеми , наскільки я можу бачити.


ви можете, будь ласка, показати робочий приклад?
zakaria amine

2
Правильна відповідь є, з plunker зразка stackoverflow.com/questions/46671235 / ...
sancelot

1
Жодна із запропонованих відповідей не працює або не є повною. Правильна відповідь описаний тут з plunker зразка stackoverflow.com/questions/46671235 / ...
sancelot

Відповіді:


95

Ви можете використовувати селектори атрибутів

@Component({
  selector: '[myTd]'
  ...
})

а потім використовувати його як

<td myTd></td>

Чи містить селектор вашого компонента []? Ви додали компонент до declarations: [...]модуля? Якщо компонент зареєстрований в іншому модулі, ви додали цей інший модуль до imports: [...]модуля, де ви хочете використовувати компонент?
Günter Zöchbauer

1
Ні, setAttributeце не мій код. Але я це зрозумів, мені потрібно було використовувати власний тег верхнього рівня в своєму шаблоні як тег для мого компонента, а не ng-containerнове робоче використання<ul smMenu class="nav navbar-nav" [submenus]="root?.Submenus" [title]="root?.Title"></ul>
Aviad P.

1
Не можна встановити компонент атрибута на ng-контейнер, оскільки він видалений з DOM. Ось чому вам потрібно встановити його на фактичному тезі HTML.
Robouste

2
@AviadP. Велике спасибі ... Я знав, що потрібна незначна зміна, і я втрачав розум через це. Тож замість цього: <ul class="nav navbar-nav"> <li-navigation [navItems]="menuItems"></li-navigation> </ul>я використав це (змінивши li-навігацію на селектор атрибутів)<ul class="nav navbar-nav" li-navigation [navItems]="menuItems"></ul>
Mahesh

3
ця відповідь не працює, якщо ви намагаєтесь збільшити матеріальний компонент, наприклад, <mat-панель інструментів my-toolbar-рядки> скаржиться, що ви можете мати лише один селекторний компонент, не кратний. Я міг би перейти до <my-toolbar-component>, але тоді він
Роберта

20

Вам потрібно "ViewContainerRef", а всередині компонента "мій результат" зробити щось подібне:

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);
  }

3
Працює як шарм! Дякую. Я використовував у Angular 8 наступне:@ViewChild('template', {static: true}) template;
AlainD.

1
Це спрацювало. У мене виникла ситуація, коли я використовував css display: grid, і ви не можете його мати тег <my-result> як перший елемент у батьківській програмі з усіма дочірніми елементами надання мого результату як брати і сестри до мого результату . Проблема полягала в тому, що один додатковий привидний елемент все-таки розбив сітки 7 стовпчиків "grid-template-столбики: повторити (7, 1fr);" і це було 8 елементів. 1 примарне місце для мого результату та 7 заголовків стовпців. Завдання для цього полягала в тому, щоб просто приховати це, помістивши <my-result style = "display: none"> у свій тег. Система css grid працювала як шарм після цього.
RandallTo

Це рішення працює навіть у більш складній справі, де my-resultпотрібно створити нових my-resultбратів і сестер. Отже, уявіть, у вас є ієрархія того, my-resultде кожен ряд може мати рядки "дітей". У такому випадку використання селектора атрибутів не буде працювати, оскільки перший селектор переходить у tbody, але другий не може перейти tbodyні у внутрішній, ні у a tr.
Даніель

1
Коли я використовую це, як я можу уникнути отримання ExpressionChangedAfterItHasBeenCheckedError?
gyozo kudor

@gyozokudor, можливо, використовуючи setTimeout
Дієго Фернандо Мурільо Валенсі

12

Використовуйте цю директиву щодо свого елемента

@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>

6
Це є помилкою, оскільки 'parentElement.parentNode' є null
emirhosseini

Ідея хороша, але вона не працює належним чином :-(
jpmottin

9

Вибір атрибутів - найкращий спосіб вирішити цю проблему.

Тож у вашому випадку:

<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() {
  }

}

мої результати html

  <tr my-result [entry]="entry" *ngFor="let entry of entries"><tr>

мій результат ц

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


селектор: "мій результат, [мій результат]", тоді я можу використовувати свої результати як атрибут тегу в HTML. Це правильна відповідь. Він працює в кутовій 8.2.
Дон Діланга

8

ви можете спробувати використовувати новий 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>

3

Інший варіант на сьогоднішній день - це ContribNgHostModuleдоступність у @angular-contrib/commonпакеті .

Після імпорту модуля ви можете додати його host: { ngNoHost: '' }до @Componentдекоратора, і жоден елемент для обгортки не буде надано.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.