程序员人生 网站导航

Mobile first! Wijmo 5 + Ionic Framework之:费用跟踪 App

栏目:互联网时间:2014-11-30 09:47:50
??

费用跟踪利用采取了Wijmo5和Ionic Framework创建,目的是构建1个hybird app。

我们基于《Mobile first! Wijmo 5 + Ionic Framework之:Hello World!》的环境,将在本教程中完成费用跟踪App的构建。下面的代码结构是本教程完成要到达的效果,请预先创建好文件和目录。

www/                     --> 工程根目录
  index.html         --> app 布局文件 (主HTML文件)
  css/                   --> css 目录
  js/                    --> javascript 目录
    app.js               --> 主模块
    app.routes.js        --> 主路由模块
    controllers/         --> app控制器目录
    models/                 --> app模块目录
    services/        --> app 数据Service目录
  templates/             --> angularJs视图代码目录(通过UI-Router调用) 
  lib/           --> 第3方类库, 包括Ionic, Wijmo, jQuery等

 

数据模型(Data Model)

在费用跟踪App中,我们先要创建Data Model,E-R图以下

etErdDiagram-300x145

 

  • Category:开支分类
  • Expense:开支记录
  • Budget: 预算(下面会用到)

在代码中,我们需要在www/js/services构建AngularJs Services来对数据模型进行建模。我们会用到HTML5的localStorage进行数据本地存储, 采取的格式为JSON。 需要注意的是,HTML5本地存储只能存字符串,任何格式存储的时候都会被自动转为字符串,所以读取的时候,需要自己进行类型的转换。目前我们实现的是HTML5 本地存储,有兴趣的读者还可移植为RESTful API、SQLite等数据存储方法。

运行demo后,通过Chrome调试查看的本地存储截图:

image

阅读开支历史记录

在开支历史页面中,提供了2个功能:阅读开支历史记录、删除开支记录。为了实现这些功能,在wwwjscontrollershistory.js文件中,添加以下代码:

//从localStorage取得开支数据
$scope.expenses = ExpenseSvc.getExpensesWithCategory();

这行代码提供了返回本地存储的开支记录。ExpenseSvc 服务,不但返回了开支对象,同时也返回了开支分类。基于这些数据,在

www emplateshistory.tpl.htm文件中,在ion-context指令内添加Ionic的ion-list指令,代码以下:

<ion-view title="History">
  <ion-nav-buttons side="right">
    <a class="button button-icon icon ion-plus" href="#/app/create"></a>
  </ion-nav-buttons>
  <ion-content class="has-header">
    <ion-list>
      <ion-item ng-repeat="expense in expenses | orderBy:'date':reverse track by expense.id" class="item item-icon-left">
        <i class="icon ion-record {{ expense.category.cssClass }}"></i>
        <div class="row">
          <div class="col⑸0">
            <h2>{{ expense.title }}</h2>
          </div>
          <div class="col⑵5">
            <small class="light-grey">{{ expense.date | date: 'shortDate' }}</small>
          </div>
          <div class="col⑵5">
            {{ expense.amount | currency }}
          </div>
        </div>
      </ion-item>
    </ion-list>
  </ion-content>
</ion-view>

 

ion-list指令,用于生成排序的HTML列表,其子标签ion-item指令用于生成HTML列表项。 在ngRepeat指令中,我们使用了“track by”,目的是在对开支集合修改时提升性能,相干教程可参考博客《Using Track-By With ngRepeat In AngularJS 1.2 》。

现在添加删除开支记录按钮,用于向左滑动出现删除按钮、点击删除可删除开支记录。

在ion-item标签关闭前添加ion-option-button标签,代码以下:

<ion-option-button class="button button-assertive" on-tap="confirmDelete(expense.id)">Delete</ion-option-button>

ion-option-button 是Ionic提供的另外一个指令,用于在ion-item指令内试用。默许的,ion-option-button 是隐藏的,当在ion-item内向左滑动,则按钮会可见。这个功能特别对小屏幕装备非常重要。另外,还可通过该指令内置的can-swipe来实现对这个权限的管理--如有的用户不允许删除操作权限。

在删除函数中(控制器),可看到代码片断以下:

