ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 5장) Angular 2 -HeroEditor- (2)
    Angular 2017. 1. 8. 14:41


    이번장에서는 Hero Editor 의 Master / Detail Page를 만들어 봅니다. 

    먼저 한명밖에 없었던 Hero 의 리스트를 더 늘려봅시다. 

    이를 위해 /src/app/app.component.ts 파일을 수정합니다. 

    
    import { Component } from '@angular/core';
    
    export class Hero {
      id : number;
      name : string;
    }
    const HEROES: Hero[] = [
      { id: 11, name: 'Mr. Nice' },
      { id: 12, name: 'Narco' },
      { id: 13, name: 'Bombasto' },
      { id: 14, name: 'Celeritas' },
      { id: 15, name: 'Magneta' },
      { id: 16, name: 'RubberMan' },
      { id: 17, name: 'Dynama' },
      { id: 18, name: 'Dr IQ' },
      { id: 19, name: 'Magma' },
      { id: 20, name: 'Tornado' }
    ];
    
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      title = 'Tour of Heroes';
      heroes = HEROES;
    }
    
    


    새로운 HEROES 라는 Hero 타입의 배열을 만들고 Hero 객체들을 넣었습니다. 

    그리고 로직에서는 heroes 라는 새로운 객체에 HEROES를 대입하여 사용하도록 하였습니다. 

    이젠 화면에서 나타날 수 있도록 일단 /src/app/app.component.html 파일을 수정합시다. 

    
    <h2>My Heroes</h2>
    <ul class="heroes">
      <li *ngFor="let hero of heroes">
        <span class="badge">{{hero.id}}</span>{{hero.name}}
      </li>
    </ul>


    app.component 의 로직코드에서 hero 라는 객체를 지우고 새로운 배열객체를 지정하였기에 

    기존의 {{hero.id}] 와 {{hero.name}} 은 삭제하고 새롭게 지정한 heroes 의 데이터를 나타내기 위해 

    *ngFor 라는 지시자를 사용하였습니다. 

    이 *ngFor 는 해당 배열을 돌며 각 객체를 따로따로 담아서 변수에 저장하고 해당 템플릿에서 사용할 수 있게 만듭니다. 

    *ngFor = " 사용할 변수명 of 참조 할 배열"  로서 사용되며 여기서는 로직에서 지정해둔 heroes 배열을 참조하여

    hero 변수에 저장되도록 하였습니다. 

    그리고 해당 변수를 이용하여 li 내부에서 {{hero.id}} , {{hero.name}} 을 사용하여 데이터를 보여주고 있죠. 

    이제 ng serve 명령어로 서버를 킨 뒤 결과를 보면 


    해당 배열에 들어있는 영웅들의 목록이 li 태그에 반복적으로 들어와 나타나고 있습니다.

    이제 리스트는 완성되었으나, 이대로 진행하기에는 아무 디자인도 적용되지 않았기에 무리가(?) 있습니다. 

    구글 역시 보기에 허접해 보였는지 스타일링을 위한 css 수정을 하도록 권하고 있습니다. 

    app.component.ts 의 stylesUrl 을 보면 우리의 스타일시트는 app.component.css 파일을 가르키고 있습니다.

    현재 /src/app/app.component.css 파일은 아무것도 없으니 채워 넣도록 합시다.

    
    .selected {
        background-color: #CFD8DC !important;
        color: white;
      }
    .heroes {
        margin: 0 0 2em 0;
        list-style-type: none;
        padding: 0;
        width: 15em;
      }
    .heroes li {
        cursor: pointer;
        position: relative;
        left: 0;
        background-color: #EEE;
        margin: .5em;
        padding: .3em 0;
        height: 1.6em;
        border-radius: 4px;
      }
    .heroes li.selected:hover {
        background-color: #BBD8DC !important;
        color: white;
      }
    .heroes li:hover {
        color: #607D8B;
        background-color: #DDD;
        left: .1em;
      }
    .heroes .text {
        position: relative;
        top: -3px;
      }
    .heroes .badge {
        display: inline-block;
        font-size: small;
        color: white;
        padding: 0.8em 0.7em 0 0.7em;
        background-color: #607D8B;
        line-height: 1em;
        position: relative;
        left: -1px;
        top: -4px;
        height: 1.8em;
        margin-right: .8em;
        border-radius: 4px 0 0 4px;
      }
    


    그리고 결과를 확인해보면  

    아주 깔끔하게 적용 되었습니다. 

    이제 리스트는 정리가 되었으니 영웅을 클릭하면 상세정보가 나오도록 Event 를 넣도록 합니다. 

    이를 위해 먼저 템플릿 파일에 click event 를 넣어봅시다. 

    /src/app/app.component.html 파일을 수정합니다. 

    <h2>My Heroes</h2>
    <ul class="heroes">
      <li *ngFor="let hero of heroes" (click)="onSelect(hero)">
        <span class="badge">{{hero.id}}</span>{{hero.name}}
      </li>
    </ul>
    


    li 태그에 (click) = "onSelect(hero)" 라는 새로운 문구가 생겼습니다. 

    (click) 은 흔히 자바스크립에서 쓰던 click 이벤트를 그대로 가져온 것 이고, 내부적으로 

    해당 컴포넌트의 즉 현재는 app.component.ts 파일에서 정의한 onSelect 라는 이벤트를 

    실행시키며 Param 으로 hero 변수를 설정하였습니다. 여기서 hero 변수는 당연히 heroes 에서 가져온 hero 변수겠지요.


    이제 onSelect 메소드를 만들어야 하는데 현재 하고자 하는 목적을 살펴봅시다. 

    영웅목록에서 영웅을 클릭 하면 세부내용으로 해당영웅의 정보를 보여줘야 한다는 건  

    현재 선택된 영웅을 사용자가 목록에서 클릭한 영웅으로 바꿔줘야 한다는 것 입니다. 

    그러기 위해서 가장 먼저 필요한건 선택된 영웅을 저장할 변수가 있어야 겠지요. 

    그런 뒤 onSelect 에서 선택된 영웅의 데이터를 클릭한 영웅으로 버꿔 주면 끝 .

    /src/app/app.component.ts 를 수정합니다. 

    
    import { Component } from '@angular/core';
    
    export class Hero {
      id : number;
      name : string;
    }
    const HEROES: Hero[] = [
      { id: 11, name: 'Mr. Nice' },
      { id: 12, name: 'Narco' },
      { id: 13, name: 'Bombasto' },
      { id: 14, name: 'Celeritas' },
      { id: 15, name: 'Magneta' },
      { id: 16, name: 'RubberMan' },
      { id: 17, name: 'Dynama' },
      { id: 18, name: 'Dr IQ' },
      { id: 19, name: 'Magma' },
      { id: 20, name: 'Tornado' }
    ];
    
    
    @Component({
      selector: 'app-root',
      templateUrl: './app.component.html',
      styleUrls: ['./app.component.css']
    })
    export class AppComponent {
      title = 'Tour of Heroes';
      heroes = HEROES;
      selectedHero: Hero;
      
      onSelect(hero:Hero) : void {
        this.selectedHero = hero;
      }
    }
    
    


    selectedHero 라는 Hero 타입의 변수를 만들었고 여기에는 현재 선택된 영웅의 데이터가 들어갑니다. 

    onSelect 메소드는 파라미터로 Hero 타입의 hero 를 받아서 selectedHero 의 데이터를 hero 로 변경합니다. 

    이정도는 누구나 다 이해할 수 있을 정도로 쉽다고 봅니다. 

    이제 이벤트를 지정해줬으니 선택한 영웅목록을 보여주기 위한 디테일 화면을 만들기 위해 

    /src/app/app.component.html  파일을 수정합시다. 

    
    <h2>My Heroes</h2>
    <ul class="heroes">
      <li *ngFor="let hero of heroes" (click)="onSelect(hero)">
        <span class="badge">{{hero.id}}</span>{{hero.name}}
      </li>
    </ul>
    
    <h2>{{selectedHero.name}} details!</h2>
    <div><label>id: </label>{{selectedHero.id}}</div>
    <div>
        <label>name: </label>
        <input [(ngModel)]="selectedHero.name" placeholder="name"/>
    </div>
    


    디테일 화면에서는 로직에서 정의한 selectedHero 의 name , id 를 보여주고 

    양방향 데이터 바인딩을 사용하여 영웅의 이름을 바로 수정할 수 있도록 만들었습니다. 

    그럼 결과를 확인해 볼까요 ? 

    ???? 내 영웅들 어디간겨  

    아무 내용도 표시 되지 않습니다.  

    무슨 일인지 콘솔창을 열어 살펴보죠. 

    에러 내용을 보니 name 속성을 읽을수 없다   , name 속성은 지정되지 않았다 , 좋은 말 할때 name 속성을 내놓아라 등등

    아무래도 selectedHero.name 이 문제가 생겼습니다. 이는 현재 로직에서 selectedHero 에는 초기 데이터를 

    지정해 주지 않았기 때문에 당연히 읽을 수 없다는 에러가 나타날 수 밖에 없죠. 

    이를 해결하기 위해 selectedHero 에 강제로 더미 데이터를 넣어 두면 해결이 될까요? 됩니다. 

    하지만 Angular 에서는 좀 더 센스있게 해결할 수 있습니다. 바로 *ngIf 로 말이죠. 

    *ngIf 는 말그대로 참인지 거짓인지 , 데이터가 있냐 ,없냐 로 해당 부분을 표시하지 않도록 만들어 버립니다. 

    일단 바로 써봅시다.  

    /src/app/app.component.html 파일의 li 태그로 가서 *ngIf 를 넣어주면 됩니다. 

    
    
    <h2>My Heroes</h2>
    <ul class="heroes">
      <li *ngFor="let hero of heroes" (click)="onSelect(hero)">
        <span class="badge">{{hero.id}}</span>{{hero.name}}
      </li>
    </ul>
    
    <div *ngIf="selectedHero">
      <h2>{{selectedHero.name}} details!</h2>
      <div><label>id: </label>{{selectedHero.id}}</div>
      <div>
          <label>name: </label>
          <input [(ngModel)]="selectedHero.name" placeholder="name"/>
      </div>
    </div>
    
    


    div 태그를 만들어 detail 에 관련된 부분을 감싸주고 *ngIf 지시자를 사용하였으며 대상은 selectedHero 를 

    지정해 주었습니다. 

    이제부터 Angular 는 해당 데이터가 없을때는 detail 부분을 DOM 밖으로 보내고, selectedHero 의 데이터가 

    생기면 해당부분을 DOM으로 가져와서 표시해 줍니다. 

    확인해봅시다. 



    이제 해당 영웅의목록도 잘나오고 , 영웅의 이름도 제대로 수정되는 걸 볼 수 있습니다. 

    영웅의 목록도 보고, 수정도 가능합니다. 하지만 구글은 여기서 멈추지 않습니다. 

    영웅을 선택했을 때 선택한 영웅은 목록에서 표시가 되었으면 좋겠답니다. 그렇죠. UX는 중요합니다. 

    이를 위해선 어떻게 해야 할까요? 

    영웅목록에서 선택한 영웅에게만 배경색을 바꾸는 새로운 css 를 적용하는데 , 이를 위해 

    현재 선택된 영웅의 class 를 추가 해 버리도록 합시다.

    그러기 위해 템플릿 파일인 /src/app/app.component.html 의 코드를 수정합시다. 

    
    <h2>My Heroes</h2>
    <ul class="heroes">
      <li *ngFor="let hero of heroes" 
      [class.selected]="hero === selectedHero"
      (click)="onSelect(hero)"
      >
        <span class="badge">{{hero.id}}</span>{{hero.name}}
      </li>
    </ul>
    
    <div *ngIf="selectedHero">
      <h2>{{selectedHero.name}} details!</h2>
      <div><label>id: </label>{{selectedHero.id}}</div>
      <div>
          <label>name: </label>
          <input [(ngModel)]="selectedHero.name" placeholder="name"/>
      </div>
    </div>
    
    


    li 태그에 [class.selected]="hero === selectedHero" 라는 코드가 추가 되었습니다. 

    어떤식으로 작동되는 걸까요? 

    [] 는 단방향 데이터 바인딩으로 angular 에서는 속성값을 조작할 때 쓰이게 됩니다. 

    여기서 class.selected 는 class="selected" 를 추가 한다는 뜻이 되구요. 

    코드를 그대로 풀어보면 li 태그의 class="selected" 라는 속성을 추가하는데 여기서 hero 객체가 selectedHero 와 

    같은 놈만 추가하고 , 나머지는 제거한다 라는 뜻이 됩니다. 

    여기서 selected 는 이미 css 파일에서 정의를 해뒀기에 해당 데이터는 selected 의 css 를 적용받게 됩니다. 


    이제 결과를 확인해 봅시다. 


    css 가 제대로 적용되는 걸 확인하였습니다. 그리고 


    요소를 확인해보니 class="selected" 가 추가 된 것도 확인됩니다.

    이번장에서는 영웅목록과 수정, 스타일을 조작하는 것을 봤습니다. 

    다음장은 뭘까요? Multiple Components 라는 걸 보아하니 이제 App.component 하나에서 모든 걸 

    조작하는 것이 아닌 각 부분을 여러 컴포넌트로 분리해서 사용하려는 것 같네요. 

    Angular 2 의 핵심 중 하나죠. 


    그럼 20000 


    댓글