Angular 동적 컴포넌트 로딩

Dynamic Component Loader

Component 템플릿이 항상 고정되어있는 것은 아닙니다. 응용 프로그램은 런타임에 새로운 Component를 로드할 수 있어야 합니다.
이 포스트는 ComponentFactoryResolver를 사용하여 Component를 동적으로 추가하는 방법을 설명합니다.

live example을 보거나 다운로드할 수 있습니다.

동적 Component 로드하기

다음 예는 동적 배너 광고를 작성하는 방법을 설명합니다.

hero agency는 배너를 통해 순환하는 여러가지 광고로 광고 캠페인을 계획하고 있습니다. 새로운 광고 Component는 여러 팀에서 자주 추가됩니다. 이로 인해 정적 Component 구조의 템플릿을 사용하는 것은 비현실적입니다.

대신 배너 광고 서식 파일의 Component에 고정된 참조없이 새로운 Component를 로드하는 방법이 필요합니다.

Angular는 Component를 동적으로 로드하기 위해 자체 API를 제공합니다.

앵커(Anchor) directive

Component를 추가하기 전에 Anchor 포인트를 정의하여 Angular에 Component를 삽입할 위치를 지정해야합니다.

배너 광고 는 AdDirective라는 핼퍼 Directive를 사용하여 템플릿에 유효한 삽입 지점을 표시합니다.

src/app/ad.directive.ts

1
2
3
4
5
6
7
8
import { Directive, ViewContainerRef } from '@angular/core';
@Directive({
selector: '[ad-host]',
})
export class AdDirective {
constructor(public viewContainerRef: ViewContainerRef) { }
}

AdDirectiveViewContainerRefInject하여 동적으로 추가된 Component를 호스팅할 요소의 뷰 컨테이너에 액세스합니다.

@Directive Decorator에서 selector 이름인 ad-host를 설정합니다. 이 이름으로 요소에 Directive을 적용할 때 사용합니다.

Component 로드하기

대부분의 배너 광고 구현은 ad-banner.component.ts에 있습니다. 이 예제에서 간단히하기 위해 HTML은 @Component Decoratortemplate 프로퍼티에 템플릿 문자열로 지정합니다.

<ng-template> 요소는 방금 만든 Directive를 적용하는 곳입니다. AdDirective를 적용하기 위해 ad.directive.ts에 있는 ad-host selector를 사용합니다. 이 selector를 대괄호없이 <ng-template>에 적용합니다. 이제 Angular는 동적으로 Component를 로드할 위치를 알수 있습니다.

src/app/ad-banner.component.ts (template)

1
2
3
4
5
6
template: `
<div class="ad-banner">
<h3>Advertisements</h3>
<ng-template ad-host></ng-template>
</div>
`

<ng-template>는 추가 출력을 렌더링하지 않으므로 동적 Component에 적합합니다.

Resolving components

ad-banner.component.ts의 메서드를 자세히 살펴보십시오.

AdBannerComponentAdService에서 가져온 AdItem 객체 배열을 입력으로 사용합니다. AdItem 객체는 로드할 Component의 유형과 Component에 바인딩할 데이터를 지정합니다. AdService는 광고 캠페인을 구성하는 실제 광고를 반환합니다.

AdBannerComponentComponent 배열을 전달하면 템플릿에 정적 요소가 없는 광고의 동적 목록이 허용됩니다.

getAds() 메서드를 사용하면 AdBannerComponentAdItem의 배열을 순회하고 loadComponent()를 호출하여 3초마다 새로운 Component를 로드합니다.

src/app/ad-banner.component.ts (excerpt)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
export class AdBannerComponent implements AfterViewInit, OnDestroy {
@Input() ads: AdItem[];
currentAddIndex: number = -1;
@ViewChild(AdDirective) adHost: AdDirective;
subscription: any;
interval: any;
constructor(private componentFactoryResolver: ComponentFactoryResolver) { }
ngAfterViewInit() {
this.loadComponent();
this.getAds();
}
ngOnDestroy() {
clearInterval(this.interval);
}
loadComponent() {
this.currentAddIndex = (this.currentAddIndex + 1) % this.ads.length;
let adItem = this.ads[this.currentAddIndex];
let componentFactory = this.componentFactoryResolver.resolveComponentFactory(adItem.component);
let viewContainerRef = this.adHost.viewContainerRef;
viewContainerRef.clear();
let componentRef = viewContainerRef.createComponent(componentFactory);
(<AdComponent>componentRef.instance).data = adItem.data;
}
getAds() {
this.interval = setInterval(() => {
this.loadComponent();
}, 3000);
}
}

