Angular chapter8 디렉티브

디렉티브란?

전역에서 공통으로 사용하는 기능을 컴포넌트에서 분리하여 구현한 것
디렉티브는 컴포넌트와 달리 자식을 가질 수 없다.
@Directive 데코레이터로 장식된 클래스이다.

Angular에서는 크게 3가지 디렉티브를 제공한다.

  • 컴포넌트 디렉티브(chapter7) : 컴포넌트 템플릿을 표시하기 위한 디렉티브이다.
  • 어트리뷰트 디렉티브 : ngClass, ngStyle 같이 Dom 요소의 어트리뷰트로 사용하여 해당요소의 모양 ,동작을 제어
  • 구조 디렉티브 : ngIf, ngFor, ngSwitch 같이 dom 의 구조를 변경하는 디렉티브

이 외에도 사용자 정의 디렉티브가 있다.

사용자 정의 디렉티브

  1. 디렉티브를 생성한다.
1
ng g d textBlue
  1. 생성된 디렉티브를 수정한다.
text-blue.directive.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Directive, ElementRef } from '@angular/core';

@Directive({
// 디렉티브의 식별자
selector: '[appTextBlue]',
})
export class TextBlueDirective {
// 생성자 함수에 주입된 ElementRef는 컴포넌트의 호스트 요소를 반환한다.
constructor(el: ElementRef) {
// 호스트 요소의 컬러를 반환한다.
el.nativeElement.style.color = 'blue';
}
}

selector 프로퍼티에는 css 셀렉터 문법과 동일하게 디렉티브가 적용되는 조건을 설정 할 수 있다.

생성자(constructor)에 ElementRef 타입의 인스턴스가 주입되었다.

ElementRef 사용 예시

그러나, ElementRef로 dom에 직접 접근하기보다는 Renderer2 클래스로 Dom에 직접 접근하지 않으면서 네이티브 요소를 조작하는 방법을 권장한다.

Renderer2 클래스 사용 예시

  1. app.module.ts에 디렉티브를 추가한다.(보통 cli로 생성하면 자동으로 등록된다.)
app.module.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
...
import { AppComponent } from './app.component';
import { TextBlueDirective } from './text-blue.directive';

@NgModule({
// 이 모듈에 속하는 컴포넌트, 디렉티브, 파이프 선언
declarations: [
AppComponent,
TextBlueDirective
],
..
bootstrap: [AppComponent]
})
export class AppModule { }
  1. 디렉티브를 모듈에 등록하면 컴포넌트에서 사용가능하다. 디렉티브를 적용해본다.
app.component.ts
1
2
3
4
5
6
7
8
9
10
import { Component } from '@angular/core';

@Component({
selector: 'app-root',
template: ` <p appTextBlue>text blue</p> `,
styles: [],
})
export class AppComponent {
title = 'custom-attr-directive';
}

p 태그가 파란색으로 바뀜

이벤트 처리

mouseenter 이벤트가 발생하면 텍스트를 파란색으로, mouseleave 되면 파란색을 취소하도록 디렉티브의 이벤트 기능을 추가한 경우이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { Directive, ElementRef, Renderer2, HostListener } from '@angular/core';

@Directive({
selector: '[appTextBlue]',
})
export class TextBlueDirective {
constructor(private el: ElementRef, private renderer: Renderer2) {
// el.nativeElement.style.color = 'blue';
}

@HostListener('mouseenter') mouseEvent() {
this.textColor('blue');
}

@HostListener('mouseleave') mouseLeave() {
this.textColor(null);
}

private textColor(color: string) {
this.renderer.setStyle(this.el.nativeElement, 'color', color);
}
}
  1. ElementRef에 접근 제한자 private를 추가한다.

  2. @HostListener 데코레이터를 사용하여 호스트 요소의 이벤트에 대한 핸들러를 정의한다.

사용자 정의 구조 디렉티브

  1. 새로운 프로젝트와 새로운 디렉티브를 생성한다.
