AngularJS/AngularJS

▶[Angular Guide] 4. User Input

비주얼라이즈 2016. 9. 16. 09:59

▶[Angular Guide] 4. User Input



우리는 사용자들이 링크를 클릭 하거나, 버튼을 누를때, 또는 텍스트를 입력할 때. 이 것에 대해 알고싶다.


이러한 사용자의 작업은 모두 DOM 이벤트를 발생시킨다.  이번 챕터에서는 Angular의 이벤트 바인딩 구문을 사용하여 해당 이벤트에 바인딩하는 방법에 대해 정리하고자한다.


[Angular2 Basic - User Input  LIVE EXAMPLE]





User input event에 바인딩하기 Binding to user input events


우리는 종류에 관계없이, 모든 DOM event에 대한 응답을 위해 Angular의 event binding을 사용할 수 있다.


구문(Syntax)은 간단하다. 

- 괄호안에 DOM 이벤트를 감싸 넣고, 인용된 template statement 를 할당해준다.


이해를 돕기위해, 다음 예제코드를 살펴보자.

간단하게 Click event handler를 구현하는 내용이다.



<button (click)="onClickMe()">Click me!</button>

위 코드에서 괄호로 감싸져있는 click 이벤트가 바인딩 될 target은 등호(=) 오른쪽에 있는 onClickMe() 함수이다.이벤트 바인딩 코드를 작성할 때, 우리는 template statement의 실행 상황을 알고있어야한다. statement에서 나타나는 식별자(identifier)는 특정 컨텍스트 객체에 속한다.


app/click-me.component.ts

@Component({
  selector: 'click-me',
  template: `
    
    {{clickMessage}}`
})
export class ClickMeComponent {
  clickMessage = '';

  onClickMe() {
    this.clickMessage = 'You are my hero!';
  }
}
위 코드는 유저가 버튼을 클릭할 때, Angular는 compoenent의 `OnclickMe` method를 호출한다.




User Input을 $event 객체에서 얻기 Get User Input from the $event object

Angular를 활용하여 모든 종류의 event를 바인딩할 수 있다. 

이에대한 내용을  Angular에서 제공하는 예제코드를 통해 보면 이해가 쉽다. 


이 예제 에서는  input box keyup event를 바인딩하고, 사용자 입력데이터를 사용자 화면에 바로 표시하는 작업을 다룬다.



app/keyup.component.ts (template v.1)


template: `
  <input (keyup)="onKey($event)">
  <p>{{values}}</p>
`


우선, Angular의  $event 라는 (변수에 접근할 수 있는)이벤트 객체를 생성한다.  그 다음 이 이벤트 객체('$event'를 말한다.) 를 위 코드에서 보이듯  'onKey()'라는 Component내의 method에 넘겨준다.


굳이 우리가 $event 객체를 타이핑하고, Component의 메서드로 넘겨주는것은 너무나 당연하게도 이 $event 객체 속 어딘가에 우리가 찾는 사용자입력데이터가 있기 때문이다.


app/keyup.component.ts (class v.1)


export class KeyUpComponent_v1 {
  values = '';

  // without strong typing
  onKey(event:any) {
    this.values += event.target.value + ' | ';
  }
}


$event 객체의 형태는 어떤 이벤트가 발생되는 지에 따라 결정된다.

예를들어, 사용자가 <Button>요소를 클릭할 때 발생하는 DOM의 click event에는 'value'값이 없다. 사용자는 단순히 '클릭'만 했을 뿐, 어떤 값을 입력하지는 않았기 때문이다. (값을 타이핑으로 입력하는 input box라면 value가 존재한다)







keyup event는 DOM에서 온다. 따라서 $event는 반드시  DOM 표준 event 객체여야한다. 

$event.target은 우리에게 하나의 HTMLInputElement를 제공한다.

여기에는 우리사용자의 Input 데이터를 포함하고있는 property 값이 있다..


- [MDN, HTMLInputElement]




onKey()라는  Component의 method


우리가 event 객체에서 사용자의 입력정보를 추출하는 지점에서, input을 사용자 데이터 리스트(user data list)에 추가한다. 

우리는 Component의 value property에 이 정보를 축적할 수 있다.


그리고 우리는 interpolation ( 이렇게 생긴 것--->{{}} )을 사용하여, Component에 축적한 값을 화면에 표시한다. 


결과페이지에서 'abc'를 입력하고 다시 백스페이스로 지워보자. 어떤 결과가 보이는가?

[사진 : angualr 공식페이지, User Input>]