loadComponent() 메서드는 여기서 많은 작업을 수행하고 있습니다. 단계별 살펴보겠습니다. 첫번째로 광고를 선택합니다.

loadComponent()가 광고를 선택하는 방법

loadComponent() 메서드는 몇 가지 계산을 사용하여 광고를 선택합니다.

현재의 currentAddIndex에 1을 더한 값을 AdItem 배열의 길이로 나눈 나머지값을 currentAddIndex 값으로 취합니다. 그런 다음 해당 값을 사용하여 배열에서 adItem을 선택합니다.

loadComponent()가 광고를 선택하면 ComponentFactoryResolver를 사용하여 각 특정 Component에 대한 ComponentFactory를 확인합니다. 그런 다음 ComponentFactory는 각 Component의 인스턴스를 만듭니다.

다음으로 Component의 특정 인스턴스에 있는 viewContainerRef를 대상으로 지정합니다. 이 특정 인스턴스를 어떻게 알 수 있을까요? 그것은 adHost를 참조하고 있기 때문에 알수 있습니다. adHost는 이전에 Angular에 동적 Component를 삽입할 위치를 지정하기 위해 설정한 Directive입니다.

ViewContainerRefAdDirective 생성자에서 주입됩니다. 이 방법이 Directive를 이용한 동적 Component를 호스팅하는 요소에 액세스하는 방법입니다.

Component를 템플릿에 추가하려면 ViewContainerRefcreateComponent()를 호출합니다.

createComponent() 메서드는 로드된 Component에 대한 참조를 반환합니다. 이 참조를 사용하여 프로퍼티에 할당하거나 메서드를 호출하여 Component와 상호 작용할 수 있습니다.

Selector references

일반적으로 Angular 컴파일러는 템플릿에서 참조되는 모든 Component에 대해 ComponentFactory를 생성합니다. 그러나 동적으로 로드된 Component는 런타임에 로드되기 때문에 Component 템플릿에는 selector 참조가 없습니다.

컴파일러가 팩토리를 생성하도록 하려면 동적으로 로드된 ComponentNgModuleentryComponents 배열에 추가 해야합니다.

src/app/app.module.ts (entry components)

1
entryComponents: [ HeroJobAdComponent, HeroProfileComponent ],

AdComponent 인터페이스

배너 광고에서 모든 Component는 공통 AdComponent 인터페이스를 구현하여 API를 표준화하고 데이터를 Component로 전달합니다.

다음은 두가지 샘플 Component와 참조용 AdComponent 인터페이스입니다.

ad.component.ts

1
2
3
export interface AdComponent {
data: any;
}

hero-profile.component.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { Component, Input } from '@angular/core';
import { AdComponent } from './ad.component';
@Component({
template: `
<div class="hero-profile">
<h3>Featured Hero Profile</h3>
<h4>{{data.name}}</h4>
<p>{{data.bio}}</p>
<strong>Hire this hero today!</strong>
</div>
`
})
export class HeroProfileComponent implements AdComponent {
@Input() data: any;
}

hero-job-ad.component.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import { Component, Input } from '@angular/core';
import { AdComponent } from './ad.component';
@Component({
template: `
<div class="job-ad">
<h4>{{data.headline}}</h4>
{{data.body}}
</div>
`
})
export class HeroJobAdComponent implements AdComponent {
@Input() data: any;
}

최종 배너 광고

최종 배너 광고는 다음과 같습니다.


이 내용은 나중에 참고하기 위해 제가 공부하며 정리한 내용입니다.
의역, 오역, 직역이 있을 수 있음을 알려드립니다.
This post is a translation of this original article [https://angular.io/guide/dynamic-component-loader]

참고

공유하기