부모 Component의 직접적인 자식 요소 제어
이번 포스트는 부모 Component에서 자식 요소에 직접 접근하는 방법에 대해서 알아보겠습니다. 이전 포스트에서 @Input
decorator를 이용해 부모 Component에서 자식 Component로 데이터를 전달하는 방법에 대해서 알아보았는데 이번에는 약간 다릅니다.
부모 Component는 자식 Component 객체뿐만 아니라 자식으로 포함된 Directive
에 직접 접근할 수 있고 또한 Component가 Rendering하는 View자체에 직접 접근할 수 있습니다.
하지만 이런 접근 방법이 항상 좋은건 아닙니다. 오히려 좋지 않은 현상이 발생하게 됩니다. 예를 들어 Component가 직접적으로 DOM에 접근해서 제어하는 코드를 작성한다고 가정해 보죠. 일단 간단하게 프로그램을 구현할 수 있으나 나중에 Component의 View가 변경되면 Component에서 처리하는 부분도 당연히 그에 맞게 바뀌어야 합니다. Component의 재사용성과 유지보수성에 문제가 생길 여지가 있습니다.
그렇기 때문에 이런 직접적인 접근방식은 꼭 필요한 경우가 아니면 지양하는 것이 좋습니다.
그럼 천천히 한번 알아보도록 하죠.
@ViewChild, @ViewChildren Decorator
부모 Component template안에 위치한 모든 자식 요소들을 ViewChild
라고 합니다. 이 ViewChild안에는 자식 Component 객체뿐만 아니라 Component가 Rendering하는 View의 DOM 그리고 Directive가 포함됩니다.
자식 Component객체에 직접 접근하는 방법부터 살펴보도록 하겠습니다.
자식 Component 객체에 직접 접근하려면 @ViewChild
decorator를 이용하시면 됩니다. 조건에 부합되는 객체 1개를 찾게되고 그에 대한 property를 지정해서 사용할 수 있습니다. 만약 @ViewChildren
을 이용하면 조건에 부합되는 객체를 모두 찾게 되고 QueryList
형태로 객체들의 집합을 얻을 수 있습니다. QueryList는 실제 배열이 아니기 때문에 toArray()
method를 이용해 배열을 얻어내 이용할 수 있습니다.
그럼 간단한 예를 가지고 알아보도록 하죠.
부모 Component에 초기화버튼을 하나 만들어서 해당 버튼을 누르면 Client가 선택한 도서 종류와 입력된 키워드를 초기화 시키는 작업을 해 보도록 하겠습니다.
먼저 초기화버튼을 만들어야 하니 book-search-main.component.html
부터 수정해야 합니다.
<div class="bookSearch-outer">
<div class="d-flex align-items-center p-3 my-3 text-white-50 bg-purple rounded box-shadow">
<img class="mr-3" src="assets/images/search-icon.png" alt="" width="48" height="48">
<div class="lh-100">
<h5 class="mb-0 text-white lh-100">Search Result : </h5>
</div>
</div>
<div class="example-container">
<mat-form-field>
<mat-select placeholder="도서종류"
#bookCategorySelect
[(ngModel)]="selectedValue"
(ngModelChange)="changeValue(bookCategorySelect.value)">
<mat-option *ngFor="let category of bookCaterory"
[value]="category.value">
</mat-option>
</mat-select>
</mat-form-field>
<button mat-raised-button color="primary"
(click)="clearCondition()">검색 초기화</button>
</div>
<div>
<app-search-box [bookCategory]="displayCategoryName"
(searchEvent)="changeTitleBar($event)"></app-search-box>
</div>
<div>
<app-detail-box></app-detail-box>
</div>
<div>
<app-list-box></app-list-box>
</div>
</div>
검색 초기화 버튼을 생성하고 해당 버튼을 클릭하면 clearCondition()
method가 호출되도록 처리했습니다.
다음은 부모 Component인 book-search-main.component.ts
파일입니다. clearCondition()
method를 작성해야하고 해당 method안에서 자신의 검색에 관련된 사항을 초기화하고 자식 Component를 찾아 자식 Component의 property를 초기화시키는 작업을 진행합니다.
import {Component, OnInit,
ViewChild, ViewChildren, QueryList } from '@angular/core';
import { SearchBoxComponent } from "../search-box/search-box.component";
@Component({
selector: 'app-book-search-main',
templateUrl: './book-search-main.component.html',
styleUrls: ['./book-search-main.component.css',
'./offcanvas.css']
})
export class BookSearchMainComponent implements OnInit {
selectedValue = null;
displayCategoryName = null;
bookCaterory = [
{value: 'all', viewValue: '국내외도서'},
{value: 'country', viewValue: '국내도서'},
{value: 'foreign', viewValue: '국외도서'}
];
searchTitle = null;
constructor() { }
ngOnInit() {
}
changeValue(category: string): void {
for(let element of this.bookCaterory ) {
if(element.value == category) {
this.displayCategoryName = element.viewValue;
}
}
}
changeTitleBar(searchInfo) : void {
this.searchTitle = `${searchInfo.keyword} ( ${searchInfo.category} )`;
}
@ViewChild(SearchBoxComponent) searchComp: SearchBoxComponent;
@ViewChildren(SearchBoxComponent) searchCompArr: QueryList<SearchBoxComponent>;
clearCondition(): void {
this.selectedValue = null;
this.searchTitle = null;
/*
@ViewChild를 사용할 경우
this.searchComp._bookCategory = null;
this.searchComp.keyword = null;
*/
// @ViewChildren을 사용할 경우
this.searchCompArr.toArray()[0]._bookCategory = null;
this.searchCompArr.toArray()[0].keyword = null;
}
}
부모 Component와 자식 Component가 데이터를 공유하는게 아니라 부모 Component가 직접 자식 Component 객체를 제어하는 방식입니다.
Component가 Rendering하는 View의 DOM에 직접 접근
@ViewChild와 @ViewChildren을 이용하면 자식 Component의 객체뿐 아니라 Component가 rendering하는 View의 DOM에 직접 접근할 수 있습니다. 이전에 나왔던 Template Reference Variable을 이용해서 Component가 DOM에 접근하는 것이죠.
우리 예제에 딱히 필요하진 않지만 이해를 돕기 위해 버튼 하나를 더 추가해 어떻게 사용하는지 살펴보겠습니다.
book-search-main.component.html
을 수정해 버튼을 하나 더 추가합니다.
...
...
<h5 #resultStatus class="mb-0 text-white lh-100">Search Result : </h5>
...
...
...
<button mat-raised-button color="primary"
(click)="changeDOM()">DOM 직접 변경</button>
...
...
일부만 표시했습니다. 결과를 표시하는 영역에 Template Reference Variable #resultStatus
을 지정했습니다. 그리고 버튼을 하나 추가했구요. 해당 버튼을 클릭하면 changeDOM()
method가 호출되겠네요.
다음은 book-search-main.component.ts
파일 내용입니다.
import {Component, OnInit,
ViewChild, ViewChildren, QueryList,
ElementRef } from '@angular/core';
import { SearchBoxComponent } from "../search-box/search-box.component";
@Component({
selector: 'app-book-search-main',
templateUrl: './book-search-main.component.html',
styleUrls: ['./book-search-main.component.css',
'./offcanvas.css']
})
export class BookSearchMainComponent implements OnInit {
selectedValue = null;
displayCategoryName = null;
bookCaterory = [
{value: 'all', viewValue: '국내외도서'},
{value: 'country', viewValue: '국내도서'},
{value: 'foreign', viewValue: '국외도서'}
];
searchTitle = null;
constructor() { }
ngOnInit() {
}
changeValue(category: string): void {
for(let element of this.bookCaterory ) {
if(element.value == category) {
this.displayCategoryName = element.viewValue;
}
}
}
changeTitleBar(searchInfo) : void {
this.searchTitle = `${searchInfo.keyword} ( ${searchInfo.category} )`;
}
@ViewChild(SearchBoxComponent) searchComp: SearchBoxComponent;
@ViewChildren(SearchBoxComponent) searchCompArr: QueryList<SearchBoxComponent>;
clearCondition(): void {
this.selectedValue = null;
this.searchTitle = null;
/*
@ViewChild를 사용할 경우
this.searchComp._bookCategory = null;
this.searchComp.keyword = null;
*/
// @ViewChildren을 사용할 경우
this.searchCompArr.toArray()[0]._bookCategory = null;
this.searchCompArr.toArray()[0].keyword = null;
}
@ViewChild('resultStatus') resultToolbar: ElementRef;
changeDOM(): void {
this.resultToolbar.nativeElement.onclick = function() {
alert('DOM을 직접 제어할 수 있어요!!');
};
this.resultToolbar.nativeElement.innerHTML = "클릭해보세요!!";
}
}
아래부분에 resultStatus
Template Reference Variable을 이용해서 해당 Element의 Reference를 획득하는 부분을 잘 보시면 됩니다. 이렇게 ElementRef
type의 객체를 획득하면 nativeElement
속성으로 직접 제어할 수 있습니다.
이번 포스트에서는 @ViewChild와 @ViewChildren을 이용해 자식 Component의 객체를 직접 제어하거나 rendering된 View의 DOM에 직접 접근해서 제어하는 방법에 대해서 살펴보았습니다. 다음 포스트는 Angular에서 Content Projection
이라고 불리는 부분에 대해서 살펴보도록 하겠습니다.
'Web Programming > Angular - TypeScript' 카테고리의 다른 글
Angular Service (0) | 2018.08.28 |
---|---|
Angular Content Projection 데이터공유 (0) | 2018.08.28 |
Angular @Output 데이터공유 (0) | 2018.08.28 |
Angular @Input 데이터공유 (0) | 2018.08.28 |
Angular Material Table (0) | 2018.08.28 |