Angular2: bir bileşeni sarmalama etiketi olmadan oluşturma


110

Bunu yapmanın bir yolunu bulmakta zorlanıyorum. Bir üst bileşende, şablon a tableve theadelemanını açıklar , ancak tbodybunu başka bir bileşene oluşturan temsilciler şunun gibi:

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

Her myResult bileşeni tr, temelde şu şekilde kendi etiketini oluşturur:

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

Bunu doğrudan üst bileşene koymamamın nedeni (bir myResult bileşenine olan ihtiyacı ortadan kaldırarak) myResult bileşeninin aslında burada gösterilenden daha karmaşık olmasıdır, bu nedenle davranışını ayrı bir bileşene ve dosyaya koymak istiyorum.

Ortaya çıkan DOM kötü görünüyor. Bunun geçersiz olmasından kaynaklandığına inanıyorum, çünkü tbodyyalnızca tröğeleri içerebiliyor (MDN'ye bakın) , ancak oluşturulan (basitleştirilmiş) DOM'm:

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

Aynı şeyi sarmalama <my-result>etiketi olmadan ve yine de bir tablo satırının işlenmesinden tek sorumlu olarak bir bileşen kullanırken oluşturmanın bir yolu var mı?

Ben baktım ng-content, DynamicComponentLoader, ViewContainerRefama bildiğim kadarıyla gördüğünüz gibi bu bir çözüm sağlamak için görünmüyor.


lütfen çalışan bir örnek gösterebilir misin?
zakaria amine

3
Doğru cevap orada, bir plunker örneği stackoverflow.com/questions/46671235/…
sancelot

2
Önerilen yanıtların hiçbiri işe yaramıyor veya tam değil. Doğru cevap burada bir plunker örnek stackoverflow.com/questions/46671235/…
sancelot

Yanıtlar:


104

Öznitelik seçicilerini kullanabilirsiniz

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

ve sonra onu gibi kullan

<td myTd></td>

Bileşeninizin seçicisi şunları içeriyor []mu? Bileşeni declarations: [...]bir modülün içine eklediniz mi ? Bileşen farklı bir modüle kayıtlıysa, bu diğer modülü imports: [...]bileşeni kullanmak istediğiniz modülün içine eklediniz mi?
Günter Zöchbauer

1
Hayır setAttributebenim kodum değil. Ama anladım, şablonumdaki gerçek üst düzey etiketi, ng-containeryeni çalışma kullanımı yerine bileşenim için etiket olarak kullanmam gerekiyordu<ul smMenu class="nav navbar-nav" [submenus]="root?.Submenus" [title]="root?.Title"></ul>
Aviad S.

1
DOM'dan kaldırıldığı için ng-container'da öznitelik bileşenini ayarlayamazsınız. Bu nedenle, onu gerçek bir HTML etiketine ayarlamanız gerekir.
Robouste

2
@AviadP. Çok teşekkürler ... Küçük bir değişikliğin gerekli olduğunu biliyordum ve bu yüzden aklımı kaybediyordum. Bunun yerine: <ul class="nav navbar-nav"> <li-navigation [navItems]="menuItems"></li-navigation> </ul>Bunu kullandım (li-navigasyonu bir öznitelik seçiciye değiştirdikten sonra)<ul class="nav navbar-nav" li-navigation [navItems]="menuItems"></ul>
Mahesh

4
Bir malzeme bileşenini büyütmeye çalışıyorsanız bu cevap işe yaramaz, örneğin <mat-toolbar my-toolbar-rows> birden fazla değil yalnızca bir bileşen seçiciye sahip olabileceğinizden şikayet eder. <toolbar- bileşenim> gidebilirdim ama sonra mat araç çubuğunu bir div içine koyuyor
robert king

26

"ViewContainerRef" e ihtiyacınız var ve sonuç bileşenimin içinde şuna benzer bir şey yapın:

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

6
Tıkır tıkır çalışıyor! Teşekkür ederim. Bunun yerine Angular 8'de aşağıdakileri kullandım:@ViewChild('template', {static: true}) template;
AlainD.

2
Bu işe yaradı. Css display: grid kullandığım bir durum yaşadım ve sonucumun tüm alt öğelerinin kardeşler olarak sonuçlara dönüştüğü ebeveynde ilk öğe olarak <my-result> etiketine sahip olamazsınız . Sorun şu ki, fazladan bir hayalet eleman hala ızgaraları 7 sütunlu "ızgara-şablon-sütunları: tekrar (7, 1fr);" ve 8 element işliyordu. Benim sonucum ve 7 sütun başlığı için 1 hayalet yer tutucu. Bunun çözümü, etiketinize <my-result style = "display: none"> koyarak basitçe gizlemekti. CSS ızgara sistemi bundan sonra bir cazibe gibi çalıştı.
RandallTo

Bu çözüm, my-resultyeni my-resultkardeşler yaratmanın gerektiği daha karmaşık bir durumda bile işe yarar. Öyleyse, my-resulther satırın "alt" satırlara sahip olabileceği bir hiyerarşiniz olduğunu hayal edin . Bu durumda, bir öznitelik seçici kullanmak işe yaramaz, çünkü ilk seçici içine girerse tbody, ikincisi tbodyne dahili ne de a'ya gidemez tr.
Daniel

1
Bunu kullandığımda ExpressionChangedAfterItHasBeenCheckedError almaktan nasıl kaçınabilirim?
gyozo kudor

@gyozokudor belki bir setTimeout kullanıyor
Diego Fernando Murillo

16

yeni css kullanmayı deneyebilirsiniz display: contents

işte benim araç çubuğu scss'im:

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

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

ve kullanımda:

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

12

Bu yönergeyi öğenizde kullanı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);
   }
}

Örnek kullanım:

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

ve ana html'de her zamanki gibi kart öğesini çağırırsınız, örneğin:

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

Çıktı şu şekilde olacaktır:

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

6
'ParentElement.parentNode' boş olduğu için bu bir hata veriyor
emirhosseini

1
Fikir iyi ama düzgün çalışmıyor :-(
jpmottin

10

Öznitelik seçiciler bu sorunu çözmenin en iyi yoludur.

Yani senin durumunda:

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

sonuçlarım 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() {
  }

}

sonuçlarım html

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

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

}

sonucum html

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

Çalışma stackblitz'e bakın: https://stackblitz.com/edit/angular-xbbegx


selector: 'sonuçlarım, [sonuçlarım]', ardından sonuçlarımı HTML'deki etiketin bir özelliği olarak kullanabilirim. Bu doğru cevap. Angular 8.2'de çalışır.
Don Dilanga

5

Günümüzde bir başka seçenek ContribNgHostModulede @angular-contrib/commonpaketten temin edilebilir .

Modülü içe aktardıktan sonra ekleyebilir host: { ngNoHost: '' }sizin için @Componentdekoratör ve hiçbir sarma elemanı hale getirilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.