2016年8月11日 星期四

Angular 1.2.30 應用程式功能建置

Angular 本身就有足夠的擴充,本章節使用 ng-route, ui-route, oc-lazyload, angular-translate 以及 postix 完成應用程式最基礎的建置。


規劃 index.html 足夠載入其他 html 的空間

為了要將其他 html 併入最主要的 html, 首要是需要規劃一個 div 之類的空間,可以給 angular-router 去做不刷新頁面的載入。

<html>
<head>
    <title>Hallo, angular</title>
</head>
<body>
    <div id="container" ui-view="">
    </div>
</body>
</html>
請注意上方在 div 中的 "ui-view" ,這是屬於 ui-router 載入其他 html 文件到這個 div 的識別標籤。

Angular 的路由器網址格式

angular 的網址格式,是在 html 文件,或是倒斜線後面加一個 # 號作為連結,在 # 號後面的路徑就是 angular 應用程式識別的路徑,舉個例子:

xxx.com/index.html/#/this/is/router

又或是

xxx.com/#/this/is/router

而我們在建置非刷新格式的功能時,就需要定義 # 號後面的路徑。

UI-Router, Angular Router

在這邊,你需要知道 angular-router 是採用傳統的 "/hallo/test" 來載入,而 ui-router 是採用 state 的方式載入,網址寫起來會像這樣 :

“ hallo.test ”

用來取代

/hallo/test

但是,你在建置時也必須先建置 hallo 的路由,才可以建置 hallo 底下的子路由 hallo.test。

理解一下這段代碼,你就會知道:

var test = angular.module('apostle',['ngRoute','oc.lazyLoad','ui.router','pascalprecht.translate','ngFileUpload']);
test.config(function($stateProvider,$urlRouterProvider,$routeProvider,$translateProvider,$ocLazyLoadProvider){
 $urlRouterProvider.otherwise('http://google.com/?q=88888888'); //如果發現沒有在清單內的路由,就導向

 $stateProvider.state('index',{
  url:'/',
  template:'<h1>Nothing can be loaded!</h1>',
  redirectTo: 'account.user'
 })
 .state('account',{
  url:'/account',
  template:'<div id="container" ui-view></div>',
  redirectTo:"account.user"
 })
 .state('account.user',{
  url:'/user',
  templateUrl:"./pages/account/user.html",
  controller:'userSettingsController',
  resolve: {
         loadMyCtrl: ['$ocLazyLoad', function($ocLazyLoad) {
          loadScript("./js/plugin/Croppie/croppie.js");
          loadScript("./js/plugin/intl-tel-input/build/js/intlTelInput.min.js");
             return $ocLazyLoad.load({
                 name: 'userSettingsController',
                 files: ['./js/controllers/account/user.js']
             });
         }]
     }
 });

 //注意一下這個區塊,等等是用來介紹建置 $translate 多國語言功能放置的區塊
        //
        //
})
看到上面的每一個 state ,這個就是 ui-router 吃的路由方式,而這些 state 中的 url,就是指定名稱的路徑,換句話說,如果 test.aa 不想為 "/test/aa" ,你也可以把 url 改成其他的,像是 "/bye" ,那麼只要使用導向狀態 test.aa ,就會跳轉到 "/test/bye"。

最簡單看到導向狀態的方式,就是在 index.html 的選單中,把 <a href> 的連結,改成 <a ui-sref="test.aa"> ,這麼一來,這個 ui-router 就會自動找到 state 的狀態路徑。

然而,你發現第二個 state ,為何要在 template 加上一層原本在 body 就有的命名都一樣的 div 呢?

template 本身就可以直接顯示 html ,或是你可以用 templateUrl 來載入其他路徑的 html 。

template 執行後,將會複寫當初擁有 ui-view 識別字的 <div id="container" ui-view> ,這會讓子路由沒有參考的 <div  id="container" ui-view> (最主要是識別字 ui-view 已經被覆寫了) ,因為這一段已經被覆寫,所以訪問子路由只會是空白,因此我們只能把子路由前一段當作過度點,也許是分類方便的關係吧,加上 template 後, 再導向到主功能去。

LoadScript, ocLazyLoad