우리는 우리의 코드를 단순화하기 위해 모든 유형의 type event를 casting함으로써, strong typing을 포기했다.  
우리는 일반적으로 TypeScript가 제공하는 강력한 타입지정을 선호한다. 


다시! HTML DOM 객체를 casting하는 method를 작성해보자.
app/ketup.component.ts (class v.1 - strongly typed)

export class KeyUpComponent_v1 {
  values = '';
  // with strong typing
  onKey(event: KeyboardEvent) {
    this.values += (event.target).value + ' | ';
  }
}
 이전 코드에서 event의 타입을 'any'로 지정했던 것과 달리, 여기서는 event의 타입을 'KeyboardEvent'로 지정했다. 






사용자입력값을 템플릿참조변수에서 얻기 Get User Input from a template reference variable


이 방법은 위에서 소개했던 $event 변수를 사용하지 않고 사용자의 데이터를 얻는 방법이다.



Angular에는 템플릿참조(Template Reference)라는 구문 기능 (syntax feature)이 있다. 

이 템플릿참조변수는 우리가 요소에 직접 접근할 수 있도록 해준다. 

템플릿참조변수를 '#'라는 식별자로 선언한다. 이 내용에 대해서도 예제코드와 함께 살펴보도록 하자.


 app/loop-back.component.ts


@Component({
  selector: 'loop-back',
  template: `
    <input #box="" (keyup)="0">
    <p>{{box.value}}</p>
  `
})
export class LoopbackComponent { }

위 코드를 뜯어서 살펴보자. 우리는 'box'라는 이름의 템플릿참조변수를 <input> 박스안에서 선언했다. 


<input #bos="" (keyup)="0">


이제 box 라는 이 변수는 <input>요소인 자신을 참조한다. 이는 우리가 <input>요소의 값을 잡아낼(grap) 수 있고, 이를 interpolation을 활용해 <p></p>태그 안에 표현할 수 있다는 말이다.


template는 완벽하게 스스로 동작한다. 이것은 component에 바인딩되는 일반적인 angular의 형태와는 다르다. 위 코드에서 Component는 이러한 동작에대해 아무런 작업도 실시하는 것이 없다.


이제 위에서 했던 것 처럼, 결과 페이지를 띄워 typing 테스트를 해본다.



우리가 위에서 배웠던 것들은 모두 event와 바인딩되지 않으면, 동작하지 않는다.

Angular는 만약 우리가 키 입력과 같은 비동기 이벤트에 대한 응답으로 뭔가를 할 경우에만 업데이트를 수행한다. 이것이 우리가 아무것도 하지않는 KeyUp 이벤트를 바인딩하는 이유이다. Angular는 이렇게 영리하게 동작하고있다.


다시말하지만, 템플릿참조변수(Template Reference Variable은) 흥미롭다.

템플릿참조변수는  $event 객체로 textbox에 대한 값을 얻는 것보다 더 명확하게 작업을 처리한다. 이를 활용하여 이전에 작성했던 keyup 예제들을 개선할 수 있을것 같다.



@Component({
  selector: 'key-up2',
  template: `
    <input #box (keyup)="onKey(box.value)">
    <p>{{values}}</p>
  `
})
export class KeyUpComponent_v2 {
  values = '';
  onKey(value: string) {
    this.values += value + ' | ';
  }
}
이번 코드를 보면 분명히 쉽게 읽히고, 이해된다.
특히, 이 방법이 좋은점이라면 우리의 Component code view에서 깨끗한 데이터 값을 얻을 수 있다는 것이다. 이 방법을 사용하면 더이상 $event및 그 구조에 대해 이해하지않아도 된다.




Key 이벤트 필터링 Key event filtering (with key.enter)


아마도 우리는 모든 키 입력을 걱정하지는 않을 것이다. 

아마 사용자가 Enter Key를 누를 때 Input box의 값에만 관심이 잇을 것이다. 따라서 우리는 다른 모든 키를 무시하고싶다. 


우리가 keyup event를 바인딩 할 때, event handling 구문은 사용자의 모든 key 입력을 수신(listen)하고 처리한다. 

여기서 우리는 모든 $event.keyCode를 먼저 검토하여 필터링하고, 엔터키를 입력할 때에만 업데이트 할 수 있다.




Angular에는 key event를 필터링하는 기능이 있다. 

특히 이 keyboard event에 대해서는 별도의 특별한 구문(syntax)이 있다. 우리는 Angular의 keyup pseudo-event에 바인딩함으로서  Enter Key를 수신(listen)할 수 있다. 그런 다음에component의 property value를 업데이트 해준다. (이 예제에서 업데이트는 event binding 구문 안에서 일어난다. 이 보다 더 권장하는 방법은 update code를 component안에 삽입하는 것이다.)


