[無瑕的前端-5] 疑難雜症篇

Angular、TypeScript實作前端MVC架構

前言

承接之前的系列文,這裡主要整理自己在實作過程中遇到的一些問題,以免之後浪費寶貴的時間。下列為簡單描述的幾個情境讓我困惑之處。

  1. 注入到Controller的物件是如何對應
  2. Controller呼叫封裝$http成Service的簡單範例
  3. Html如何引用Controller的資料
  4. 常見問題

 

注入到Controller的物件是如何對應

注入只看順序性,名稱甚至可以完全不同,以上方例子而言,Memer在注入的時候是大寫,但是在constructor內是小寫的member。當然,也可以取完全不一樣的名字,因為controller是完全針對view在設計,因此,宣告一個適當的名字會有助於對程式碼的理解。

另外,member後方的:ViewModel.MemberViewMoel是在宣告型別,基本上,在TypeScript世界裡,程式設計師就是神,神主宰一切,所以神說它是甚麼型別,它就會是甚麼型別。但是說穿了,這只是在編譯時期TypeScript會依照你宣稱的型別來幫你做檢查而已,所以到了執行階段,TypeScript發現這個物件的型別跟你宣告的不一樣,而剛好又對物件不存在的屬性做了讀取的動作,程式還是會爆炸的,這點要特別注意。

 

Controller呼叫封裝$http成Service的簡單範例

在angular的世界裡,想要與後端的WebApi作互動,一般來說都會使用$http這個物件,所以把它封裝在Service裡面會是一個不錯的設計方式。

以下就是簡單的Service設計,提供一個GetMember的方法,並且接受一個參數model,型別為ViewModel.MemberViewModel。

 export class MemberService {

        GetMember(model:ViewModel.MemberViewModel) {
                var url = 'http://localhost/api/member';
                return this.$http.post<void>(url,model);
            }
    }

接下來處理controller,把這個定義好的服務注入進來,然後在controller定義一個方法來呼叫這個Service。這樣做的好處就是,其他程式人員想要呼叫同一個WebApi時,使用這個Service就會得到intellisense的好處,也不需要知道太多實作的細節,只需要給予適當的參數即可。

 export class MemberController {
        static $inject = ['MemberService'];
        constructor(
             memberService: MemberService
        ) {
        }

        Submit(): void {
            let model :ViewModel.MemberViewModel ={
                Id:"1234"
            }
            memberService.GetMember(model);
        }

 

Html如何引用Controller的資料

Html跟Controller要關聯上是靠angular.ui.route來設定,可參考下方設定。

function MemberControllerRoute($stateProvider: angular.ui.IStateProvider) {
        $stateProvider
            .state('member',
            {
                url: '/member',
                templateUrl: 'TypeScripts/Controllers/MemberLogin/member.base.html',
                controller: 'BaseController',
                controllerAs: 'BaseCtrl',
                resolve: {
                  
                }
            })
}

透過templateUrl取得html頁面,然後透過controller屬性指定controller,這裡的名稱需要完全跟angular.module裡設定的controller名稱匹配,否則會出現錯誤。但有時controller的名稱太長,所以可以使用controllerAs的屬性來設定縮寫,而這個縮寫將會成為html頁面上controller的參考,html頁面可透過這個參考來取得controller內的資訊,以及呼叫定義的方法,可參考以下範例。

controller只定義一個Id的屬性供存取之用,下方的angular.module('Hank.Controllers')....則是將BaseController註冊到angular的module,第一個參數是Key(字串),必須要跟上方route設定的controller名稱匹配,第二個參數是實際型別。

module Hank.Controllers {
   export class BaseController {
         Id:string
        }
    }
    angular.module('Hank.Controllers')
        .controller('BaseController', BaseController);
}

Html的部分就可以透過route設定的簡稱BaseCtrl當作參考,直接讀取controller內的資訊。

<div >
  {{BaseCtrl.Id}}
</div>

 

常見問題

執行typeScript為何會出現下列畫面?

偵錯時請按ctrl+F5,並且不要直接在html頁面上按,不然它會直接去讀這個頁面,所以造成angular的東西會沒被載入而出現這個畫面。

 

angular.ui.route套件,巢狀的state跑不出來?

如果templateUrl的名稱一樣的話,就會導致畫面跑不出來。其實,也不應該有這兩個url需要一樣的需求,但這問題算是比較雷的,它沒有錯誤訊息,就單純畫面跑不出來而已,所以要多加注意。

 

結論

寫程式很吃筆記,不管是甚麼程式語言都有滿山滿谷的坑,就算自己不跳,別人也會挖給你跳。所以沒辦法避免不跳新坑,但卻有辦法一樣的雷不去踩第二次,而作筆記或寫部落格真的是一個很棒的方式,大家共勉之。