Angular chapter9 파이프

파이프

데이터 자체를 변경하기 보다는 화면에 표시하는 형식만 변경하고 싶을때 사용하는 것

app.component.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import {Component} from '@angular/core';

@Component({
selector: 'app-root',
template: `
<h2>{{today}}</h2>
<h2>{{today | date}}</h2>
<h2>{{today | date : 'y년 MM월 dd일 HH시 mm분 ss초'}}</h2>

`,
styles: []
})
export class AppComponent {
title = 'custom-attr-directive';
today = new Date();
}

/**
Fri Jun 12 2020 16:46:36 GMT+0900 (대한민국 표준시)
Jun 12, 2020
2020년 06월 12일 16시 46분 36초
**/

파이프는 템플릿 내에서 원하는 형식으로 데이터를 변환하여 표시하는 기능으로, 원본 데이터 자체가 변경되는 것은 아니다.
파이프는 다음과 같이 사용한다.

파이프 사용법
1
2
3
{{value | pipeName}}
{{value | pipeName : Option : OptionValue}}
{{value | pipeName1 | pipeName2}}

이와 같이 파이프 대상 값 뒤에 연산자(|)를 붙인후 원하는 파이프를 지정한다.
(:)으로 구분하여 파이프 옵션을 지정하거나, 파이프를 추가할 수도 있다.

빌트인 파이프

Angular는 다양한 빌트인 파이프를 제공한다.

파이프 의미
date 날짜 형식 변환
json json 형식 변환
uppercase 대문자 변환
lowercase 소문자 변환
currency 통화 형식 변환
percent 퍼센트 형식 변환
decimal 자리수 형식 변환
slice 문자열 추출
async 비동기 객체 출력
빌트인 파이프 예제
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
import {Component} from '@angular/core';
import {interval} from 'rxjs';
import {take} from 'rxjs/operators';

@Component({
selector: 'app-root',
template: `
<h3>DatePipe</h3>
<p>{{ today | date: 'y년 MM월 dd일 HH시 mm분 ss초' }}</p>

<h3>CurrencyPipe</h3>
<!-- 한국원:통화기호표시:소숫점위 최소 1자리 소숫점아래 1~2 -->
<p>{{ price | currency:'KRW':'symbol':'1.1-2' }}</p>

<h3>SlicePipe : array</h3>
<ul>
<li *ngFor="let i of collection | slice:1:3">{{i}}</li>
</ul>

<h3>SlicePipe : string</h3>
<p>{{ str | slice:0:4 }}</p>

<h3>JsonPipe</h3>
<pre>{{ object | json }}</pre>

<h3>DecimalPipe</h3>
<p>{{ pi | number:'3.5' }}</p>

<h3>PercentPipe</h3>
<p>{{ num | percent:'3.3' }}</p>

<h3>UpperCasePipe</h3>
<p>{{ str | uppercase }}</p>

<h3>LowerCasePipe</h3>
<p>{{ str | lowercase }}</p>

<h3>AsyncPipe</h3>
<p>{{ second | async }}</p>
`
})
export class AppComponent {
today = new Date();
price = 0.1234;
collection = ['a', 'b', 'c', 'd'];
str = 'abcdefghij';
object = {foo: 'bar', baz: 'qux', nested: {xyz: 3}};
pi = 3.141592;
num = 1.3495;
// 1s마다 값을 방출하고 10개를 take한다. (0 ~ 9)
second = interval(1000).pipe(take(10));

/*
* DatePipe
2020년 06월 12일 17시 06분 26초

CurrencyPipe
₩0.12

SlicePipe : array
b
c
SlicePipe : string
abcd

JsonPipe
{
"foo": "bar",
"baz": "qux",
"nested": {
"xyz": 3
}
}
DecimalPipe
003.14159

PercentPipe
134.950%

UpperCasePipe
ABCDEFGHIJ

LowerCasePipe
abcdefghij

AsyncPipe
*/
}

체이닝 파이프

여러 개의 파이프를 조합하여 결과를 출력하는 것을 말한다.

1
2
3
4
5
6
7
8
9
10
11
12
import { Component } from '@angular/core';

@Component({
selector: 'app-root',
template: `
<h3>SlicePipe + UpperCasePipe</h3>
<p>{{ name | slice:4 | uppercase }}</p>
`
})
export class AppComponent {
name = 'Lee ung-mo';
}

위 코드의 slice:4 파이프는 4번째 문자부터 마지막 문자까지를 잘라내고, 그걸 전부 대문자로 출력하는 예제이다.
출력하면 ‘UNG-MO’가 출력된다.

커스텀 파이프

사용자가 직접 파이프를 만들어서 사용할 수도 있다.

  1. 먼저 아무프로젝트나 생성하고 프로젝트 안에 파이프를 추가한다.
1
2
3
ng new pipe-custom -t -s -S
cd pipe-custom
ng g pipe reverse // reverse라는 파이프 파일을 생성함
  1. 프로젝트와 pipe 파일이 생성되면 pipe 파일을 수정한다.
