웹사이트 검색

Angular에서 NgTemplateOutlet을 사용하여 재사용 가능한 구성 요소를 만드는 방법


소개

단일 책임 원칙은 애플리케이션의 일부가 하나의 목적을 가져야 한다는 생각입니다. 이 원칙을 따르면 Angular 앱을 더 쉽게 테스트하고 개발할 수 있습니다.

Angular에서 특정 구성 요소를 만드는 대신 NgTemplateOutlet을 사용하면 구성 요소 자체를 수정하지 않고도 다양한 사용 사례에 맞게 구성 요소를 쉽게 수정할 수 있습니다!

이 문서에서는 기존 구성 요소를 가져와 NgTemplateOutlet을 사용하도록 다시 작성합니다.

전제 조건

이 자습서를 완료하려면 다음이 필요합니다.

  • Node.js를 로컬에 설치했습니다. Node.js를 설치하고 로컬 개발 환경을 만드는 방법에 따라 수행할 수 있습니다.
  • Angular 프로젝트 설정에 어느 정도 익숙합니다.

이 튜토리얼은 Node v16.6.2, npm v7.20.6 및 @angular/core v12.2.0에서 검증되었습니다.

1단계 – CardOrListViewComponent 구성

모드카드 또는 목록 형식으로 항목을 표시하는 CardOrListViewComponent를 고려하십시오. >.

card-or-list-view.component.ts 파일로 구성됩니다.

import {
  Component,
  Input
} from '@angular/core';

@Component({
  selector: 'card-or-list-view',
  templateUrl: './card-or-list-view.component.html'
})
export class CardOrListViewComponent {

  @Input() items: {
    header: string,
    content: string
  }[] = [];

  @Input() mode: string = 'card';

}

그리고 card-or-list-view.component.html 템플릿:

<ng-container [ngSwitch]="mode">
  <ng-container *ngSwitchCase="'card'">
    <div *ngFor="let item of items">
      <h1>{{item.header}}</h1>
      <p>{{item.content}}</p>
    </div>
  </ng-container>
  <ul *ngSwitchCase="'list'">
    <li *ngFor="let item of items">
      {{item.header}}: {{item.content}}
    </li>
  </ul>
</ng-container>

다음은 이 구성 요소의 사용 예입니다.

import { Component } from '@angular/core';

@Component({
  template: `
    <card-or-list-view
        [items]="items"
        [mode]="mode">
    </card-or-list-view>
`
})
export class UsageExample {
  mode = 'list';
  items = [
    {
      header: 'Creating Reuseable Components with NgTemplateOutlet in Angular',
      content: 'The single responsibility principle...'
    } // ... more items
  ];
}

이 구성 요소는 단일 책임이 없으며 매우 유연하지 않습니다. 모드를 추적하고 카드목록 보기 모두에서 항목을 표시하는 방법을 알아야 합니다. 그리고 헤더콘텐츠가 있는 항목만 표시할 수 있습니다.

템플릿을 사용하여 구성 요소를 별도의 보기로 나누어 이를 변경해 보겠습니다.

2단계 – ng-template 및 NgTemplateOutlet 이해

CardOrListViewComponent가 모든 종류의 항목을 표시할 수 있도록 하려면 표시 방법을 알려줄 수 있어야 합니다. 항목을 스탬프 처리하는 데 사용할 수 있는 템플릿을 제공하여 이를 달성할 수 있습니다.

템플릿은 를 사용하는 TemplateRefs이고 스탬프는 TemplateRefs에서 생성된 EmbeddedViewRefs입니다. EmbeddedViewRefs는 자체 컨텍스트가 있는 Angular의 뷰를 나타내며 가장 작은 필수 빌딩 블록입니다.

Angular는 NgTemplateOutlet을 사용하여 템플릿에서 보기를 스탬프 처리하는 이 개념을 사용하는 방법을 제공합니다.

NgTemplateOutletTemplateRef와 컨텍스트를 취하고 제공된 컨텍스트로 EmbeddedViewRef를 스탬프 처리하는 지시어입니다. 템플릿에서 사용할 수 있는 변수를 생성하기 위해 let-{{templateVariableName}}=\contextProperty\ 속성을 통해 템플릿에서 컨텍스트에 액세스합니다. 컨텍스트 속성 이름이 제공되지 않으면 $implicit 속성을 선택합니다.

다음은 예입니다.

import { Component } from '@angular/core';

@Component({
  template: `
    <ng-container *ngTemplateOutlet="templateRef; context: exampleContext"></ng-container>
    <ng-template #templateRef let-default let-other="aContextProperty">
      <div>
        $implicit = '{{default}}'
        aContextProperty = '{{other}}'
      </div>
    </ng-template>
`
})
export class NgTemplateOutletExample {
  exampleContext = {
    $implicit: 'default context property when none specified',
    aContextProperty: 'a context property'
  };
}

다음은 예제의 출력입니다.

<div>
  $implicit = 'default context property when none specified'
  aContextProperty = 'a context property'
</div>

defaultother 변수는 let-defaultlet-other=\aContextProperty\ 소품에서 제공됩니다.

