Angular2:ラッピングタグなしでコンポーネントをレンダリングする


100

私はこれを行う方法を見つけるのに苦労しています。親コンポーネントでは、テンプレートは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は悪いように見えます。これはtbodytr要素しか含めることができないため(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-contentDynamicComponentLoaderViewContainerRef、しかし、彼らは私の知る限りこれに対する解決策を提供していないようです。


実用的な例を見せていただけますか?
ザカリアアミン2018

2
正しい答えはplunkerサンプルと、そこにあるstackoverflow.com/questions/46671235/...
sancelot

1
提案された回答はどれも機能していないか、完全ではありません。ここでは、正しい答えを、プランカーのサンプルstackoverflow.com/questions/46671235/…で
sancelot

回答:


95

属性セレクターを使用できます

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

そしてそれを次のように使用します

<td myTd></td>

コンポーネントのセレクターに[]declarations: [...]モジュールのコンポーネントを追加しましたか?コンポーネントが別のモジュールに登録されている場合、コンポーネントimports: [...]を使用するモジュールにこの他のモジュールを追加しましたか?
ギュンターZöchbauer

1
いいえsetAttribute、私のコードではありません。しかし、私はそれを考え出した、私がするのではなく、私のコンポーネントのためのタグとしての私のテンプレートでは、実際のトップレベルのタグを使用するために必要なng-containerので、新しい作業の使用である<ul smMenu class="nav navbar-nav" [submenus]="root?.Submenus" [title]="root?.Title"></ul>
Aviad P.

1
ng-containerはDOMから削除されるため、属性コンポーネントを設定することはできません。そのため、実際のHTMLタグに設定する必要があります。
ロブステ

2
@AviadP。どうもありがとう...必要なマイナーな変更があることを知っていたので、これについて頭を悩ませていました。したがって、これの代わりに、これ<ul class="nav navbar-nav"> <li-navigation [navItems]="menuItems"></li-navigation> </ul>を使用しました(li-navigationを属性セレクターに変更した後)<ul class="nav navbar-nav" li-navigation [navItems]="menuItems"></ul>
Mahesh

3
マテリアルコンポーネントを拡張しようとしている場合、この回答は機能しません。たとえば、<mat-toolbar my-toolbar-rows>は、複数ではなく1つのコンポーネントセレクターしか使用できないというメッセージを表示します。<my-toolbar-component>に移動することもできますが、それはdiv内にmat-toolbarを配置しています
robert king

20

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

3
魅力的な作品!ありがとうございました。私の代わりに、次の角8で使用:@ViewChild('template', {static: true}) template;
AlainD。

1
これはうまくいきました。私はcss display:gridを使用していて、my-resultのすべての子要素がmy-resultの兄弟としてレンダリングされている<my-result>タグを親の最初の要素として持つことはできません。問題は、1つの余分なゴースト要素が依然としてグリッドを7列分断していたことです "grid-template-columns:repeat(7、1fr);"。そして、それは8つの要素をレンダリングしていました。my-result用のゴーストプレースホルダー1つと7つの列ヘッダー。これを回避するには、タグに<my-result style = "display:none">を挿入して非表示にするだけです。その後、CSSグリッドシステムは魅力的に機能しました。
Randall

このソリューションは、my-result新しいmy-result兄弟を作成できる必要があるより複雑なケースでも機能します。したがって、my-result各行に「子」行を含めることができる階層があるとします。その場合、最初のセレクターがに入るので、属性セレクターを使用しても機能しませんtbodyが、2番目のセレクターは内部にtbodyもにも入ることができませんtr
Daniel

1
これを使用すると、ExpressionChangedAfterItHasBeenCheckedErrorを回避できますか?
餃子クドール

@gyozokudorはおそらくsetTimeoutを使用している
Diego Fernando Murillo Valenci

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では、通常どおりcard要素を呼び出します。次に例を示します。

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

アイデアは良いですが、適切に機能していません:-(
jpmottin

9

この問題を解決するには、属性セレクターが最善の方法です。

だからあなたの場合:

<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


セレクター: 'my-results、[my-results]'の場合、my-resultsをHTMLのタグの属性として使用できます。これが正解です。Angular 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

最近のもう1つのオプションはContribNgHostModule@angular-contrib/commonパッケージから利用できるようにすることです。

モジュールをインポートした後host: { ngNoHost: '' }@Componentデコレーターに追加することができ、ラッピング要素はレンダリングされません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.