[Angular2官方tutorial學習02]display list,master detail,click event,master detail advanced,幫css設定加上if

[Angular2官方tutorial學習02]display list,master detail,click event,master detail advanced,幫css設定加上if

@接下來要練習在畫面上顯示一個list,類似查詢出來一堆資料這樣。

@首先要先做一些假資料,所以請先新增一個檔案src/app/mock-heroes.ts,檔案內容如下
import Hero這個這個class之後,就可以直接宣告一些假資料了,用json的form做資料即可。如此一來在server端就已經建立好假資料了

import { Hero } from './hero';

export 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' }
];


@然後我們希望在HerosComponet顯示這個清單,所以接著在src/app/heroes/heroes.component.ts加入下列import,把剛才的假資料import進來

import { HEROES } from '../mock-heroes';

然後再於同一檔案內的export class HeroesComponent implements OnInit裡面新增一個property叫做heros,用來讀取剛才的假資料

heroes = HEROES;

最後看起來會是這樣

再來於src\app\heroes\heroes.component.html加入下列內容,用來顯示假資料的清單
*ngFor就是angular的迴圈語法,let就是宣告區域變數(注意別用var宣告變數,會變成全域變數)

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

最後看起來會像是這樣

打開網頁瀏覽器,此時畫面已經成功顯示剛才的假資料了,雖然畫面有點醜,但是這不重要先不理會囉

最後於src\app\heroes\heroes.component.css輸入官方提供的css排版語法,這些語法因為寫在heroes.component.css檔案裡,因此不會影響到其他的component,是不是很方便呢?

/* HeroesComponent's private CSS styles */
.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;
  }


這樣子排版之後,網頁瀏覽器的結果看起來會好一點……嗎 XD


@接著要示範master detail的畫面設計以及click event
請把於src\app\heroes\heroes.component.html的<li *ngFor="let hero of heroes">改成如下:

<li *ngFor="let hero of heroes" (click)="onSelect(hero)">

接著打開檔案src\app\heroes\heroes.component.ts,目前尚未修改的hero變數應該長的像是下面這樣

將紅色框框刪除之後修改為如下:

selectedHero: Hero;
onSelect(hero: Hero): void {
  this.selectedHero = hero;
}


接著再打開檔案src\app\heroes\heroes.component.html,原本有一段跟hero變數綁定的html如下

請將此紅色框框內容刪除之後,以下列內容取代, *ngif就是angular的if,這段html只有在selectedHero為true(表示有資料)的時候,才會顯示在畫面上

<div *ngIf="selectedHero">

  <h2>{{selectedHero.name | uppercase}} Details</h2>
  <div><span>id: </span>{{selectedHero.id}}</div>
  <div>
    <label>name:
      <input [(ngModel)]="selectedHero.name" placeholder="name">
    </label>
  </div>

</div>


如此一來,頁面上基本的master detail就完成了!,click list裡面任一個hero,就會把這個hero的detail顯示在上方喔



還有一點要補充一下,就是要替已經用滑鼠選定的hero的li上色,不然用肉眼很難分辨到底剛才用滑鼠點選了哪一個,請查看目前src/app/heroes/heroes.component.css的內容,可以發現有一個class叫做.selected,這個就是會幫滑鼠選定的li上色的css class


再來請打開src\app\heroes\heroes.component.html,把下面這段加入到<li *ngFor="let hero of heroes" (click)="onSelect(hero)">的上面
我把這個寫法叫做(幫css設定加上if),當hero物件等於selectedHero物件的時候,就會套用.selected這個css

[class.selected]="hero === selectedHero"


加好之後會長的像是這樣

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


此時打開瀏覽器隨便點選一個hero的話,html裡面的li就會變色囉


最後的src\app\heroes\heroes.component.html內容長這樣

<div *ngIf="selectedHero">
  <h2>{{selectedHero.name | uppercase}} Details</h2>
  <div><span>id: </span>{{selectedHero.id}}</div>
  <div>
    <label>name:
      <input [(ngModel)]="selectedHero.name" placeholder="name">
    </label>
  </div>
</div>

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



最後的src\app\heroes\heroes.component.ts內容長這樣

import { Component, OnInit } from '@angular/core';
//加入了這個import
import { Hero } from '../hero';
//把HEROES假資料import進來
import { HEROES } from '../mock-heroes';

@Component({
  selector: 'app-heroes',
  templateUrl: './heroes.component.html',
  styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
  //hero = 'Windstorm';
  //hero原本只是字串變數,現在變成一個資料結構
  // hero: Hero = {
  //   id: 1,
  //   name: 'Windstorm'
  // };
  selectedHero: Hero;
  onSelect(hero: Hero): void {
    this.selectedHero = hero;
  }
  heroes = HEROES;

  constructor() { }

  ngOnInit() {
  }

}



@master detail advanced要介紹的是,將master 以及 detail元件拆開來到不同的component,便於以後維護(上面所介紹的master以及detail元件都是在同一個component裡面)


接著執行angular cli指令以新增detail專用的component

ng g c hero-detail


再來打開src/app/hero-detail/hero-detail.component.html,將內容修改如下,主要就是用*ngModel來顯示hero的detail

<div *ngIf="hero">

  <h2>{{hero.name | uppercase}} Details</h2>
  <div><span>id: </span>{{hero.id}}</div>
  <div>
    <label>name:
      <input [(ngModel)]="hero.name" placeholder="name"/>
    </label>
  </div>

</div>


然後修改src/app/hero-detail/hero-detail.component.ts的內容如下,主要就是設定這個detail component可以跟master component做連動,設定的方式就是用@Input

import { Component, OnInit, Input } from '@angular/core';
//detail會跟master做binding,因此這邊要先做好import
import { Hero } from '../hero';
 
@Component({
  selector: 'app-hero-detail',
  templateUrl: './hero-detail.component.html',
  styleUrls: ['./hero-detail.component.css']
})
export class HeroDetailComponent implements OnInit {
  //另外一個component連動的設定方式,就是用@Input
  @Input() hero: Hero;
 
  constructor() { }
 
  ngOnInit() {
  }
 
}


最後就是把原本hero component的detail的部分拿掉,src/app/heroes/heroes.component.html把detail的內容拿掉之後,應該會長的像是下面這樣
塞值到@Input的寫法就像是這樣:[被塞的變數] = 要塞進去的變數

<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>

<app-hero-detail [hero]="selectedHero"></app-hero-detail>


打開瀏覽器的畫面變成這樣(其實沒啥變,只是排版位置改了一下)

最後終於把master detail分開來顯示囉!在實務上這是常見的設計,很好用的!寫好這個功能,通常可以運用在多個系統功能

最後補充目前為止做到這邊的整個專案的壓縮檔下載,方便大家自行在家確認程式碼
https://www.dropbox.com/s/0tbki3sye88hz8k/angular-tour-of-heros%20-%20%E5%AE%8C%E6%88%90master%20detail.rar?dl=0




參考資料:
Master/Detail Components
https://angular.io/tutorial/toh-pt3
Display a Heroes List
https://angular.io/tutorial/toh-pt2