2016年1月6日 星期三

Ionic 簡易入門

  作者雖然比較習慣jQueryMobile 開發Hybrid App但是隨著angular.js 的火紅,不少框架也有專門為angular.js量身打造,雖然入門的門檻較高,各位也可以參考看看ionic版的Hybrid APP,選擇適合自己喜歡的框架吧。

一、初步的網頁架構說明

ionic的路由是非常重要的部份,因為網頁的頻寬很有限,尤其是手機上的瀏覽速度更加緩慢情況下,當然就只需要把部份的網頁碼充填某個<div></div>即可,好比jQeruyMobile page是一樣的意思,所以<ion-nav-view></ ion-nav-view>就好比空白的<div></div>,要用到頁面再動態充填裡面即可

<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no, width=device-width">
    <title></title>
    <link href="lib/ionic/css/ionic.css" rel="stylesheet">
    <link href="css/style.css" rel="stylesheet">
    <script src="lib/ionic/js/ionic.bundle.js"></script>
    <script src="cordova.js"></script>
    <script src="js/app.js"></script>
    <script src="js/controllers.js"></script>
  </head>
<!—如同Angular ng-app來啟動angular  -- >
<body ng-app="starter">  
<!—如同Angular 的路由會自動把範本(home.htm,details.html帶入-- >   
   <ion-nav-view animation="slide-left-right"></ion-nav-view>
<!—如同實際外部產生一隻home.html,為了減少流量所以合併在同一隻程式  —>     
    <script id="home.html" type="text/ng-template"> </script>
    <script id="details.html" type="text/ng-template"></script>
</body>
</html>
    
ionic 要如何動態充填網頁到<ion-nav-view></ ion-nav-view> ,從www/js/app.js就是幕後最大的推手,如下面所設定的範本中state就是用/url來讀取用哪個範本(templateURL)controller所以每個獨立頁面都需要設定一個state
很重要,很重要,很重要!  

.config(function ($stateProvider, $urlRouterProvider) {
    $stateProvider
      .state('home', {
          url: "/home",
          templateUrl: "home.html",
          controller: "AppCtrl" 
      })
      .state('details', {
          url: "/details/:id",  //可以傳遞參數,參數名稱可以自訂,但要加上
          templateUrl: "details.html",
          controller: function ($stateParams, $scope) {
              $scope.params = $stateParams;
          }
      });
    $urlRouterProvider.otherwise("/home");  <!— 設定預設的路徑  -- >
});
 補充說明:ionic 有分幾種樣式tabs設定state的參數也有些不一樣,還請各位不要囫圇吞棗
  
二、瞭解angularfactory

相信大家早就瞭解factoryservice的差異,雖然它們功能用法差不多,但它們的回傳的方式略有不同,factory 會以物件方式回傳,service會以本身this回傳,所以聰明的選擇當然是factory,因為不會有全域變數的衝突,尤其是網頁有自動壓縮的功能,自動js打包成一個檔案,這種的問題就能避免了,以下的範例透過factory達到MVC的效果,將資料獨立在Factory 函式中。

(function () {
    "use strict";
    angular.module("starter.services", []).factory("sqlLiteService", ["$rootScope", "$http", function ($rootScope, $http) {
        var sqlLiteService = {};
        var db = window.openDatabase("Database", "1.0", "clinicDatabase", 1000);
        //建立資料表
        sqlLiteService.createdTable = function () {
            if (db) {
                sqlList.executeNonQuery(db, "CREATE TABLE  IF NOT EXISTS  clinics (id INTEGER PRIMARY KEY AUTOINCREMENT, clinicName TEXT, clinicIP TEXT, user TEXT, password TEXT)");
            }
        }
        //新增資料表
        sqlLiteService.add = function (clinicName, clinicIP, user, password) {
            var sqlValue = "INSERT INTO clinics (clinicName,clinicIP,user,password) VALUES('" + clinicName + "'," + "'" + clinicIP + "','" + user + "','" + password + "')";
            sqlList.executeNonQuery(db, sqlValue);
        }
        //修改資料表
        sqlLiteService.edit = function (id, clinicName, clinicIP, clinicUser, clinicPassword) {
            var query = "update clinics set clinicName='" + clinicName + "',clinicIP = '" + clinicIP + "',user = '" + clinicUser + "',password ='" + clinicPassword + "' where id=?";
            sqlList.executeNonQueryWithID(db, query, id);
        }
        //刪除資料表
        sqlLiteService.delete = function (id) {
            sqlList.executeNonQueryWithID(db, "delete from clinics where id=?", id);
        }
       //讀取資料表
        sqlLiteService.read = function (id,callback) { 
       //因為sqlLite 本身非同步傳遞資料,需要回傳資料,只能呼叫 callback 回傳資料
            sqlList.executeSQL(db, "select * from clinics where id=" + id, function (readData) {
                callback(readData.item(0));
            });
        }
        sqlLiteService.list = function (callback) {
            var listClinics = {};
            sqlList.executeSQL(db, "select * from clinics", function (objData) {
                callback(objData);
            });
        }
        return sqlLiteService;
    }]);
})();
補充說明: sqlList 請參考http://joyceevent.blogspot.tw/2015/09/codova-sqlite-crud.html

    然後開始注入factory controller 中,並呼叫factory的物件,就可以開始使用sqlLiteService , 非常特別的是ionic 透過controller轉換其他頁面時,需要呼叫$location.path(‘xxx.html’) Ionic 就會依據app.js找到配對的URL,並將對應的templateUrl 注入<ion-nav-view>

angular.module('starter.controllers', ['starter.services'])
.controller('AppCtrl', function ($scope, $location, sqlLiteService) {  
    sqlLiteService.createdTable(); //新增資料表
    sqlLiteService.list(function (objData) {
        $scope.playlists = [];
        for (var i = 0; i < objData.length; i++) {
            $scope.playlists.push(objData.item(i));
        }
    });
  $scope.add = function () {
      $location.path("/add");
      $scope.editTitle = "新增設定";
  }
  $scope.edit = function (path) {
      $location.path(path);
      $scope.editTitle = "修改設定";
  }
})

    當我們萬事具備時,就把資料呈現在我們的畫面中,此時我們的viewlayout 應該事先已經準備完成,如下所示

<body ng-app="starter">
<!—Ionic back  -- >
    <ion-nav-bar animation="nav-title-slide-ios7" class="bar-positive">
        <ion-nav-back-button class="button-icon ion-arrow-left-c">
        </ion-nav-back-button>
    </ion-nav-bar>
<!—Ionic 的動態注入頁面  -- >
    <ion-nav-view animation="slide-left-right"></ion-nav-view>   

    <script id="home.html" type="text/ng-template">       
        <ion-view view-title="XX小幫手" cache-view="false">
<!— cashe-view 每次回到home的頁面,就會重新讀取資料  —>
            <ion-content>
                <ion-list>
                    <ion-item class="item-button-right" ng-repeat="item in playlists">
                        <div class="item-content">
                            <a href="#/details/{{item.id}}">
                                <h2>{{item.clinicName}}</h2>
                                <p>{{item.clinicIP}}</p>
                            </a>
                        </div>
                        <div class="button" ng-click="edit('/edit/{{item.id}}')">
                            <a class="button button-icon icon ion-settings"></a>
                        </div>
                    </ion-item>
                </ion-list>
                <button class="button button-block button-calm" ng-click="add()">
                    新增
                </button>
            </ion-content>
        </ion-view>
    </script>
</body>

補充說明 : Ionic ng-model的宣告名稱中需要加『.』這令作者無法理解,如果宣告名稱中沒有『.』,就無法在controller中取得ng-model的值

Ionic ng-model的名稱宣告的範例
<ion-view view-title="{{editTitle}}">
    <ion-content>
        <div class="list">
            <label class="item item-input item-stacked-label">
                <span class="input-label">診所名稱</span>
                <input type="text" ng-model="formData.clinicName" placeholder="請輸入名稱">
            </label>
            <label class="item item-input item-stacked-label">
                <span class="input-label">診所IP</span>
                <input type="text" ng-model="formData.clinicIP" placeholder="請輸入診所IP">
            </label>
            <label class="item item-input item-stacked-label">
                <span class="input-label">帳號</span>
                <input type="text" ng-model="formData.user" placeholder="請輸入帳號">
            </label>
            <label class="item item-input item-stacked-label">
                <span class="input-label">密碼</span>
                <input type="text" ng-model="formData.password" placeholder="請輸入密碼">
            </label>
        </div>
        <button class="button button-block button-positive" ng-click="save()">
            儲存
        </button>
    </ion-content>
</ion-view>
   
    Ionic controller 不論讀取或設定,都要先初始化物件,如上所示viewng-model宣告中formData視為物件,所以要始初化formData,才能讀取或設定ng-model
  
.state('edit', {
        url: "/edit/:id",
        templateUrl: "templates/edit.html",
        controller: function ($stateParams, $scope, $location, sqlLiteService) {
            $scope.params = $stateParams;
            $scope.editTitle = "編輯設定";                   
            if ($stateParams != null) {
                sqlLiteService.read($stateParams.id, function (sqlRtuenObj) {
                    $scope.formData = {}; <!— 要先初始化 formData -- > 
                    $scope.formData.clinicName = sqlRtuenObj.clinicName;
                    $scope.formData.clinicIP = sqlRtuenObj.clinicIP;
                    $scope.formData.user = sqlRtuenObj.user;
                    $scope.formData.password = sqlRtuenObj.password;
                })
            }           
            $scope.save = function () {
                 sqlLiteService.edit($stateParams.id, $scope.formData.clinicName, $scope.formData.clinicIP, $scope.formData.user, $scope.formData.password);
                $location.path("/home");
            }
        }
    });


補充說明:  ionic 的框架架構
作者為了讓clinicName controller 設定名稱,但是無法順利取得名稱,每次要重新刷新畫面才能讀取值,結果之後要在ion-view 加上cache-view="false" 屬性,就等同我們重新刷新畫面

其次選單框架的架構,後來終於搞懂順序關係,順序如下所示
<ion-view cache-view="false">
    <ion-side-menus enable-menu-with-back-views="true">
<!—放置ionic menu 的內容 -->
        <ion-side-menu-content>
            <!—放置ionic menu 的抬頭bar -->
            <ion-nav-bar class="bar-positive">
  <!--倒回鍵-->
                <ion-nav-back-button></ion-nav-back-button>
                <!--menu 鍵放置在右邊-->
                <ion-nav-buttons side="right">
                    <!—menu 的方向 由右邊往左展開-->
                    <button class="button button-icon button-clear ion-navicon" menu-toggle="right"></button>
                </ion-nav-buttons>
</ion-nav-bar class="bar-positive">

  <!--右邊 menu 選擇的內容範例  -->
        <ion-side-menu side="right">
            <ion-header-bar class="bar-positive">
                <h1 class="title">選單</h1>
            </ion-header-bar>
            <ion-content>
                <ion-list>
                    <ion-item menu-close ng-click="login()">
                        Login
                    </ion-item>
                    <ion-item menu-close href="#/app/search">
                        Search
                    </ion-item>
                    <ion-item menu-close href="#/app/browse">
                        Browse
                    </ion-item>
                    <ion-item menu-close href="#/app/playlists">
                        Playlists
                    </ion-item>
                </ion-list>
            </ion-content>
        </ion-side-menu>
    </ion-side-menus>
</ion-view



   Ionic的動畫換頁的css 效果似乎很少,不像jQueryMobile 效果,但Ionic的維護比JQueryMobile 方便,如果以上仍有錯誤的地方,還請前輩們加以指正,感謝各位的到訪,希望這篇文章對大家有所幫助。