기본 콘텐츠로 건너뛰기

[Angular2] 컴포넌트에 대해서

[Angular2] 컴포넌트에 대해서

Component

angular2의 component에 대해서 설명합니다.

Introduce

angular2 는 기본적으로 CBD(Component Based Development)이다, 여기서 Component는 W3C의 Web Component를 의미하며 명세(Spec)에 따른 배포, 조립이 가능한 독립 구성 단위이다.

컴포넌트는 명세(Spec)를 정확히 구현해야한다.

컴포넌트는 배포 및 다른 컴포넌트에서 사용 가능해야한다.

Structure

컴포넌트 파일은 크게 3가지 영역으로 구분할 수 있다.

import : 관련 라이브러리를 호출한다.

@Component : 장식자(Decorator)로서 컴포넌트의 기본적인 메타데이터를 정의한다.

Class : 컴포넌트의 상태를 정의할 클래스이다.

Import

Angular 라이브러리의 모듈을 호출할 수 있다. angular 팀은 다른 모듈과 구분짓기 라이브러리의 접두사로 @ 를 붙혔다.

import { Component } from '@angular/core'

사용자 정의 모듈은 아래와 같이 상대경로로 호출한다. webpack.config.js 와 같은 번들러 설정을 통해 root context를 지정하여 절대경로로도 호출가능하다.

import { LoggerService } from './logger.service'

@Component

스프링의 어노테이션처럼 Angular도 컴포넌트의 메타데이터를 설정할 수 있는 @Component 장식자(Decorator)를 제공한다.

@Component({ selector: 'hello-world', template: 'template', styles: ['div { background: blue; }'] }) export class HelloWorld { }

selector

위 예의 @Component 의 selector 은 다른 컴포넌트에서 형식으로 사용할 수 있다.

template

템플릿을 속성은 두 가지가 있다.

template : 직접 정의

templateUrl: 외부 템플릿 파일, 상대경로

Class

클래스는 템플릿과 관련된 로직을 처리한다.

서비스를 통한 데이터 송수신

데이터 바인딩

뷰 액션에 대한 이벤트 핸들링

사용하기

컴포넌트를 사용하려면 아래와 같은 과정을 거친다.

컴포넌트 생성

컴포넌트 모듈에 등록

모듈에 등록되지 않는 컴포넌트는 다른 컴포넌트에서 참조하거나 사용할 수 없다. 반드시 의존하는 컴포넌트는 모듈에 등록해줘야한다.

컴포넌트 생성

파일명에 대한 명명규칙에 대한 내용은 프로젝트나 개발자의 취향이지만 기본적으로 아래와 같은 룰을 가진다.

name.[component, interface, module].ts : 첫 이름은 대변할 이름이고 중간 이름은 구분자이다.

two-word.component.ts : 이름이 만약 2가지 단어 이상일 땐, 중간에 대쉬(-)를 붙혀 구분한다.

컴포넌트 파일을 위 Decorator 부분의 샘플과 같이 만들 수 있다.

컴포넌트 모듈 등록

컴포넌트를 사용하려면 모듈에 생성한 컴포넌트를 등록해야한다. @NgModule 의 declarations 속성에 임포트한 컴포넌트를 등록한다.

import { MyComponent } from './my.component'; @NgModule({ declarations : [ MyComponent ], ... })

상호작용

여러 컴포넌트는 각각의 독립적인 개체로써 서로간의 상호작용이 가능하다.

계층 (Hierarchy)

npm 라이브러리의 의존성, 메이븐의 dependency hierarchy 또는 html element처럼 컴포넌트도 동일하게 parent와 child가 있다.

child component

