iT邦幫忙

2019 iT 邦幫忙鐵人賽

1
Modern Web

Angular 深入淺出三十天系列 第 31

[Angular 深入淺出三十天] Day 30 - Angular 小學堂(四之四)

昨天的登入頁、購物車頁與結帳成功頁大家都有順利套完版嗎?!

如果沒有的話沒關係,看完今天的文章再繼續努力吧!!


結帳頁套版

今天一開始我們先來套結帳頁的版型,打開 assets/css/ 裡的 checkout.css 檔,一樣全部複製下來之後,貼到我們專案裡的 checkout.component.css 裡。

一樣要記得調整檔案路徑噢!!忘記怎麼修改的話可以看昨天的文章複習一下。

接著打開素材 pages/ 裡的 checkout-1.html,一樣只要複製中間的區塊就好:

Imgur

複製貼上到我們專案裡的 checkout.component.html 之後,就可以先來看一下畫面:

Imgur

耶!!套完了!!

還沒啦!!

這邊其實會稍微要大家跟著我處理一下,因為當初其實沒有想說要切路由,直接用狀態切換就好。

不過在寫文章的時候又想說,就多練習一些巢狀路由的設定也不錯,才把這邊切成其他路由。

所以接下來就要開始處理這部分了!!

首先先打開我們專案的 checkout.component.css 檔,然後開啟搜尋/取代的功能 (masOS 請按下 command + option + F , Windows 請按下 ctrl + H ,將整個檔案裡的 article 全部取代為 article /deep/

像這樣:

Imgur

最上面那兩個我移除是因為那兩個不需要改成 article /deep/

接著再到打開 checkout.component.html ,然後剪下這個區塊裡的程式碼:

Imgur

整個貼到 customer-info.component.html 裡,儲存後看一下畫面,應該就會跟剛剛一開始的時候長得一模一樣。

Angular 小知識:

在 Angular 裡,每個 component 的 css 是互不影響的,避免命名衝突以及讓程式碼更容易被維護。
不過如果像遇到這種一定要從父層設定樣式,且要能夠一併調整子層 Component 的樣式時,就需要加上 /deep/ 的特殊字來讓樣式能夠套用在子層上。

畫面沒問題之後,我們先把 customer-info.component.html 裡的下一步這個按鈕的連結改為:

<a [routerLink]="['..', path.checkoutFlow.paymentInfo]">下一步</a>

咦?有發現這個連結長得有點奇怪嗎?

其實如果昨天有練習購物車頁的套版的話,應該會遇到路由設定的問題,因為在巢狀路由下的時候,連結是往子層下面找的。

我們用 Augury 幫我們畫的圖來解釋:

Imgur

是不是超清楚的?! Augury 超好用的!!

一開始我們的共用層連結是在 AppComponent 裡,所以在設定連結的時候只要像這樣即可:

<ul>
  <li><a [routerLink]="[path.home]">首頁</a></li>
  <li><a [routerLink]="[path.products]">甜點</a></li>
  <li><a [routerLink]="[path.login]">登入</a></li>
</ul>

因為路由機制會從 Appcomponent 往下找的關係,所以從上圖中的結構就能清楚地看到,路由機制可以找到相對應的路徑,沒有問題。

但如果像是現在我們要設定的連結是在 CustomerInfoComponent 裡的時候,如下圖紅框處:

Imgur

這時候如果我們連結設成這樣:

<a [routerLink]="path.checkoutFlow.paymentInfo">下一步</a>

可想而知,路由機制會往 CustomerInfoComponent 的下一層去找路由。

但有這個路由嗎?沒有。

所以這時候我們可以這樣設:

<a [routerLink]="['..', path.checkoutFlow.paymentInfo]">下一步</a>

'..' 有一種回上一層的感覺,然後再由上一層的位置往下找 path.checkoutFlow.paymentInfo 這個變數儲存的路徑,也就是 PaymentInfoComponent 所對應的路由。

設定好之後,應該就能夠正常導頁了:

Imgur

再來讓我們把 PaymentInfoComponent 跟 ReceiptInfoComponent 迅速地套版套一套,他們各自對應的素材檔案是 pages/ 底下的 checkout-2.htmlcheckout-3-1.htmlcheckout-3-2.html

PaymentInfoComponent 的套版非常簡單,只要將 checkout-2.html 裡的這塊程式碼:

Imgur

複製貼上到 payment-info.component.ts 裡,然後一樣修改一下連結:

<a [routerLink]="['..', path.checkoutFlow.receiptInfo]">下一步</a>

這樣就完工了,超簡單的吧?!

ReceiptInfoComponent 的套版其實也差不多,一樣將 checkout-3-1.html 裡的這塊程式碼:

Imgur

複製貼到上 receipt-info.component.ts 裡,然後修改一下連結:

<p class="full button"><a [routerLink]="['/', path.success]">確認結帳</a></p>

咦?!這次連結怎麼又不一樣了?!

這是因為之前用的 .. ,它最多只能用一次,也就是像上面這樣:

<a [routerLink]="['..', path.checkoutFlow.receiptInfo]">下一步</a>

但如果要用兩次以上像是這樣的話:

<a [routerLink]="['..', '..', path.success]">下一步</a>

路由機制讀取到的路徑就會變成 /checkout/../success ,然後就會被萬用路由轉回 /checkout/customer-info 了。

接下來打開素材 assets/css/ 底下的 checkout-3.css ,複製這邊的樣式設定到 receipt-info.component.css 裡,然後用上次提到的全部取代的功能將 .container article 全部取代為空字串。

像這樣:

Imgur

樣式設定完之後,這頁有個可以 swtich 的按鈕可以切換要顯示電子發票的表單區塊還是郵寄發票的表單區塊:

Imgur

這塊要怎麼處理咧?!

其實很簡單,利用我們學過的事件綁定再加上 *ngIf 來判斷要顯示的區塊就可以了!

首先我想先用 CLI 建個列舉來代表不同寄送方式:

ng generate enum checkout/receipt-info/send-type

然後打開 send-type.enum.ts , 輸入以下程式碼:

/**
 * 寄送發票的方式的列舉
 *
 * @export
 * @enum {number}
 */
export enum SendType {

  // 用 Email 寄送電子發票
  EMAIL,

  // 寄送實體發票到指定地址
  ADDRESS

}

接下來將這個列舉 import 到 receipt-info.component.ts 裡使用並將程式碼調整成這樣:

import { Component, OnInit } from '@angular/core';

// Constant
import { appPath } from 'src/app/app-path.const';

// Enum
import { SendType } from './send-type.enum';

@Component({
  selector: 'app-receipt-info',
  templateUrl: './receipt-info.component.html',
  styleUrls: ['./receipt-info.component.css']
})
export class ReceiptInfoComponent implements OnInit {

  /**
   * 給 Template 用的路由定義
   *
   * @memberof ReceiptInfoComponent
   */
  path = appPath;

  /**
   * 給 Template 用的寄送方式列舉
   *
   * @memberof ReceiptInfoComponent
   */
  sendType = SendType;

  /**
   * 當前的寄送類型,預設使用 Email
   *
   * @memberof ReceiptInfoComponent
   */
  selectedType = SendType.EMAIL;

  constructor() { }

  ngOnInit() {
  }

  /**
   * 切換寄送類型
   *
   * @param {number} type - 欲切換的寄送類型
   * @memberof ReceiptInfoComponent
   */
  switch(type: number): void {
    this.selectedType = type;
  }

  /**
   * 傳入的寄送類型是否為當前所選擇的寄送類型
   *
   * @param {number} type - 欲判斷的寄送類型
   * @returns {boolean}
   * @memberof ReceiptInfoComponent
   */
  didSelected(type: number): boolean {
    return this.selectedType === type;
  }

}

然後我們再把 receipt-info.component.ts 裡的這段 HTML :

<div class="segment font-light-green">
  <ul>
    <li class="selected"><a href="javascript:;">電子發票</a></li>
    <li><a href="checkout-3-2.html">郵寄發票</a></li>
  </ul>
</div>

改成:

<div class="segment font-light-green">
  <ul>

    <li [class.selected]="didSelected(sendType.EMAIL)">
      <a
        href="javascript:;"
        (click)="switch(sendType.EMAIL)"
      >
        電子發票
      </a>
    </li>

    <li [class.selected]="didSelected(sendType.ADDRESS)">
      <a
        href="javascript:;"
        (click)="switch(sendType.ADDRESS)"
      >
        郵寄發票
      </a>
    </li>

  </ul>
</div>

然後再把這段 HTML :

<form>

  <p class="full">
    <label for="email">電子郵件信箱</label>
    <input type="email" id="email" placeholder="example@email.com">
  </p>

  <p class="full">
    <label for="company-code">統一編號(選填)</label>
    <input type="text" id="company-code" placeholder="12345678">
  </p>

</form>

<ng-container></ng-container> 將兩個 <p></p> 像這樣包起來:

<form>

  <!-- 電子發票所需之表單區域開始 -->
  <ng-container>

    <p class="full">
      <label for="email">電子郵件信箱</label>
      <input type="email" id="email" placeholder="example@email.com">
    </p>

    <p class="full">
      <label for="company-code">統一編號(選填)</label>
      <input type="text" id="company-code" placeholder="12345678">
    </p>

  </ng-container>
  <!-- 電子發票所需之表單區域結束 -->
  
</form>

接著再新增一個 <ng-template></ng-template> ,然後把素材 pages/ 裡的 checkout-3-2.html 打開,把以下這段的 HTML 複製下來:

Imgur

貼到剛剛新增的 <ng-template></ng-template> 裡,像這樣:

<form>

  <!-- 電子發票所需之表單區域開始 -->
  <ng-container>
    <!-- ... -->
  </ng-container>
  <!-- 電子發票所需之表單區域結束 -->

  <!-- 郵寄發票所需之表單區域開始 -->
  <ng-template>

      <p class="address full">

        <label for="address">地址</label>

        <select name="city" id="city">
          <option value="高雄市">高雄市</option>  
        </select>

        <select class="float-right" name="county" id="county">
            <option value="新興區">新興區</option>  
        </select>

        <input type="text" id="address" placeholder="幸福路 520 號">

        <span class="same-checkbox">
          <input type="checkbox" name="same-address" id="same-address">
          <label for="same-address">同運送地址</label>
        </span>

      </p>

      <p class="full">
        <label for="company-code">統一編號(選填)</label>
        <input type="text" id="company-code" placeholder="12345678">
      </p>

  </ng-template>
  <!-- 郵寄發票所需之表單區域結束 -->
  
</form>

最後再把 *ngIf 的判斷加入:

<form>

  <!-- 電子發票所需之表單區域開始 -->
  <ng-container *ngIf="didSelected(sendType.EMAIL); else addressArea">
    <!-- ... -->
  </ng-container>
  <!-- 電子發票所需之表單區域結束 -->

  <!-- 郵寄發票所需之表單區域開始 -->
  <ng-template #addressArea>
    <!-- ... -->
  </ng-template>
  <!-- 郵寄發票所需之表單區域結束 -->
  
</form>

完成了!來看看效果吧:

Imgur

好的,如此一來我們就完成了結帳頁的套版了!!

至於甜點那頁的就留給你們練習囉?!

範例程式碼的部份我會上傳到 Github 上讓大家參考,如果有任何的問題也歡迎在文章底下留言或是傳訊給我。

目前的系列賽文章我預計會再發兩篇文章,一篇是關於如果已經跟著這個系列的文章練習過也研讀過一遍的話,接下來的學習方向以及學習資源分享;另外一篇則是完賽心得。

敬請期待!!


上一篇
[Angular 深入淺出三十天] Day 29 - Angular 小學堂(四之三)
下一篇
[Angular 深入淺出三十天] Day 31 - 三十天之後
系列文
Angular 深入淺出三十天33

1 則留言

0
開心學編程
iT邦新手 5 級 ‧ 2018-11-23 15:38:48

有個問題額,用ng build --prod打包的話,會報錯
Invalid configuration of route '': routes must have either a path or a matcher specified

Leo iT邦新手 4 級‧ 2018-11-23 18:35:36 檢舉

非常感謝你發現問題!!

麻煩你稍等我一下,我處理完再回報狀況讓你知道!

Leo iT邦新手 4 級‧ 2018-11-23 20:07:01 檢舉

開心學編程,我這邊試過是沒有問題的耶~~~@@

如下圖所示:

Imgur

會不會是你那邊有調整過程式碼?

你再試試看~ 如果有什麼問題再 PO 上來我們一起討論囉!

catstar iT邦新手 5 級‧ 2018-12-06 10:55:07 檢舉

同一樓問題,編譯時不會有問題
是必須用瀏覽器去檢視console才會出現
使用ng build 不會出錯
使用ng build --prod console報錯
部屬至IIS,ROUTE中的USEHASE已設為TRUE

我要留言

立即登入留言