디렉티브
디렉티브의 구체적인 정의가 뭘까? 디렉티브는 템플릿 문법의 일환이 맞다고 봐도 무관할 것 같다. 디렉티브는 엘리먼트에 속성처럼 부여하여 DOM을 제어하기 위한 도구이다. HTML 요소의 특별한 동작이나 모양을 부여할 수 있다.
기본 어트리뷰트 디렉티브
[ngClass]
에 Record 타입으로 된 값을 넘겨주면 해당 값에 맞게 클래스가 지정됨[ngStyle]
은 마찬가지로 오브젝트 타입으로 값을 넘기면 스타일이 지정됨[(ngModel)]
은 Form의 값을 바인딩하기 위해 주로 사용되는데[]
프로퍼티 바인딩과()
이벤트 바인딩이 동시에 필요하면[()]
과 같이 간략하게 표기할 수 있다.- 원레는 이렇게
[ngModel]
(ngModelChange)
각각 바인딩하는데 프로퍼티 바인딩에 Change라는 이름이 붙은 emit 가능한 이벤트가 있다면 위와같이 간략한 표현을 사용하여 바인딩 할 수 있다.
- 원레는 이렇게
🤔
[style]
이것도 오브젝트 타입이 지원되는데[ngStyle]
과 어떤 차이가 있는걸까?
[style]
은 정적인 스타일링에 사용되는 요소이며 상태에 의존하여 스타일링이 변경되어야 하는 경우에는 [ngStyle]
을 사용하면 된다. ng
붙은 속성들은 상태와 속성을 동기하기 위한 요소라고 봐도 될 듯?
기본 구조 디렉티브
*ngIf
에 바인딩되는 값이false
거나null
이면 해당 엘리먼트를 DOM 트리에 추가하지 않는다.*ngFor="let item of items"
와 같이 객체를 순회하여 엘리먼트를 렌더링 할 수 있다. 인덱스를 활용하려면*ngFor="let item of items; let i=index"
와 같이 선언하면 된다.[ngSwitch]
로 프로퍼티를 바인딩하고*ngSwitchCase
&*ngSwitchDefault
로 선언하여 스위치문을 템플릿에서 사용할 수 있다.
구조 디렉티브는 한 엘리먼트에 동시에 사용할 수 없다. 왜냐면 구조 디렉티브는 간소하게 추상화된 동작이므로 동시에 사용하면 Angular가 어떻게 처리 해야할지 이해할 수 없다.
예를들어, 엘리먼트에 *ngIf
를 선언하는 경우, Angular는<ng-template>
엘리먼트를 생성하고 *ngIf
를 프로퍼티 바인딩 형태의 [ngIf]
로 변환한다. 그리고 구조 디렉티브가 적용된 <div>
에 클래스 어트리뷰트를 유지한 채로 <ng-template>
안쪽으로 옮긴다.
<div *ngIf="true">Rendered!</div>
위 구문은 다음과 같이 변경되어 처리된다.
<ng-template [ngIf]="true">
<div>Rendered!<div/>
</ng-template>
엘리먼트 추가 없이 두 가지 구조 디렉티브를 사용해야 한다면 <ng-container>
를 사용하면 된다.
ngNonBindable
ngNonBindable
은 표현식이 평가되는 걸 방지하도록 해준다. 아래와 같은 평가식 같은 표현이 일반 텍스트로 표기된다.
<div ngNonBindable>{{ 1 }}</div>
커스텀 어트리뷰트 디렉티브
CLI로 간단하게 생성이 가능하다.
ng generate directive highlight
기본적으로 아래와 같은 형태로 만들고 ElementRef를 하면 현재 디렉티브를 사용한 엘리먼트를 가져올 수 있다. (🤔 document.quertSelector
된거랑 동일한걸까?)
import { Directive, ElementRef } from '@angular/core';
@Directive({
selector: '[appHighlight]'
})
export class HighlightDirective {
constructor(private el: ElementRef) {
this.el.nativeElement.style.backgroundColor = 'yellow';
}
}
해당 엘리먼트에 이벤트를 반인딩하려면 HostListener
라는 데코레이터를 사용한다.
@HostListener('mouseenter') onMouseEnter() {
// do something
}
@HostListener('mouseleave') onMouseLeave() {
// do something
}
🤔 엘리먼트에
addEventListener
사용하면 어떻게 될까?
사용은 가능하지만 모든 SPA 프레임워크가 그렇듯 당연하게도 권장하지 않는 방법이니 사용하지 말자. (더 이상 이런 생각을 하지마..!)
@Input
데코레이터를 사용해서 값을 전달하도록 만들수도 있다.
@Input() appHighlight = '';
디렉티브 적용은 다음과 같이 selector에 명시한 값을 엘리먼트에 붙혀주면 된다.
<div appHighlight>Some Text</div>
// Input을 적용한 이후에는
<div [appHighlight]="">Some Text</div>
커스텀 구조 디렉티브
import { Directive, Input, TemplateRef, ViewContainerRef } from '@angular/core';
@Directive({ selector: '[appUnless]' })
export class UnlessDirective {
// 무분별한 리랜더링을 막으려고 사용한 듯?
private hasView = false;
constructor(
private templateRef: TemplateRef<unknown>,
private viewContainer: ViewContainerRef) { }
@Input() set appUnless(condition: boolean) {
if (!condition && !this.hasView) {
this.viewContainer.createEmbeddedView(this.templateRef);
this.hasView = true;
} else if (condition && this.hasView) {
this.viewContainer.clear();
this.hasView = false;
}
}
}
이 디렉티브는 아래와 같이 사용하는데.
<p *appUnless="false">
(A) This paragraph is displayed because the condition is false.
</p>
위에서 구조 디렉티브를 설명한 것과 동일하게 아래와 같이 바뀔 것이기 때문에
<ng-template [appUnless]="false">
<p>(A) This paragraph is displayed because the condition is false.</p>
</ng-template>
template
내부에서 참조하는 VDOM을 this.templateRef
로 참조하여 createEmbeddedView
로 랜더링을 실행하는 동작으로 보여진다.
Ghost