1
2
3
ng new custom-structure-directive -t -s -S
cd custom-structure-directive
ng g d myNgIf // 디렉티브 생성
  1. templateRef와 viewContainer를 import 한 후 디렉티브를 수정한다.
my-ng-if.component.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';

@Directive({
selector: '[myNgIf]',
})
export class MyNgIfDirective {
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef
) {}

@Input() set myNgIf(condition: boolean) {
if (condition) {
// 호스트 뷰에 ng-template 요소를 추가
this.viewContainer.createEmbeddedView(this.templateRef); // ①
} else {
// 호스트 뷰에서 ng-template 요소를 제거
this.viewContainer.clear(); // ②
}
}
}
app.component.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
import { Component } from '@angular/core';

@Component({
selector: 'app-root',
template: `
<h2 *myNgIf="condition">Hello {{ name }}</h2>
<button (click)="condition = !condition">Click</button>
`,
})
export class AppComponent {
condition = false;
name = 'Lee';
}

1) 프로퍼티 바인딩에 의해 전달된 myNgIf 디렉티브 상태값이 true이면 viewContainerRef의 createEmbeddedView 메소드에 호스트( app.component)의 ng-template으로 래핑된 요소를 의미하는 templateRef 객체를 인자로 전달하여 뷰에 추가한다.

2) 위와 같은 방식으로 clear 하여 뷰에서 완전히 제거한다.

ng-template 디렉티브

ngIf, ngFor, ngSwitch 같은 빌트인 구조 디렉티브는 디렉티브 이름 앞에 붙은 *기호에 의해 ng-template으로 변환된다.

예를 들어
<element *nglf=”expression”>

ngIf 앞에 붙은 * 기호는 아래 구문으로 변환된다.

1
2
3
<ng-template [ngIf]="expression">
<element></element>
</ng-template>

angular 는 *ngIf를 만나면 호스트 요소를 ng-template 디렉티브로 래핑하고 ngIf를 프로퍼티 바인딩으로 변환한다. ng-template 디렉티브는 컴포넌트 템플릿의 일부로서 주석처리 되어 뷰에 랜더링되지 않다가 ngIf 바인딩 값이 true 이면 뷰에 랜더링된다.

이 때 ngIf는 TemplateRef와 ViewContainerRef 의 인스턴스를 사용하여 ng-template 디렉티브로 래핑된 요소를 랜더링하거나 dom에서 제거한다.


위의 예제에서도 myNgIf가 ngIf와 같은 역할을 한다. myNgIf 디렉티브에 바인딩된 값을 평가하여 ViewContainerRef의 createEmbeddedView 메소드 또는 clear 메소드를 호출하여 ng-template의 랜더링 여부를 결정한다.

templateRef와 ViewContainerRef

templateRef는 ng-template 로 래핑된 요소를 가리키는 객체를 생성한다.
ViewContainerRef는 하나 이상의 뷰를 포함시킬 수 있는 컨테이너이다. 즉 ng-template 디렉티브로 래핑된 요소를 dom에 삽입하기 위해 필요한 컨테이너 로써 메소드를 통해 새로운 요소를 DOM에 추가한다.

ng-container 디렉티브

단순히 디렉티브를 선언하기 위해 태그가 필요할 경우 사용한다.
ng-template는 문법을 사용할 수 없지만, ng-container는 *문법(ex)ngIf) 등이 사용가능하다.

일반적으로 angular는 같은 요소에 하나 이상의 구조 디렉티브 사용을 금지하므로, 이런 경우가 필요할 때 ng-cotainer 요소를 사용한다.

1
2
3
4
5
6
<!--같은 요소에 ngFor문과 ngIf 문을 사용하고 싶을 때 -->
<ng-container *ngIf="isShow">
</ng-container *ngFor="let item of itemList">
{{item}}
</ng-container>
</ng-container>

Comentarios

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×