sourcetip

Angular2 : 래핑 태그 없이 구성요소 렌더링

fileupload 2023. 4. 28. 21:16
반응형

Angular2 : 래핑 태그 없이 구성요소 렌더링

저는 이것을 할 방법을 찾기 위해 고군분투하고 있습니다.상위 구성 요소에서 템플릿은 다음을 설명합니다.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하지만 제가 보기에는 해결책을 제시하지 못하는 것 같습니다.

특성 선택기를 사용할 수 있습니다.

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

그런 다음 그것을 사용합니다.

<td myTd></td>

"ViewContainerRef"가 필요하며 my-result 구성 요소 내부에서 다음과 같은 작업을 수행합니다.

.html:

<ng-template #template>
    <tr>
       <td>Lisa</td>
       <td>333</td>
    </tr>
</ng-template>

.ts:

@ViewChild('template', { static: true }) template;


constructor(
  private viewContainerRef: ViewContainerRef
) { }

ngOnInit() {
  this.viewContainerRef.createEmbeddedView(this.template);
}

당신은 새로운 CSS를 사용해 볼 수 있습니다.display: contents

다음은 내 도구 모음 scs입니다.

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

속성 선택기는 이 문제를 해결하는 가장 좋은 방법입니다.

그래서 당신의 경우:

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

}

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

}

my-result html

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

작업 스택블리츠를 참조하십시오. https://stackblitz.com/edit/angular-xbbegx

요소에 이 지시어 사용

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

요즘의 또 다른 옵션은ContribNgHostModule패키지에서 사용할 수 있습니다.

모듈을 가져온 후 추가할 수 있습니다.host: { ngNoHost: '' }당신에게@Component장식자 및 래핑 요소가 렌더링되지 않습니다.

@Shlomi Aharoni 답변의 개선.일반적으로 Renderer2를 사용하여 DOM을 조작하여 Angular를 루프에 유지하는 것이 좋습니다. 보안(예: XSS 공격) 및 서버 측 렌더링을 포함한 다른 이유 때문입니다.

지시 예제
import { AfterViewInit, Directive, ElementRef, Renderer2 } from '@angular/core';

@Directive({
  selector: '[remove-wrapper]'
})
export class RemoveWrapperDirective implements AfterViewInit {
  
  constructor(private elRef: ElementRef, private renderer: Renderer2) {}

  ngAfterViewInit(): void {

    // access the DOM. get the element to unwrap
    const el = this.elRef.nativeElement;
    const parent = this.renderer.parentNode(this.elRef.nativeElement);

    // move all children out of the element
    while (el.firstChild) { // this line doesn't work with server-rendering
      this.renderer.appendChild(parent, el.firstChild);
    }

    // remove the empty element from parent
    this.renderer.removeChild(parent, el);
  }
}
구성요소 예제
@Component({
  selector: 'app-page',
  templateUrl: './page.component.html',
  styleUrls: ['./page.component.scss'],
})
export class PageComponent implements AfterViewInit {

  constructor(
    private renderer: Renderer2,
    private elRef: ElementRef) {
  }

  ngAfterViewInit(): void {

    // access the DOM. get the element to unwrap
    const el = this.elRef.nativeElement; // app-page
    const parent = this.renderer.parentNode(this.elRef.nativeElement); // parent

    // move children to parent (everything is moved including comments which angular depends on)
    while (el.firstChild){ // this line doesn't work with server-rendering
      this.renderer.appendChild(parent, el.firstChild);
    }
    
    // remove empty element from parent - true to signal that this removed element is a host element
    this.renderer.removeChild(parent, el, true);
  }
}

이것은 저에게 효과적이며 ExpressionChangedAfterItHasCheckedError 오류를 방지할 수 있습니다.

하위 구성 요소:

@Component({
    selector: 'child-component'
    templateUrl: './child.template.html'
})

export class ChildComponent implements OnInit {
@ViewChild('childTemplate', {static: true}) childTemplate: TemplateRef<any>;

constructor(
      private view: ViewContainerRef
) {}

ngOnInit(): void {
    this.view.createEmbeddedView(this.currentUserTemplate);
}

}

상위 구성요소:

<child-component></child-component>

언급URL : https://stackoverflow.com/questions/38716105/angular2-render-a-component-without-its-wrapping-tag

반응형