function confirmDelete(expenseId) {
   // delete expense by its id property
   $scope.expenses = ExpenseSvc.deleteExpense(expenseId);
}

通过这个代码,我们调用ExpenseSvc服务的deleteExpense进行删除指定的开支记录(expenseId),同时这个方法也会返回开支记录集适用于更新页面数据。在真实的场景中,删除记录返回全部集合不是最理想的,但在此处我们用于演示说明。可动手试着删除几行数据试试。

image

另外,在删除这类比较危险的操作中,应当需要添加对话框再次提示1下用户。这里我们使用了Ionic提供的$ionicActionSheet service服务来实现。更新wwwjscontrollershistory.js控制器代码的confirmDelete函数以下:

//删除开支记录
$scope.confirmDelete = function (expenseId) {
  //ionic的 确认对话框
  // show()函数返回了1个函数,用于隐藏actionSheet
  var hideSheet = $ionicActionSheet.show({
    titleText: 'Are you sure that you'd like to delete this expense?',
    cancelText: 'Cancel',
    destructiveText: 'Delete',
    cancel: function () {
      // 如果用户选择cancel, 则会隐藏删除按钮
      $ionicListDelegate.closeOptionButtons();
    },
    destructiveButtonClicked: function () {
      // 通过id删除开支记录
      $scope.expenses = ExpenseSvc.deleteExpense(expenseId);

      // 隐藏对话框
      hideSheet();
    }
  });
};

ionicActionSheet服务提供了自定义接口,可实现各种提示对话框。上面代码实现的提示对话框效果截图以下:

 image

 

创建开支记录

点击History页面右上角的image可实现手工创建1条新的开支记录。在www emplatescreateExpense.tpl.htm文件中,代码以下:

<ion-view title="Create">
    <ion-content class="has-header padding">
        <form name="frmCreate">
            <div class="custom-form-list list">
                <label class="item item-input">
                    <i class="icon ion-alert-circled placeholder-icon assertive" ng-show="!frmCreate.title.$pristine && frmCreate.title.$invalid"></i>
                    <input name="title" type="text" placeholder="Title" ng-model="expense.title" ng-maxlength="55" required>
                </label>
                <wj-input-number value="expense.amount" min="0" step="5" format="c2"></wj-input-number>
                <wj-calendar value="expense.date"></wj-calendar>                
                <wj-combo-box items-source="categories" 
                              display-member-path="htmlContent"
                              selected-value-path="id"
                              selected-value="expense.categoryId" 
                              is-editable="false" 
                              is-content-html="true"></wj-combo-box>
                <label class="item item-input">
                    <textarea placeholder="Description" ng-model="expense.description"></textarea>
                </label>
            </div>
            <div class="button-bar">
                <button type="button" class="button button-dark icon-left ion-close" on-tap="cancel()">Cancel</button>
                <button type="button" class="button button-balanced icon-left ion-checkmark" on-tap="addExpense()" ng-disabled="frmCreate.title.$invalid">Save</button>
            </div>
        </form>
    </ion-content>
</ion-view>

这里使用ion-view 和 ion-content 指令进行内容展现。然后再添加Form,用ng-show指令验证输入内容---Wijmo的指令已在输入门限做了限制,故不需要验证。同时Wijmo Calendar 和InputNumber应当是自解释,ComboBox中可能不是。

ComboBox关联数据模型中的开支分类,我们通过其itemsSource属性进行数据绑定。ComboBox的displayMemberPath 用于设置显示内容,selectedItem的selectedValue用于选择开支分类的id属性。

在createExpense 控制器中,可看到以下的代码片断:

// 初始化Expense object
$scope.expense = new Expense('', 0, new Date(), '', null);

// 取得HTML类型的开支分类
$scope.categories = CategorySvc.getCategoriesWithHtmlContent();

// 用localStorage存储开支数据
$scope.addExpense = function () {
  // insert expense
  ExpenseSvc.insertExpense($scope.expense);
  $scope.cancel();
};

// 取消方法 (如,可回到主页面)
$scope.cancel =  
    
------分隔线----------------------------
------分隔线----------------------------

最新技术推荐