從上方的 js 程式碼中, loadScript 帶有 js 路徑的參數,這段是我寫的後載入 js 強化版 (? 。
請加在你的 router 文件之前。
function loadScript(url){
    var resource = document.createElement('script'); 
    resource.src = url;
    var script = document.getElementsByTagName('script')[0];
    script.parentNode.insertBefore(resource, script);
}
這麼一來只要需要做後載入 js 來源文件,就可以用到。

 ocLazyLoad 可以輔助  ui-router 後載入 controller 的問題
因此先下載 ocLazyLoad ,可以參考上方的做法,先找到你的 controller 的 js 文件,寫一段範例:
angular.module('test').controller('userSettingsController',function($scope,$translate){
    console.log("Loaded page and controller: userSettingsController");
});
之所以要寫 angular.module('test').controller ,是因為我發現寫 test.controller() 都沒反應,所以只能這樣寫,或許論壇上有更好的解法!

Rest API , Postfix, Header 在 Post 正確傳輸方法

postfix 這個模組解決了 angular 在 post 上的一些問題,當你在 java 後端接收不到 post 資料時,不妨可以先下載,或是使用這段 code 加入在 $http.post 前面:
$http.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
/**
 * The workhorse; converts an object to x-www-form-urlencoded serialization.
 * @param {Object} obj
 * @return {String}
 */ 
var param = function(obj) {
  var query = '', name, value, fullSubName, subName, subValue, innerObj, i;

  for(name in obj) {
    value = obj[name];

    if(value instanceof Array) {
      for(i=0; i<value.length; ++i) {
        subValue = value[i];
        fullSubName = name + '[' + i + ']';
        innerObj = {};
        innerObj[fullSubName] = subValue;
        query += param(innerObj) + '&';
      }
    }
    else if(value instanceof Object) {
      for(subName in value) {
        subValue = value[subName];
        fullSubName = name + '[' + subName + ']';
        innerObj = {};
        innerObj[fullSubName] = subValue;
        query += param(innerObj) + '&';
      }
    }
    else if(value !== undefined && value !== null)
      query += encodeURIComponent(name) + '=' + encodeURIComponent(value) + '&';
  }

  return query.length ? query.substr(0, query.length - 1) : query;
};

// Override $http service's default transformRequest
$http.defaults.transformRequest = [function(data) {
  return angular.isObject(data) && String(data) !== '[object File]' ? param(data) : data;
}];

如果你有使用 postfix ,但是還是發現後段收不到 post 方法的資料,你可以這樣做:
var config = {
    headers : {
        'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8;'
    }
}

var data = $.param({
    first_name:"hello"
});

$http.post("../api/account/ChangeUserName",data,config).then(function(response){...

Get 很正常,但不要貪圖方便用 get 來做什麼更換資料的 api, 這個很 87。

Angular-translate 多國翻譯,與帶來的後遺症製作 loading page 來解決它

下載來 angular-translate ,最基礎的用法就是:
$translateProvider.useUrlLoader('/i18n/zh_TW.json'); //載入中文
$translateProvider.useUrlLoader('/i18n/en_US.json'); //載入英文
$translateProvider.preferredLanguage('zh_TW'); //設定預設語言是中文

...

要變更的時候再到 controller 導入 $translate 。 (記得,剛剛那個是 provider ,是放在 angular.config 中的,這個是放 controller 的)
$translate.use("en_US");

不過,如果你有幾百個語言,那大概會載入到爆炸,因此,你可以多下載一個官方再推出的 Angular Translate UrlLoader ,它的用法如下:
$translateProvider.useStaticFilesLoader({
    prefix: 'i18n/locale-',
    suffix: '.json'
});

...

$translate.use("en_US");

UrlLoader 這個模組可以讓你手動找到語言的後輟詞來找到這個語言檔案。

不過,用久了你就會發現
<p translate="">translate_hello</p>
<p translate="translate_hello"><p>
{{ "translate_hello" | translate }}

不管是那用法,都會出現翻譯的空白期,或是滿滿的識別字字串,非常難看。

這時候可以來製作一個 html loading 來搞定:
你可以在頁面載入之前,設定這個 loading 先處於顯示狀態,然後再 translate 翻譯完成後,將它關閉。
而這個事件為:
$rootScope.$on('$translateChangeSuccess', function () {
    $("#loading").hide();
    console.log("TRANSLATE SUCCESSFULLY!");
});
請務必把這段寫在最早次載入的 body 之類的地方,所加的 controller 中,並注意,這個事件是 $rootScope

沒有留言:

張貼留言

© Mac Taylor, 歡迎自由轉貼。
Background Email Pattern by Toby Elliott
Since 2014