Angular2: hiển thị một thành phần không có thẻ gói của nó


100

Tôi đang đấu tranh để tìm ra một cách để làm điều này. Trong thành phần mẹ, mẫu mô tả a tabletheadphần tử của nó , nhưng ủy quyền hiển thị tbodythành phần khác, như thế này:

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

Mỗi thành phần myResult hiển thị trthẻ riêng của nó , về cơ bản như vậy:

<tr>
  <td>{{ entry.name }}</td>
  <td>{{ entry.time }}</td>
</tr>

Lý do tôi không đặt điều này trực tiếp trong thành phần mẹ (tránh sự cần thiết phải có thành phần myResult) là thành phần myResult thực sự phức tạp hơn được hiển thị ở đây, vì vậy tôi muốn đặt hành vi của nó trong một thành phần và tệp riêng biệt.

DOM kết quả trông xấu. Tôi tin rằng điều này là do nó không hợp lệ, vì tbodychỉ có thể chứa trcác phần tử (xem MDN) , nhưng DOM được tạo (đơn giản hóa) của tôi là:

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

Có cách nào để chúng ta có thể hiển thị cùng một thứ, nhưng không có <my-result>thẻ gói và trong khi vẫn sử dụng một thành phần chịu trách nhiệm duy nhất cho việc hiển thị một hàng trong bảng không?

Tôi đã xem xét ng-content, DynamicComponentLoaderthì ViewContainerRef, nhưng họ dường như không cung cấp một giải pháp này như xa như tôi có thể nhìn thấy.


bạn có thể vui lòng cho thấy một ví dụ làm việc?
zakaria amine

2
Câu trả lời đúng là có, với một mẫu plunker stackoverflow.com/questions/46671235/...
sancelot

1
Không có câu trả lời được đề xuất nào đang hoạt động hoặc đã hoàn thành. Câu trả lời đúng được mô tả ở đây với một mẫu plunker stackoverflow.com/questions/46671235/...
sancelot

Câu trả lời:


95

Bạn có thể sử dụng bộ chọn thuộc tính

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

và sau đó sử dụng nó như

<td myTd></td>

Bộ chọn của thành phần của bạn có chứa []không? Bạn đã thêm thành phần vào declarations: [...]của một mô-đun? Nếu thành phần được đăng ký trong một mô-đun khác, bạn có thêm mô-đun khác này vào imports: [...]của mô-đun mà bạn muốn sử dụng thành phần không?
Günter Zöchbauer

1
Không setAttributekhông phải là mã của tôi. Nhưng tôi figured it out, tôi cần phải sử dụng thẻ cấp cao nhất thực tế trong mẫu của tôi như là thẻ cho thành phần của tôi thay vì ng-containersử dụng lao động nên mới là<ul smMenu class="nav navbar-nav" [submenus]="root?.Submenus" [title]="root?.Title"></ul>
Aviad P.

1
Bạn không thể đặt thành phần thuộc tính trên ng-container vì nó bị xóa khỏi DOM. Đó là lý do tại sao bạn cần đặt nó trên một thẻ HTML thực tế.
Robouste

2
@AviadP. Cảm ơn nhiều ... Tôi biết có một thay đổi nhỏ cần thiết và tôi đã mất trí về điều này. Vì vậy, thay vì điều này: <ul class="nav navbar-nav"> <li-navigation [navItems]="menuItems"></li-navigation> </ul>tôi đã sử dụng điều này (sau khi thay đổi li-navigation thành công cụ chọn thuộc tính)<ul class="nav navbar-nav" li-navigation [navItems]="menuItems"></ul>
Mahesh

3
câu trả lời này không hoạt động nếu bạn đang cố gắng tăng cường thành phần vật liệu, ví dụ: <mat-toolbar my-toolbar-rows> sẽ phàn nàn rằng bạn chỉ có thể có một bộ chọn thành phần chứ không phải nhiều. Tôi có thể truy cập <my-toolbar-component> nhưng sau đó nó đặt thanh công cụ mat-bên trong một div
robert king