3단계 – CardOrListViewComponent 리팩터링

CardOrListViewComponent에 유연성을 제공하고 모든 유형의 항목을 표시할 수 있도록 템플릿으로 읽을 두 가지 구조 지시문을 만듭니다. 이러한 템플릿은 카드 및 목록 항목이 됩니다.

다음은 card-item.directive.ts입니다.

import { Directive } from '@angular/core';

@Directive({
  selector: '[cardItem]'
})
export class CardItemDirective {

  constructor() { }

}

그리고 여기 list-item.directive.ts가 있습니다:

import { Directive } from '@angular/core';

@Directive({
  selector: '[listItem]'
})
export class ListItemDirective {

  constructor() { }

}

CardOrListViewComponentCardItemDirectiveListItemDirective를 가져옵니다.

import {
  Component,
  ContentChild,
  Input,
  TemplateRef 
} from '@angular/core';
import { CardItemDirective } from './card-item.directive';
import { ListItemDirective } from './list-item.directive';

@Component({
  selector: 'card-or-list-view',
  templateUrl: './card-or-list-view.component.html'
})
export class CardOrListViewComponent {

  @Input() items: {
    header: string,
    content: string
  }[] = [];

  @Input() mode: string = 'card';

  @ContentChild(CardItemDirective, {read: TemplateRef}) cardItemTemplate: any;
  @ContentChild(ListItemDirective, {read: TemplateRef}) listItemTemplate: any;

}

이 코드는 구조 지시문에서 TemplateRefs로 읽힙니다.

<ng-container [ngSwitch]="mode">
  <ng-container *ngSwitchCase="'card'">
    <ng-container *ngFor="let item of items">
      <ng-container *ngTemplateOutlet="cardItemTemplate"></ng-container>
    </ng-container>
  </ng-container>
  <ul *ngSwitchCase="'list'">
    <li *ngFor="let item of items">
      <ng-container *ngTemplateOutlet="listItemTemplate"></ng-container>
    </li>
  </ul>
</ng-container>

다음은 이 구성 요소의 사용 예입니다.

import { Component } from '@angular/core';

@Component({
  template: `
    <card-or-list-view
        [items]="items"
        [mode]="mode">
      <div *cardItem>
        Static Card Template
      </div>
      <li *listItem>
        Static List Template
      </li>
    </card-or-list-view>
`
})
export class UsageExample {
  mode = 'list';
  items = [
    {
      header: 'Creating Reuseable Components with NgTemplateOutlet in Angular',
      content: 'The single responsibility principle...'
    } // ... more items
  ];
}

이러한 변경으로 CardOrListViewComponent는 이제 제공된 템플릿을 기반으로 카드 또는 목록 양식에 모든 유형의 항목을 표시할 수 있습니다. 현재 템플릿은 정적입니다.

마지막으로 해야 할 일은 템플릿에 컨텍스트를 제공하여 템플릿을 동적으로 만드는 것입니다.

<ng-container [ngSwitch]="mode">
  <ng-container *ngSwitchCase="'card'">
    <ng-container *ngFor="let item of items">
      <ng-container *ngTemplateOutlet="cardItemTemplate; context: {$implicit: item}"></ng-container>
    </ng-container>
  </ng-container>
  <ul *ngSwitchCase="'list'">
    <li *ngFor="let item of items">
      <ng-container *ngTemplateOutlet="listItemTemplate; context: {$implicit: item}"></ng-container>
    </li>
  </ul>
</ng-container>

다음은 이 구성 요소의 사용 예입니다.

import { Component } from '@angular/core';

@Component({
  template: `
    <card-or-list-view
        [items]="items"
        [mode]="mode">
      <div *cardItem="let item">
        <h1>{{item.header}}</h1>
        <p>{{item.content}}</p>
      </div>
      <li *listItem="let item">
        {{item.header}}: {{item.content}}
      </li>
    </card-or-list-view>
`
})
export class UsageExample {
  mode = 'list';
  items = [
    {
      header: 'Creating Reuseable Components with NgTemplateOutlet in Angular',
      content: 'The single responsibility principle...'
    } // ... more items
  ];
}

주목해야 할 흥미로운 점은 구문 설탕에 별표 접두사와 마이크로 구문을 사용한다는 것입니다. 다음과 동일합니다.

<ng-template cardItem let-item>
  <div>
    <h1>{{item.header}}</h1>
    <p>{{item.content}}</p>
  </div>
</ng-template>

그리고 그게 다야! 원래 기능이 있지만 이제 템플릿을 수정하여 원하는 대로 표시할 수 있으며 CardOrListViewComponent의 책임이 줄어듭니다. ngFor와 유사한 first 또는 last와 같은 항목 컨텍스트에 더 많은 것을 추가하거나 완전히 다른 유형의 items를 표시할 수 있습니다. .

결론

이 문서에서는 기존 구성 요소를 가져와 NgTemplateOutlet을 사용하도록 다시 작성했습니다.

Angular에 대해 자세히 알아보려면 연습 및 프로그래밍 프로젝트에 대한 Angular 주제 페이지를 확인하세요.