reverse.pipe.ts
1
2
3
4
5
6
7
8
9
10
11
12
import {Pipe, PipeTransform} from '@angular/core';

@Pipe({
name: 'reverse'
})
export class ReversePipe implements PipeTransform {

transform(value = ''): string {
return value.split('').reverse().join('');
}

}

파이프는 @Pipe 데코레이터로 장식된 클래스이고, @Pipe 메타데이터 객체의 name 프로퍼티에 파이프의 식별자를 지정한다.
파이프 클래스는 PipeTransform 인터페이스의 추상 메소드 transform를 구현해야 한다.
transform 메소드는 파이프의 대상인 값(value)와 옵션(args)를 인자로 받고 변환된 값을 반환한다.

  1. angular cli로 pipe를 생성하면 app.module.ts에 자동으로 등록된다.
app.module.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { ReversePipe } from './reverse.pipe';

@NgModule({
declarations: [
AppComponent,
ReversePipe // 자동 등록
],
imports: [
BrowserModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
  1. 커스텀 파이프는 빌트인 파이프와 동일한 방법으로 사용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import {Component} from '@angular/core';

@Component({
selector: 'app-root',
template: `
<input type="text" [(ngModel)]="value"/>
{{value |reverse}}
`,
styles: []
})
export class AppComponent {
value: string;

constructor() {
}
}

파이프와 변화감지

변화감지는 상태의 변화를 감지하여 뷰에 반영하는 것으로 데이터 바인딩이 이에 해당한다.

간단한 todo 예제를 통해 파이프의 변화감지를 알 수 있다.

  1. 프로젝트를 생성한다 (위의 내용반복)
  2. todo 컴포넌트를 생성한다.
  3. limit 파이프를 생성하고 수정한다. (최대 5개까지만 리스트가 보이는 파이프)
limit.pipe.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import {Pipe, PipeTransform} from '@angular/core';
import {Todo} from './todos.component';

@Pipe({
name: 'limit',
pure: false
})
export class LimitPipe implements PipeTransform {

transform(todos: Todo[], limit: number): Todo[] {
return todos.filter((el, i) => i < limit);
}

}
  1. todo 컴포넌트를 수정한다. (limit 파이프 반영)
todo.component.ts
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62

// todo-list 만들기
import {Component, OnInit} from '@angular/core';

export interface Todo {
id: number;
content: string;
completed: boolean;
}

@Component({
selector: 'app-todos',
template: `

<input type="text" #todo>
<button (click)="add(todo.value)">add</button>
<ul>
<!-- 1) 컴포넌트에 limit 파이프를 적용한다. -->
<li *ngFor="let todo of (todos | limit : 5)" (click)="complete(todo.id)" [class.completed]="todo.completed">{{todo.content}}</li>
</ul>

<pre>{{todos | json}}</pre>
`,
styles: [`
.completed {
text-decoration: line-through;
}`]
})
export class TodosComponent {

todos: Todo[] = [
{id: 1, content: 'HTML', completed: false},
{id: 2, content: 'css', completed: false},
{id: 3, content: 'gs', completed: false},
];

constructor() {
}

add(content: string) {

// push 메소드는 원본 배열을 직접 변경하지만, 원본배열의 참조는 밴경되지 않기 때문에 파이프에 의한 변화가 감지되지 않는다.
// this.todos.push({
// id: this.getNextId(),
// content,
// completed: false
// });

// 2) 파이프에 의해 변화 감지가 작동하도록 todos 프로퍼티의 참조가 변경되도록 수정한다.
this.todos = [...this.todos, {id: this.getNextId(), content, completed: false}];

}

complete(id: number) {
this.todos = this.todos.map(todo => todo.id === id ? ({...todo, completed: !todo.completed}) : todo);
}


private getNextId() {
return !this.todos.length ? 1 : Math.max(...this.todos.map(({id}) => id)) + 1;
}
}

2) 이처럼 파이프를 사용하려면 코드 수정이 불가피한데 이를 피하기 위해서 angular에서는 비순수 파이프를 제공한다.

순수 파이프와 비순수 파이프

파이프는 순수 파이프와 비순수 파이프로 분류할 수 있다.
비순수 파이프는 메타데이터 객체의 pure 프로퍼티에 false를 지정한 것이다. pure 프로퍼티를 생략하면 순수 파이프로 동작한다.

비순수 파이프
1
2
3
4
5
...
@Pipe({
name :'limit',
pure : false //비순수 파이프
})

위 limit 파이프를 비순수 파이프로 변경하면 push를 사용하여도 변화감지가 작동한다.
하지만 비순수 파이프를 사용하면 빈번하게 파이프가 호출되어 퍼포먼스 측면에서 좋지 않으므로 주의한다.

순수 파이프는 기본 자료형의 값 또는 객체 참조의 변경과 같은 순수한 변경만을 감지하고 파이프를 실행한다.
angular는 퍼포먼스를 위해 객체 내부의 변경은 무시하며 순수 파이프를 실행하지 않는다.

Comentarios

Your browser is out-of-date!

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

×