20

Bạn cần "ViewContainerRef" và bên trong thành phần kết quả của tôi thực hiện một số việc như sau:

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
Hoạt động như một sự quyến rũ! Cảm ơn bạn. Thay vào đó, tôi đã sử dụng trong Angular 8 như sau:@ViewChild('template', {static: true}) template;
AlainD.

1
Điều này đã hiệu quả. Tôi đã gặp một tình huống khi sử dụng css display: grid và bạn không thể có nó vẫn có thẻ <my-result> là phần tử đầu tiên trong phần tử gốc với tất cả các phần tử con của kết quả của tôi hiển thị là anh chị em với kết quả của tôi . Vấn đề là một phần tử ma bổ sung vẫn phá vỡ lưới 7 cột "lưới-mẫu-cột: lặp lại (7, 1fr);" và nó đang kết xuất 8 phần tử. 1 trình giữ chỗ ma cho kết quả của tôi và tiêu đề 7 cột. Công việc giải quyết vấn đề này là chỉ cần ẩn nó đi bằng cách đặt <my-result style = "display: none"> vào thẻ của bạn. Hệ thống lưới css hoạt động như một sự quyến rũ sau đó.
Randall đến

Giải pháp này hoạt động ngay cả trong trường hợp phức tạp hơn, nơi my-resultcần có khả năng tạo my-resultanh chị em mới . Vì vậy, hãy tưởng tượng bạn có một hệ thống phân cấp trong my-resultđó mỗi hàng có thể có các hàng "con". Trong trường hợp đó, việc sử dụng bộ chọn thuộc tính sẽ không hoạt động, vì bộ chọn đầu tiên đi vào tbody, nhưng bộ chọn thứ hai không thể đi trong nội bộ tbodycũng như trong a tr.
Daniel

1
Khi tôi sử dụng điều này, làm cách nào để tránh bị ExpressionChangedAfterItHasBeenCheckedError?
gyozo kudor

@gyozokudor có thể sử dụng setTimeout
Diego Fernando Murillo Valenci

12

Sử dụng chỉ thị này trên phần tử của bạn

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

Ví dụ sử dụng:

<div class="card" remove-wrapper>
   This is my card component
</div>

và trong html mẹ, bạn gọi phần tử thẻ như bình thường, ví dụ:

<div class="cards-container">
   <card></card>
</div>

Đầu ra sẽ là:

<div class="cards-container">
   <div class="card" remove-wrapper>
      This is my card component
   </div>
</div>

6
Đây là ném một lỗi vì 'parentElement.parentNode' là null
emirhosseini

Ý tưởng là tốt nhưng nó không hoạt động bình thường :-(
jpmottin

9

Bộ chọn thuộc tính là cách tốt nhất để giải quyết vấn đề này.

Vì vậy, trong trường hợp của bạn:

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Time</th>
    </tr>
  </thead>
  <tbody my-results>
  </tbody>
</table>

kết quả của tôi 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 kết quả của tôi

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

kết quả của tôi 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 kết quả của tôi

  <td>{{ entry.name }}</td>
  <td>{{ entry.time }}</td>

Xem stackblitz đang hoạt động: https://stackblitz.com/edit/angular-xbbegx


selector: 'my-results, [my-results]', sau đó tôi có thể sử dụng my-results làm thuộc tính của thẻ trong HTML. Đây là câu trả lời chính xác. Nó hoạt động trong Angular 8.2.
Don Dilanga

8

bạn có thể thử sử dụng css mới display: contents

đây là scss trên thanh công cụ của tôi:

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

và 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>

và đang sử dụng:

<navigation-toolbar (toggle)="snav.toggle()"></navigation-toolbar>

3

Một lựa chọn khác hiện nay là được ContribNgHostModuletạo sẵn từ @angular-contrib/commongói .

Sau khi nhập mô-đun, bạn có thể thêm host: { ngNoHost: '' }vào trình @Componenttrang trí của mình và không có phần tử gói nào được hiển thị.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.