import { Component } from '@angular/core'; @Component({ selector: 'child', template: '자식', styles: ['div { border: 2px solid black; margin: 10px; padding: 10px; }] }) export class ChildComponent { }

parent component

import { Component } from '@angular/core'; @Component({ selector: 'parent', template: ` 부모 `, styles: ['div { border: 2px solid red; margin: 10px; padding: 10px; }'] }) export class ParentComponent { }

module

import { ChildComponent } from './child.component'; import { ParentComponent } from './parent.component'; @NgModule({ declarations : [ ParentComponent, ChildComponent ], ... bootstrap: [ParentComponent] })

먼저 component 생성 후 사용할 module에 등록하는 절차로 사용할 수 있다. 모듈에 등록한 후에 잘 보면 ParentComponent 에는 ChildComponent 와 관련된 선택자(Selector)만 존재할 뿐 직접적인 import 하지 않았음에도 child 선택자를 통해서 자식 컴포넌트로써 사용했다.

@Input을 통한 부보 -> 자식 데이터 전달

컴포넌트는 독립적인 개체이기 때문에 하위 컴포넌트가 상위 컴포넌트로부터 데이터를 전달받아야하는 경우도 있다. 물론 직접적인 전달이 아닌 service 또는 event bus, data store 등을 통해서도 가능하다.

만약 부모 컴포넌트에서 자식 컴포넌트에게 아래와 같이 값을 전달하고 싶다고 가정한다.

@Component({ selector: 'parent', template: ` 부모 // getter (in typescript) ` }) export class ParentComponent { data = 1; data2() { return "data2"; } data3 = ['one', 'two', 'three']; static data5 = "Five"; get data6() { return ParentComponent.data5; } }

위와 같이 [] 기호를 통해 자식 컴포넌트에게 데이터를 바인딩할 수 있었다. 자식 컴포넌트는 부모가 전달한 데이터를 아래와 같이 받을 수 있다.

```ts import { Component, Input } from '@angular/core';

@Component({ selector: 'child', template: 자식 {{data1}}, {{data2}}, {{data3}}, {{data4}}, {{data5}}, {{data6}} }) export class ChildComponent { @Input() data1: number; @Input() data2: string; @Input() data3: string[]; @Input() data4: number; @Input() data5: string; @Input() data6: string; }

위와 같이 받을 수 있지만 `@Component`의 `inputs` 속성을 통해서도 받을 수 있다. ```ts import { Component } from '@angular/core'; @Component({ selector: 'child', template: ` 자식 {{data1}}, {{data2}}, {{data3}}, {{data4}}, {{data5}}, {{data6}} `, inputs: ['data1', 'data2', 'data3', 'data4', 'data5', 'data6'] }) export class ChildComponent { data1: number; data2: string; data3: string[]; data4: number; data5: string; data6: string; }

Custom Event(@Output + EventEmitter )를 통한 자식 -> 부모 데이터 전달

위의 @Input , inputs 는 모두 부모가 자식에게 데이터를 전달하는 방식의 데이터 바인딩이다. 자식도 부모에게 데이터를 전달해야하는 경우가 있는데 이런 경우에 이벤트를 통해서 해결할 수 있다.

import { Component, EventEmitter, Output } from '@angular/core'; @Component({ selector: 'child', template: ` 부모에게 이벤트 전달 ` }) exprot class ChildComponent { active:boolean = false; @Output() outputProperty = new EventEmitter(); emitEvent() { // 위 button 객체의 (click) 이벤트 핸들러로 사용되는 함수이다. active = !active; outputProperty.emit(active); // 이 컴포넌트의 outputProperty라는 custom event를 발생(emit) 시킨다. } }

EventEmitter 타입인 속성을 @Output 을 이용해 상위 컴포넌트에 공개한다.

import { Component } from '@angular/core'; @Component({ selector: 'parent', template: ` 부모 count : {{countClick}} ` }) export class ParentComponent { countClick:number = 0; outputEvent(active: boolean) { // handling child custom event if(active) this.countClick++; // increase count if active is true } }

부모(상위) 컴포넌트는 자식 컴포넌트가 공개한 이벤트에 한해서 이벤트 핸들러를 바인딩할 수 있고, 자식컴포넌트의 EventEmitter 속성의 emit 메서드를 통해 이벤트를 발생시킬 수 있다. 이때, 기존의 상위 컴포넌트에서 바인딩한 메서드들이 실행한다.

추가로 @Input, @Output 장식자는 모두 one-way data binding이다. [] 는 부모 -> 자식, () 는 자식 -> 부모, [()] 는 양방향 바인딩으로 보면된다.

자식 엘리먼트의 호출과 탐색

@ViewCHild 를 이용해 자식의 상태가져오기

사용방법은 @ViewChild(ClassName) variable: ClassName; 과 같으며 현재 DOM에 존재하는 첫 번째 지시자(컴포넌트)의 내부 상태나 정보를 가져온다.

이때 DOM이 모두 로드된 이후에 정상적으로 상태를 가져올 수 있으므로 아래 예와 같이 setTimeout 함수를 이용해 이벤트 큐의 맨 끝에 상태 접근에 관련한 로직(람다함수)을 위치하도록 한다.

@ViewChild(Item) set item(v: Item){ setTimeout(() => this.status = v.status, 0); }

전체 코드는 아래와 같다.

@Directive({ selector: 'item'}) export class item { @Input() status: boolean; } @Component({ selector: 'parent', template: ` 선택 isShow : {{isShow}} status : {{status}}` }) export class ParentComponent { @ViewChild(Item) set item(v: Item) { setTimeou8t(() => this.status = v.status, 0); } isShow: boolean = true; status: boolean; toggle() { this.isShow = !this.isShow; } }

첫 번째 자식컴포넌트가 임에도 불구하고 결과는 status 가 true 이다. 기존에 DOM에 있는 첫 번째 자식 컴포넌트라고 했으므로 isShow 가 true 이므로 렌더링 되지 않아 @ViewChild 로 받아오는 상태는 두 번째 컴포넌트가 된다. 또한 버튼을 누르면 상태 값이 변해 템플릿 중 item 컴포넌트가 재 렌더링하게 되는데, 이 경우 DOM의 첫 번쨰 자식 컴포넌트가 바뀌게 되므로 @ViewChild 로 인해 item 속성은 계속해서 다시 set 해주게 되어 마치 isShow 의 값과 동일하게 토글되는 것처럼 보임을 확인할 수 있다.

@ViewChild 를 통해 접근하는 컴포넌트는 공개된 속성에 대해서 모두 접근이 가능하다.

@ViewChildren 을 이용해 그룹 상태 얻기

템플릿에 button 이 여러개 존재하듯이 @ViewChild 와 다르게 @ViewChildren 은 여러 지시자의 상태를 한번에 얻을 수 있다.

@ViewChildren('설명레이블' or ClassName) children: QuaryList;

Component 의 지시자가 component 이고 cmp 라는 참조 변수가 붙어있다고 아래와 같이 가정한다면

이렇게 사용할 수 있다.

@ViewChildren('cmp') children: QueryList;

각각의 참조변수 이름이 다른 형태도 아래와 같이 사용할 수 있다.

... @ViewChildren('cmp1,cmp2,cmp3') children: QueryList;

또는 해당 컴포넌트 타입을 모두 참조하고 싶다면 label 대신 아래와 같이 쓸 수 있다

@ViewChildren(Component) children: QueryList;

다만 이 역시 @ViewChild 와 마찬가지로 DOM이 모두 렌더링된 뒤에 참조가 가능하므로 만약 참조 즉시 무엇인가 해야하는 코드가 필요하다면, ngAfterViewInit 에 정의해야 한다.

@Component({...}) export class Component { @ViewChild(...) children: QueryList<...>; ngAfterViewInit() { this.children.toArray().forEach(child => child.something()); } }

@ContentChild 이용해 컨텐츠의 상태 얻기

@ContentChild 는 콘텐츠 DOM을 탐색해 상태를 저장한다. 먼저, 콘텐츠 DOM은 아래와 같은 예에서 확인할 수 있다.

이건 컨텐츠야! // component 내부에 있는 모든 것을 컨텐츠라고 보면 된다.

즉 특정 컴포넌트 내에 포함된 모든 컴포넌트, 태그 등을 컨텐츠라고 지칭한다.

이전의 @View... 은 컴포넌트 내 템플릿에 존재하는 요소를 탐색했다면, @Content... 는 외부에서 전달하는 컨텐츠를 탐색한다. 또한 이는 아래와 같이 내부에서 참조할 수 있다.

@ContentChild 는 콘텐츠 DOM에서 단 한건의 상태만을 담당한다. 예를 들어서 아래와 같은 컨텐츠로 쓰일 지시자가 있다고 하자.

@Directive({ selector: 'group-title'}) export class GroupTitle { @Input value:string; }

그리고 이를 사용하는 컴포넌트를 만든다.

@Component({ selector: 'group', template: ` {{this.title.value}} something group ` }) export class Group { @ContentChild(GroupTitle) title: GroupTitle; }

위 두 가지 컴포넌트를 사용하는 템플릿은 아래와 같다.

@ContentsChildren 를 통해 그룹 값 상태 얻기

기본적으로 @ViewChildren 과 같은 원리이다.

@Directive({ selector: 'word' }) export class Word { @Input() value:string; } @Component({ selector: 'word-list', template: `{{this.words}}` }) export class WordListComponent { @ContentChildren(Word) word: QueryList; get words(): string { return this.word ? this.word.map(v => v.value).join(", "):''; } }

위와 같은 예제를 아래와 같이 사용할 수 있다.

컴포넌트 스타일링

컴포넌트의 스타일링은 기본적으로 css와 같다. 하지만 기본적으로 컴포넌트는 독립적인 개체의 단위로 보기 때문에 자신의 스타일링이 부모, 자식의 스타일에 영향을 주지 않는다. 따라서 아래와 같은 3가지 참조 옵션을 제공한다.

:host

:host-context

/deep/

자신의 컴포넌트 :host

다른 프론트엔드 프레임워크와 다르게 NG2는 template에 최상위 엘리먼트란 존재가 없는데, 그 이유는 selector 가 되기 떄문이다. 따라서 selector 에 스타일 별도의 스타일을 적용하려면 :host 가 필요하다.

host는 자기 자신의 컴포넌트를 의미하며 사용법은 아래와 같다.

:host { /* CSS 정의 */ }

host를 주로 사용하는 컴포넌트 그 자체에 상태변화에 대한 스타일을 적용하기 위해서다. 예를들어 focus , active , hover 등이 그 대상이 될 수 있다. 사용법은 아래와 같다.

:host(:hover) selector { /* styles */ }

현재 컴포넌트가 사용된 환경에 따른 내부 스타일 적용

현재 컴포넌트가 사용된 환경은 아래와 같은 뜻을 의미한다.

이때 환경(컨텍스트)는 context1 , context2 가 될 수 있다. 만약 이 외부 엘리먼트의 클래스 속성이 primary 라면 아래와 같다.

실제 컴포넌트에서는 이런 경우에 대한 스타일을 아래와 같이 정의할 수 있다.

:host-context(.primary) selector { /* styles */ }

컨텍스트의 클래스가 primary 가 되었으므로 selector 에 특정한 styles 가 적용되는 것이다. 잘 응용하면 modal , notice , alarm , popup , messagebox 등과 같은 곳에서 쓰일 수 있을 것 같다.

자식컴포넌트에 스타일 적용

우선 다양한 컴포넌트에서 의존하는 컴포넌트를 사용할 때 스타일을 변경하고 해당 컴포넌트에서만 특정한 스타일을 확장할 수 있다. 여기서 확장의 의미는 상위 컴포넌트보다 하위 컴포넌트의 스타일이 우선시 되기 떄문에 덮어씌우기(Override)가 아닌 확장(Extends)이다.

사용법은 아래와 같다.

:host /deep/ selector { /* styles */ }

from http://sticky32.tistory.com/166 by ccl(A) rewrite - 2020-03-06 22:54:52

댓글

이 블로그의 인기 게시물

[Angular] Router 라우터 정리

[Angular] Router 라우터 정리 Angular2 버전 이후를 기준으로 정리한 글입니다. 라우터는 URL을 사용하여 특정 영역에 어떤 뷰를 보여 줄지 결정하는 기능을 제공한다. 전통적인 서버사이드 렌더링을 하는 웹 사이트는 주소가 바뀔 때마다 서버에 전체 페이지를 요청하고 전체 페이지를 화면에 렌더링한다. 매 요청시 전체 페이지를 새로 랜더링하는 것은 비효율적이기 때문에 라우터를 이용하여 필요한 부분만 랜더링을 한다면 효율적일 것이다. 라우터는 URL에 해당하는 컴포넌트를 화면에 노출하고 네비게이션을 할 수 있는 기능을 가지고 있다. Router 구성 요소 Router – 라우터를 구현하는 객체이다. Navigate() 함수와 navigateByUrl() 함수를 사용하여 경로를 이동할 수 있다. RouterOulet – 라우터가 컴포넌트를 태그에 렌더링하는 영역을 구현하는 Directive이다. Routes – 특정 URL에 연결되는 컴포넌트를 지정하는 배열이다. RouterLink – HTML의 앵커 태그는 브라우저의 URL 주소를 변경하는 것이다. 앵귤러에서 RouterLink를 사용하면 라우터를 통해 렌더링할 컴포넌트를 변경할 수 있다. ActivatedRoute – 현재 동작하는 라우터 인스턴스 객체이다. Router 설정 라우터를 사용하기 위해서는 먼저 Router 모듈을 import 해야 한다. import { RouterModule, Routes } from '@angular/router'; 라우터에서 컴포넌트는 고유의 URL과 매칭된다. URL과 컴포넌트는 아래와 같이 Routes 객체를 설정하여 지정할 수 있다. 아래의 예에서는 디폴트 path에서는 MainComponent가 노출이 되고 product-list path에서는 ProductListComponent가 노출이 되도록 설정을 한 것을 볼 수 있다. const routes: Routes = [ { pa...

[Angular] Controller

[Angular] Controller Step02_controller.html // angular 모듈 만들기 var myApp = angular.module( "myApp" ,[]); // Ctrl1 이라는 이름의 컨트롤러 만들기 myApp.controller( "Ctrl1" ,[ "$scope" , function ($scope){ $scope. name = "김구라" ; $scope.clicked = function (){ alert ( "버튼을 눌렀네?" ); }; $scope.nums = [ 10 , 20 , 30 , 40 , 50 ]; }]); // Ctrl2 이라는 이름의 컨트롤러 만들기 myApp.controller( "Ctrl2" ,[ "$scope" , function ($scope){ $scope. name = "원숭이" ; $scope.friends = [ {num: 1 , name : "김구라" ,addr: "노량진" }, {num: 2 , name : "해골" ,addr: "행신동" }, {num: 3 , name : "원숭이" ,addr: "동물원" } ]; }]); 내이름은 : {{name}} 눌러보셈 {{tmp}} 너의 이름은 : {{name}} 번호 이름 주소 {{tmp.num}} from http://heekim0719.tistory.com/164 by ccl(A) rewrite - 2020-03-07 09:21:16

JQuery - $ 사용 유형.

JQuery - $ 사용 유형. 최근에 새로운 Javascript 프레임워크와 라이브러리들이 많이 등장했고 시장 점유율 또한 많이 변동 되었다고 한다. 특히 요새 대새는 Angular와 React라고들 한다. 그리고 Jquery 요즘 누가써? Jquery 퇴물이야 등등... 그런 이야기들을 종종 찾아볼 수 있는데, 유행은 돌고 돌듯이 결국 Jquery가 몰락할 일은 없다고 생각하는 바, 묵묵히 Jquery를 고집하기로 한다...ㅎㅎ 먼저 Jquery 교과서랄까.. 기본 문법을 배울 수 있는 링크를 걸어둔다. https://www.w3schools.com/jquery 여기서 기초들을 다 익힐 수 있다. 프로그래밍 문법을 한번이라도 봤다면 + - * / 같은 연산 while, for 등은 다 비슷하기 때문에 $ 사용법만 잘 알면 될 것 같다. $ syntax 사용유형 일단 기본적으로 $는 JQuery에서 미리 정해놓은 변수 값이다. : JQueryStatic 1. $( ) : JQueryObject 가장 많이 사용하는 형태이다. 괄호 안에 들어 갈 수 있는 것들은 클래스 이름, 아이디, 셀렉터 등이다. 예를 들어 $('p')이면 현재 html 페이지에 있는 모든 를 JqueryObject로 가져오겠다는 것이다. hide()는 제이쿼리 메서드이다. $('p')는 제이쿼리 오브젝트이기 때문에 제이쿼리 메서드를 사용할 수 있다. 그중의 하나인 hide()를 사용해 보았다. 결과이다. 에는 스타일이 적용이 되었다. 해당 태그에는 jquery의 메서드가 적용이 된 것을 확인할 수 있다. 2. $.함수 : 플러그인 개발 또는 기본 전역함수 플러그인을 개발 할 때나 Jquery가 갖고 있는 기본 전역함수들을 사용할 때 쓴다. 전역함수에는 여러개가 있는데 그중에 개인적으로 많이 쓰는 것은 $.ajax({}), $.each({}) 등이 있다. 이 함수들의 사용방법은 따...