화면 구성
먼저 todo.vo.ts 에 isEdited 변수를 하나 추가한다.
1 |
isEdited: boolean; |
수정 버튼을 누르면 수정 템플릿으로 전환이 되어야 한다. 수정 템플릿에서는 완료, 미완료가 체크박스로 todo는 input 박스로 바인딩되어야 한다. angular에서는 이런 경우에 사용할 수 있는 ng-template이 제공된다. 이제 반복문이 ng-template로 들어간다. 여기서 사용되는 규칙은 아래와 같다.
ngFor let-item [ngForOf]=”todoList”
ng-template 안에는 일반 템플릿과 수정 템플릿 두가지가 존재하고 isEdited 변수가 true이면 수정 템플릿으로 false이면 일반 템플릿을 보여준다.
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 |
<tbody> <ng-template ngFor let-item [ngForOf]="todoList"> <tr *ngIf="!item.isEdited"> <td>{{item.isFinished ? '완료' : '미완료'}}</td> <td>{{item.todo}}</td> <td>{{item.created}}</td> <td>{{item.updated}}</td> <td> <button class="btn btn-success btn-sm" (click)="save(item)">수정</button> <button class="btn btn-danger btn-sm ml-1" (click)="remove(item)">삭제</button> </td> </tr> <tr *ngIf="item.isEdited"> <td> <input type="checkbox" [(ngModel)]="item.isFinished"> </td> <td [class.todo_canceled]="item.isFinished"> <input [(ngModel)]="item.todo"> </td> <td>{{item.created}}</td> <td>{{item.updated}}</td> <td> <button class="btn btn-info btn-sm" (click)="modify(item)">저장</button> <button class="btn btn-warning btn-sm ml-1" (click)="restore(item)">취소</button> </td> </tr> </ng-template> </tbody> |
수정 버튼을 누르면 수정 템플릿으로 전환하고 취소 버튼을 누르면 일반템플릿으로 전환되도록 함수를 구현해보자.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
/** * 수정 템플릿으로 전환 * @param {TodoVo} item */ save(item: TodoVo) { item.isEdited = true; } /** * 일반 템플릿으로 전환 * @param {TodoVo} item */ restore(item: TodoVo) { item.isEdited = false; } |
수정, 취소를 누르면 템플릿이 전환되는지 테스트해보자.
수정, 취소 로직 구현
수정 템플릿에서 값을 입력 한 다음 취소를 하면 기존 값으로 값이 돌아오지를 못한다. 기존 값으로 복원하기 위해서는 수정 템플릿으로 전환전에 기존 값을 저장하고 있어야 한다. 그런데, 실제로는 한줄이 아니라 여러줄에서 수정을 하는게 가능해야 하므로 여러건의 데이터를 저장하귀 위해서 Map이라는 es6에 새로나온 자료구조 형태를 사용해보겠다.
todo_id는 서버에서 생성한 primary key이므로 유일한 키이다. 이 키 값으로 데이터를 저장하는 Map 데이터 변수를 정의하자.
1 |
tempTodoList: Map<number, TodoVo> = new Map<number, TodoVo>(); |
save함수에서는 기존 객체 데이터를 deep copy를 해서 Map에 저장한다. 만일 shallow copy를 하게 되면 같은 메모리 주소를 참조하게 되어서 올바르게 동작하지 않는다.
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 |
/** * 수정 템플릿으로 전환 * @param {TodoVo} item */ save(item: TodoVo) { item.isEdited = true; // 새로운 객체를 생성하고 값을 복사하는 deep copy를 수행 let tempTodo = new TodoVo(); Object.assign(tempTodo, item); console.log(tempTodo); this.tempTodoList.set(item.todo_id, tempTodo); } /** * 일반 템플릿으로 전환 * @param {TodoVo} item */ restore(item: TodoVo) { item.isEdited = false; let tempTodo = this.tempTodoList.get(item.todo_id); // 기존에 저장된 isEdited는 true이기 때문에 false를 다시 추가하였다. Object.assign(item, tempTodo, {isEdited: false}); console.log(item); } |
deep copy를 하는 방법은 es5에서 사용되는 Object.assign(source, target1, target2, …) 방법과 es6에 새로나온 스프레드 연산자를 이용하는 방법이 있다. 여기서는 es5 방식으로 deep copy를 하였다. 입력 박스를 수정후 취소를 누르면 기존 값이 수정되는지 테스트해보자. 여러건을 동시에 수정 취소 테스트를 해보아야 한다.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// shallow copy var obj = { a: 1 }; var copy = obj; obj.a = 2; console.log(obj); console.log(copy); // deep copy var obj = { a: 1 }; var copy = Object.assign({}, obj); obj.a = 2; console.log(obj); console.log(copy); |
수정 로직 서비스 구현
서버에 수정하는 서비스를 구현한다. 보내는 데이터는 항상 json 타입이므로 헤더에 content-type을 다시 정의하면 데이터가 중복이다. 그러므로 멤버 변수로 선언한다.
1 2 3 4 5 |
headers = new HttpHeaders(); constructor(private http: HttpClient) { this.headers.append('Content-Type', 'application/json'); } |
modifyTodo 함수를 구현한다.
1 2 3 |
modifyTodo(todo: TodoVo): Observable<TodoVo> { return this.http.put<TodoVo>(environment.HOST + '/api/todo', todo, {headers: this.headers}); } |
수정 로직 구현
1 2 3 4 5 6 7 8 9 |
modify(item: TodoVo) { this.heroService.modifyTodo(item) .subscribe(body => { // 기존 객체에 새로온 객체의 퍼라퍼티를 복사한다. Object.assign(item, body); // 편집상태에서 일반상태로 전환 item.isEdited = false; }); } |
삭제 서비스 구현
Rest API로 먼저 테스트해보자. 리턴되는 형태는 result와 msg 변수를 포함하는 json 객체이다. 먼저 리턴값을 저장하는 result.vo.ts를 정의한다.
1 2 3 4 |
export class ResultVo { result: number; msg: string; } |
서비스를 구현한다. es6에 새로나온 템플릿 스트링 문법을 이용한다. 양쪽끝에 빽틱 기호를 쓰고 ${변수} 형태로 사용한다.
1 2 3 |
removeTodo(todo_id: number): Observable<ResultVo> { return this.http.delete<ResultVo>(environment.HOST + `/api/todo?todo_id=${todo_id}`); } |
삭제 로직 구현
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
remove(item: TodoVo) { const result = confirm(item.todo + '을(를) 삭제하시겠습니까?'); if (result) { this.heroService.removeTodo(item.todo_id) .subscribe(body => { if (body.result === 0) { let index = this.todoList.findIndex(data => { return item.todo_id === data.todo_id ? true : false; }); this.todoList.splice(index, 1); } }); } } |