2019년 2월 12일 21:02

Angular ElementRef, ViewRef, ComponentRef, ViewContainerRef

ElementRef

엘리먼트 하나를 나타내는 간단한 객체입니다.

class ElementRef {
  constructor(nativeElement: any)
  nativeElement: any
}

역할

특정 엘리먼트를 기반으로 작동해야 하는 어떤 기능에 대해 대상 엘리먼트를 지정해주는 역할을 합니다.

특징

  • 다양한 환경(플랫폼)에서 실행될 수 있는것을 염두에 둔 추상화된 객체, 플랫폼에 따라 nativeElement 는 다른 객체가 됩니다.
  • 생성자에 주입되는 ElementRef 는 루트 엘리먼트

다른 플랫폼에서도 실행될 가능성을 염두에 둔 부분이기 때문에 Renderer2 의 메서드를 이용하여 처리하는것이 좋습니다.

ViewRef

class ViewRef extends ChangeDetectorRef {
  destroy(): void
  destroyed: boolean
  onDestroy(callback: Function): any
}

역할

UI의 생성과 소멸을 다루는 최소 단위로 다시말해, Angular 애플리케이션의 UI 는 ViewRef 들의 조합으로 만들어집니다.

renderer2 를 이용해 엘리먼트를 만들면 Angular 의 관리를 받지않는 상태가 됩니다.

Angular 의 UI가 해석과 관리를 받으려면 ViewRef 를 생성하는 형태로 UI 를 추가해야합니다. 먼저, <ng-template> 태그를 TemplateRef 객체로 래핑한뒤 ViewContainerRef.createEmbeddedView() 의 파라미터로 넘겨주면 해당 템플릿이 생성되고 생성된 템플릿은 return 되어 ViewRef 객체로 옵니다.

// ...
@ViewChild(TemplateRef) template;
constructor(private vcr: ViewContainerRef) {}
ngOnInit() {
  this.vcr.createEmbeddedView(this.vcr.createEmbeddedView(this.template));
}

특징

  • viewRef 는 생성자에 주입받을 수 없습니다.
  • ChangeDetectorRef 를 상속받고 있어 뷰 간의 상하 구조를 변경감지를 위한 구조로 사용합니다.

ComponentRef

생성된 컴포넌트를 나타냅니다.

class ComponentRef {
  location: ElementRef
  injector: Injector
  instance: C
  hostView: ViewRef
  changeDetectorRef: ChangeDetectorRef
  componentType: Type<any>
  destroy(): void
  onDestroy(callback: Function): void
}

동적 컴포넌트 생성

  1. ComponentFactoryResolver 를 통해 생성하려는 컴포넌트의 ComponentFactory 를 가져옴
  2. ComponentFactory 를 ViewContainerRef.createComponent 에 전달
export class AppComponent {
  constructor(
    private vcr: ViewContainerRef,
    private resolver: ComponentFactoryResolver
  ) {}

  myMethod() {
    const factory = this.resolver.resolveComponentFactory(MyComponent)
    this.vcr.createComponent(factory)
  }
}
@NgModule({
  declarations: [ AppComponent, MyComponent ],
  entryComponents: [ MyComponent ],
})

생성하려는 컴포넌트는 NgModule 의 entryComponents에 등록을 해야합니다.

특징

  • ComponentRef.instance 속성을 통해 생성된 컴포넌트의 메서드를 호출할 수 있습니다.
  • ComponentRef 또한 생성자에 주입할 수 없습니다.

ViewContainerRef

class ViewContainerRef {
  element: ElementRef
  injector: Injector
  parentInjector: Injector
  clear(): void
  get(index: number): ViewRef | null
  length: number
  createEmbeddedView<C>(
    templateRef: TemplateRef<C>,
    context?: C,
    index?: number
  ): EmbeddedViewRef<C>
  createComponent<C>(
    componentFactory: ComponentFactory<C>,
    index?: number,
    injector?: Injector,
    projectableNodes?: any[][],
    ngModule?: NgModuleRef<any>
  ): ComponentRef<C>
  insert(viewRef: ViewRef, index?: number): ViewRef
  move(viewRef: ViewRef, currentIndex: number): ViewRef
  indexOf(viewRef: ViewRef): number
  remove(index?: number): void
  detach(index?: number): ViewRef | null
}

역할

동적으로 뷰를 만들고 추적합니다. ViewRef 와 ComponentRef 를 만드는 역할을 수행합니다.

  • 뷰를 생성하는 메서드: createEmbeddedView, createComponent
  • 뷰의 위치를 이동하는 메서드: get, move, insert,
  • 추적: length (ViewContainerRef가 관리하고 추적하는 ViewRef 의 개수)

특징

ViewChild 로 ViewContainerRef 가져오기

@ViewChild 는 기본적으로 ElementRef 를 가져오는데 read 속성으로 ViewContainerRef 를 기술하여 ViewContainerRef를 가져올 수 있습니다.

...
@ViewChild('div', { read: ViewContainerRef }) vc1: ViewContainerRef;

생성자를 통해 주입

생성자를 통해 주입하게 되면 루트 엘리먼트 객체가 됩니다.

...
constructor(
  private vcr: ViewContainerRef
) {
  console.log(this.vcr) // 루트 엘리먼트
}

Sibling Element

ViewContainerRef 를 통해 생성한 뷰는 ViewContainerRef 의 Sibling 엘리먼트로 위치합니다.

참고문서

©2022 heecheolman

Built with Gatsby