@Component({
  selector: 'key-up3',
  template: `
    <input #box (keyup.enter)="values=box.value">
    <p>{{values}}</p>
  `
})
export class KeyUpComponent_v3 {
  values = '';
}

위 코드가 어떻게 동작하는지 결과페이지에서 살펴보자.




On blur


앞서 살펴보았던 예제에서는 사용자가 입력상자가 아닌 페이지의 다른 영역을 클릭하면 입력상자(input box)의 현재상태를 전송하지 않을 것이다. 


다음 예제를 통해, 사용자가 input box안에 focus를 두고 Enter를 입력할 경우에만 Component의 value property를 업데이트하는 방법을 살펴보자.


또한, 이 방법에 더해 input box에서 blur event를 수신(listen)함으로써 부가적인 표현들을 적용해볼 수 있다.


app/keyup.component.ts (v4)



@Component({
  selector: 'key-up4',
  template: `
    <input #box
      (keyup.enter)="values=box.value"
      (blur)="values=box.value">
    <p>{{values}}</p>
  `
})
export class KeyUpComponent_v4 {
  values = '';
}

우리가 이 장에서 알게된 내용을 바탕으로 (이전챕터에 이어) Hero list에 새로운 Hero를 추가할 수 있다. 마이크로 앱에서 모두 같이 넣어보기로한다. 사용자가 입력상자에 먼저 입력하여 Hero를 추가한다음 Enter 키를눌러 추가 버튼을 클릭하거나 다른 페이지에서 클릭할 수 있다.



app/little-tour.component.ts


@Component({
  selector: 'little-tour',
  template: `
    <input #newHero (keyup.enter)="addHero(newHero.value)"
      (blur)="addHero(newHero.value); newHero.value='' ">

    <button (click)=addHero(newHero.value)>Add</button>

    <ul><li *ngFor="let hero of heroes">{{hero}}</li></ul>
  `
})
export class LittleTourComponent {
  heroes = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado'];
  addHero(newHero: string) {
    if (newHero) {
      this.heroes.push(newHero);
    }
  }
}

이제 이 챕터의 중요한 내용을 거의 다 보아간다. 몇 가지 기능에대해서 조금더 살펴보도록 하자.


요소를 참조하는 Template 변수를 사용하기 Use Template variables to refer to elements

여기서 'newHero' template 변수는 <input>요소를 참조한다. 나아가 이를 활용하면 'newHero'을 <input>의 모든  sibling 또는 child에서 사용할 수 있다.


요소(Element)가 아닌 값을 전달하기. Pass Values, not Element 

우리는 component의 'addHero' 메서드를 통해 'newHero'를 전달할 수 있었다.대신, 우리는 input box의 값을 잡아(grab) 'addHero'에 전달한다. Component는 HTML또는 DOM에 대해 아무 정보도 알고있을 필요가 없다. 이것이 우리가 좋아하는 작업의 방법이기도하다.


템플릿 구문은 심플하게 유지하기 Keep template statements simple

우리는 'addHero'를 호출하는 첫 번째를 좋아한다. 우리는 입력상자의 값으로 빈 문자열을 할당하는 두 번째 방법은 좋아하지 않는다. 


두 번재 구문에 좋은 이유가 존재한다. 우리는 Herolist에 새로운 Hero를 추가하고 난 다음, input box를 clear해주어야한다. Component는 이 것을 수행할 방법이 없다. 왜냐하면 이것은 스스로 inputbox 에 접근할 수 없게 되어있기 때문이다. 


비록 예제가 잘 동작하기는 하지만, HTML에서의 자바스크립트를 올바른 방법으로 사용할 수 있도록 주의를 기울이고 있는 중이다. 결론적으로, Angular2의 Template 구문은 강력하다. 우리는 이 것을 사용하는 것을 자신있게 권한다.  이것을 쓰지 않고, HTML 안에서 복잡한 자바스크립트를 작성하는 것은 정말 무책임한 일이라고 생각한다.




Summary


우리는 사용자 입력과 제스처에 응답하기위한 기본원리를 이해했다. 이러한 기본 원리는 매우 강력하지만, 사용자입력처리가 대용량으로 이루어진다면 이 방법은 약간 투박하다고 할 수 있다. 


  만약 data entry fields와 model properties 사이에서 양방향 바인딩되어질때 We're operating down at the low level of events. Angular는 NgModel이라는 이름의 양방향 바인딩을 처리하는 기능을 가지고 있다. 이에대해서는 다음 챕터인 'Forms'에서 살펴볼 예정이다.