PartyBid 學習筆記 之 第一張卡片總結

此博客已棄,請轉至 此處

本文僅爲培訓期間應試做文,不具任何教學價值,具體問題請參考對應文章。javascript

AngularJS LOGO


前情提要

Party Bid 是一款基於 AngularJS 的安卓網頁應用。所謂安卓網頁應用,指的是應用徹底使用網頁開發模式構造(HTML + CSS + JavaScript),以後使用 Apache Cordova 工具將其生成爲安卓本地應用項目。css

對於應用內容的介紹,考慮到本文的面向讀者,此處再也不詳細說明,主要內容在於 開發過程當中所用到的技術我的學習的一些心得體會html


創建項目

爲了方便的直接創建出 AngularJS 項目,咱們須要使用到 Yeoman 工具。前端

安裝 Yeoman 的步驟已在 OSX 之 Web 開發環境配置文章中給出。java

核心步驟的命令行代碼以下:node

$ npm install -g yo

至此已經安裝好了 Yeoman ,隨後咱們經過其來建立一個 AngularJS 項目:git

詳細說明及內容拓展請參見 Yeoman 之 搭建 AngularJS 開發環境(待寫)github

1.安裝 Yoeman 的 AngularJS 模板生成器:web

$ npm install -g generator-angular

含義顧名思義。Yo (Yeoman 的一個組件,下文中都會具體說明每一個操做是 Yo, Grunt 或 Bower 的功能)能夠經過安裝相應的 generator 來實現功能拓展,除了 AngularJS 的以外,還有不少其餘的 generator 可使用。也就是說,經過拓展,Yo 理論上能夠適用於任何類型的項目。shell

2.新建一個文件夾並進入,文件夾名稱即爲項目名 party_bid(爲了不各類地方都加引號而拋棄空格),手動或者命令行以下:

$ mkdir party_bid
$ cd party_bid

3.使用 Yeoman 建立一個 AngularJS 項目:

$ yo angular

以後會有提示以下:

_-----_
    |       |    .--------------------------.
    |--(o)--|    |    Welcome to Yeoman,    |
   `---------´   |   ladies and gentlemen!  |
    ( _´U`_ )    '--------------------------'
    /___A___\    
     |  ~  |     
   __'.___.'__   
 ´   `  |° ´ Y ` 

Out of the box I include Bootstrap and some AngularJS recommended modules.

[?] Would you like to use Sass (with Compass)? (Y/n) 
[?] Would you like to include Bootstrap? (Y/n)
[?] Would you like to use the Sass version of Bootstrap? (Y/n) 
[?] Which modules would you like to include? (Press <space> to select)
❯⬢ angular-animate.js
 ⬢ angular-cookies.js
 ⬢ angular-resource.js
 ⬢ angular-route.js
 ⬢ angular-sanitize.js
 ⬢ angular-touch.js

上面列出了是否須要在 AngularJS 直接添加一些拓展組建,在此全選,主要理由以下:

  • 因爲目前還不是對 Sass 和 Bootstrap 特別瞭解,以優先保證可用性爲前提;
  • Party Bid 爲開發項目而不是真正的商業 Web App ,不須要對應用大小最簡化來保證用戶體驗;
  • 即使如今的需求中用不到 Sass ,但在 Sass 自己也能使用純 css ,以後也能夠須要對應用進行 Sass 重構。

所有選擇後結果顯示以下:

[?] Would you like to use Sass (with Compass)? Yes
[?] Would you like to include Bootstrap? Yes
[?] Would you like to use the Sass version of Bootstrap? Yes
[?] Which modules would you like to include? angular-animate.js, angular-cookies.js, angular-resource.js, angular-route.js, angular-sanitize.js, angular-touch.js

注:上述代碼可能會隨着平臺差別和程序版本而不一樣,請以本身的實際狀況爲準。

接下來可能生成的過程會比較漫長,在這段休息時間中能夠看看本博客的其餘內容 ^o^

在 AngularJS 項目生成完畢後(要肯定終端中是生成完畢中止而不是報錯中止的 =_=||),就會發現當前目錄中多了不少文件和文件夾:

  • app -- 應用的主文件夾,寫代碼的地方
  • test -- 放置測試文件的文件夾
  • node_modules -- Nodejs 安裝的程序包
  • bower.json -- Bower 的依賴配置文件
  • Gruntfile.js -- Grunt 的配置文件
  • package.json -- NodeJS 的依賴配置文件

4.在終端中開啓 Grunt 的內置服務器:

$ grunt serve

注意:grunt servegrunt server 都能開啓服務器,可是 grunt server 已經不被推薦使用。目前的狀況是使用 grunt server 會被重定向到 grunt serve ,因此功能上沒有任何區別。

接着 grunt 會自動在瀏覽器中打開 app 文件夾中的 index.html 頁面,若是可以正常現實內容及樣式的話說明咱們這個環節已經成功了。


配置項目

雖然已經創建了一個 AngularJS 的模板項目,可是咱們的命令行操做還並無結束。

或者說,Yo 的強大功能還遠不只僅如此。本文僅講解所用到的功能,拓展內容具體可參考 Yeoman 之 搭建 AngularJS 開發環境(待寫)

AngularJS 中,路由是一個核心功能,用來響應 url 並鏈接對應的 view 和 controller。關於 MVC 的更多內容能夠參考 AngularJS 之 MVC 架構開發介紹(待寫)

在有 Yo 的狀況下,咱們無需本身添加每個 view 和 controller 。在第一張卡片中,咱們須要用到三個頁面:"活動列表" , "活動報名" 和 "建立活動" ,爲此,咱們在終端中繼續輸入以下命令:(由於當前正在運行服務器,故可使用 "Command + N"(Mac)或 "Ctrl + N" 來建立一個新窗口,並再次回到當前文件夾)

$ yo angular:route ActivityList
$ yo angular:route ActivityRegister
$ yo angular:route CreateActivity

這樣就直接建立了 app/scripts/controllers/ 文件夾下的 activitylist.js , activityregister.jscreateactivity.js 三個 controller 文件,以及 app/views/ 下的 activitylist.html , activityregister.htmlcreateactivity.html 三個 view 文件,而且已經在 app/scripts/ 下的 app.js 中將模板和控制器相互關聯起來了,十分簡單粗暴。

不論在上面的命令中使用 PascalCase 仍是 camelCase ,文件名都會自動使用小寫,可是 controller 的名稱和路由中配置的路徑會按照上面命令中給出的大小寫來生成。

爲了代碼的可讀性和可維護性,咱們再新建一個 app/scripts/models 文件夾,並在其中添加一個 activity.js 文件用於處理一切與 activity 相關的數據操做。

特別注意,本身添加文件後也要在 index.html 中添加相應的引用。

...
<#script src='scripts/models/activity.js'></#script>
...

博文中禁止 script 標籤,請自行去掉#號。

引用添加的位置不要在它默認生成的腳本塊中,放在它的註釋塊外面。另外 activity.js 中的代碼模式爲:

function Activity(parameter) {
  //Do something
}

Activity.prototype.someMethod = function () {
  //Do something
};

Activity.anotherMethod = function () {
  //Do something
};

上面的代碼中,採用面向對象的方式,依次是 類的構造函數類的實例函數 以及 類函數


數據結構設計

首先分析數據結構,爲了保證良好的可讀性和可拓展性,採用 對象數組 的形式來在 locolStorage 中存儲活動。若是以後改用數據庫存儲的話,這樣也是最方便對接的。

activity.js 的主要代碼以下:

1.構造函數
function Activity(name, createdAt) {
    this.name = name;
    this.createdAt = createdAt;
}

在上面的代碼中, Activity 類的對象具備兩個字段(可能有些語言通常用屬性,效果都是差很少的),其中,createdAt 用於記錄活動建立時間,以便於排序。

注意:由於採用對象數組,雖然其自己的物理存儲數據已經能夠實現對活動的排序,可是物理存儲順序是絕對不足以做爲排序依據的,其具備很是明顯的不可靠性。即使不存儲建立時間,也須要指定一個 index 屬性來支持排序功能。除此以外,也能使用哈希表來做爲數據結構,以 index 做爲索引,這裏不做過多介紹。

2.讀取函數

此博客已棄,請轉至 此處

由於不針對任何實例,因此此處採用類函數(方法)實現,從 localStorage 中取出相應 json 數組並解析爲對象。

Activity.all = function () {
    return JSON.parse(localStorage.getItem('activities')) || [];
};

上面的代碼中,最後的邏輯或操做是爲了防止沒有任何數據時致使程序崩潰,所以返回一個空數組而不是 undefined 。而當非空時,因爲邏輯運算的短路法則,或運算及其後內容會直接被忽略。

3.存儲函數

爲了保持面向對象的純潔性,此處採用實例函數而非類函數來進行存儲,存儲在瀏覽器本地的 localStorage 中。

Activity.prototype.save = function () {
    var list = Activity.all();
    list.push(this);
    localStorage.setItem('activities', list);
};

無活動時跳轉

在卡片一中,存在以下需求:

在打開程序後判斷一下,是否已經存在已建立的活動,若是沒有,就要顯示「建立活動」頁面,引導用戶去建立一個活動。

因此,咱們須要在 controller 中增長相應的判斷。

首先,咱們來看看如今的路由配置,打開 app.js,部分代碼以下:

angular
  .module('partyBidApp', [
  ...
  ])
  .config(function ($routeProvider) {
    $routeProvider
      .when('/', {
        templateUrl: 'views/main.html',
        controller: 'MainCtrl'
      })
      ...
      .when('/ActivityList', {
        templateUrl: 'views/activitylist.html',
        controller: 'ActivitylistCtrl'
      })
      .when('/CreateActivity', {
        templateUrl: 'views/createactivity.html',
        controller: 'CreateactivityCtrl'
      })
      .otherwise({
        redirectTo: '/'
      });
  });

能夠看出,目前的默認頁面爲 views/main.html,對應的控制器爲 MainCtrl

爲此,咱們須要在 activity.jsmain.js 中添加一些代碼:

activity.js:

Activity.exist = function () {
    return (Activity.all()).length == 0;
};

在 model 中增長這個函數用於判斷活動列表是否爲空。

main.js:

angular.module('partyBidApp')
  .controller('MainCtrl', function ($scope, $location) {
    $scope.initiate = function () {
      var path = Activity.exist()? '/ActivityList': '/CreateActivity';
      $location.path(path);
    };
    
    $scope.initiate();
  });

上面的 controller 中的代碼,雖然只有 2 行(甚至能夠一行解決),但仍是定義了一個 初始化函數 並調用。這是一種習慣,至因而不是好習慣如今還不能肯定,只是隨着代碼量的增長我以爲這種方式可以使代碼變得更加 neat 。另外一個好處,如今還不能肯定是否有用,就是在頁面須要更新數據的時候能夠直接(被)調用。

特別注意要在控制器的定義函數參數中添加 $location ,固然,實際上除了 $location.path() 外,還有別的頁面跳轉方法,好比最簡單的 href 或者 AngularJS 的 ng-href 等,此處不作介紹。

接着運行 grunt serve ,就能看到如今已經跳到了活動報名頁面,雖然目前整個頁面上就只有一行 "This is the CreateActivity view."


無活動時返回按鈕不顯示

在卡片一中,存在以下需求:

無已建立活動狀況下,進入」建立活動「頁面,」返回「按鈕不顯示。

AngularJS 中有一個便捷的設置 DOM 可見性的方式,ng-showng-hide ,用法上沒有任何區別,只是效果相反。

注意:全部 ng-someThing 屬性都是 AngularJS 中的指令(directive),若是繼續深刻學習的話讀者可能以後會建立本身的指令。

首先要在 views/createactivity.html 中添加一個按鈕,位置等樣式以及 ng-click 事件由讀者自行實現:

...
<button class='' ng-show='{ {activity_exist} }' ng-click=''>返回</button>
...

其中, { { } }(雙層花括號,無空格,因格式問題沒法直接打出。)是 AngularJS 的數據綁定語法,表示其值綁定到了 $scope.activity_exist 變量上。

爲此,咱們須要在 CreateactivityCtrl 中對其進行賦值(僅給出核心代碼,下同):

...
$scope.activity_exist = !Activity.exist();
...

運行應用,能夠看到按鈕不顯示(這也叫能夠看到麼 -.-),由於咱們尚未添加建立活動的功能,因此目前看不到按鈕顯示。

事實上在開發中,咱們並不須要按照完整的流程進行測試,在 Jasmine 測試當中,能夠直接使用 SpyOn() 來僞造類或對象。這裏咱們能夠先手動建立活動:

...
var new_activity = new Activity('活動一', Date.parse(new Date()));
new_activity.save();
...

將其添加到上面的判斷代碼以前便可手動建立活動,注意屢次運行後將會有多個 活動一 。可在建立一次後刪除該代碼並從新運行,該活動將保留在 localStorage 中。

關於 Jasmine 測試的使用方法請參見:2014-08-08-JavaScript之TDD開發簡介

爲了將按鈕固定顯示在左上角,讀者應自行參看模板應用的 css 代碼並對此應用進行相應的修改,本文不對 css 樣式自己做過多講解。


活動列表按時間排序

在卡片一中,存在以下需求:

打開程序後直接進入"活動列表"頁面,列表顯示爲已建立活動。
"活動列表"頁面按照時間順序由新到舊排列活動,最後建立的活動顯示在列表的最前。
點擊活動列表中的「活動」查看活動信息。

這裏明顯必定要用到動態的數據綁定,固然這也是 AngularJS 最擅長的地方之一。

和以前的 ng-show 以及 ng-hide 類似,可使用 ng-repeat 來綁定數組數據。

爲此,在 activitylist.html 中添加一個列表控件(寫 Xaml 的時候習慣把頁面的東東都叫控件了,見諒),主要代碼以下:

...
<ul class=''>
  <li ng-repeat='activity in activities | orderBy:"-createAt"'>
    <a ng-click='go_to_detail(activity.name)'>
      { {activity.name} }
    </a>
  </li>
</ul>
...

上面的代碼中,ng-repeat 中的 activity in activities<li> 列表綁定到了 $scope.activities 數組變量中,對於其中的每一個元素生成一個 <li> ,並將其名稱做爲顯示內容,每一個活動的點擊事件調用 $scope.go_to_detail 函數並將活動名稱做爲參數。 orderBy:"-createAt" 表示以每一個活動的 createAt 屬性做爲排序依據,- 號表示由大到小。

對於 ng-repeat 中的 ng-click 事件,爲了肯定具體點擊的內容,能夠將當前元素的某個屬性或者當前元素自己做爲參數傳遞,另外一種方法是將 $index 做爲參數傳遞,其表明 ng-repeat 中該元素的位置(從0開始)。

與此對應的 ActivitylistCtrl 核心代碼以下:

初始化活動列表:

...
$scope.activities = Activity.all();
...

活動名稱點擊事件:

...
$scope.go_to_detail = function (activity_name) {
  $location.path('/ActivityRegister/' + activity_name);
};
...

按照上面的函數代碼運行,點擊活動,而後,先本身試一試,真的試了麼?好吧我相信你。

頁面並無發生任何變化。Why?由於咱們在路由中並無定義形如 /ActivityRegister/活動一 之類的路徑,因此路由按照 .otherwise() 中的配置回到了 起始頁面 ,進而在判斷有無活動後從新進入到 活動列表頁面

爲此,咱們須要返回到 app.js 中修改路由配置(記得原來看到有我的的博客裏寫的是路由器配置,當時就驚呆了,估計是懶得校稿,不過其實我也沒校,若是發現低級錯誤記得和我說哦 ^o^)。

將原有的 /ActivityRegister 的配置修改成:

...
.when('/ActivityRegister/:activityName', {
  templateUrl: 'views/activityregister.html',
  controller: 'ActivityregisterCtrl'
})
...

其中的冒號 : 表示 activityName 爲參數(或者說變量),因爲不存在脫離活動的報名,因此無需保留原有的無參數路徑。

以後,在 ActivityregisterCtrl 中,咱們就可以取出該參數:

angular.module('partyBidApp')
  .controller('ActivityregisterCtrl', function ($scope, $location, $routeParams) {
    $scope.this_activity = $routeParams.activityName;
  });

之因此給出完整代碼是但願讀者注意到除了 $location 外如今參數中又多了一個 $routeParams ,用於與路由參數相關的操做。


活動建立

在卡片一中,存在以下需求:

在「建立活動」頁面,當輸入框內信息爲空時,「建立」按鈕爲灰色的不可點擊狀態;
建立的活動名稱不能重複,若是名稱重複,點擊【建立】按鈕,文本框下紅字提示:「*活動名稱重複」。頁面不跳轉。

和以前的數據綁定不一樣,這裏要求 建立 按鈕的可用性隨着輸入框的內容實時變化,而非進入頁面時一次性載入。

這裏介紹三種方法,ng-model , ng-change$scope.$watch

1.ng-model

ng-model 至關於 ng-bind (也就是雙花括號)的逆向使用,即以使用 ng-model 的控件做爲數據源,其屬性值中的變量做爲數據綁定的對象。所以,咱們能夠直接經過數據綁定實現。

在 view 中,咱們定義一個輸入框和一個按鈕:

...
<input ng-model='activity_name' placeholder="如:活動一"/>
...
<button ng-disabled='!activity_name' ng-click='create_activity'>建立</button>
...

上面的代碼已經能夠實現無輸入時不可用,其中,Button 的 ng-disabled 屬性綁定爲輸入框文本的取反值,因爲無輸入文本時其值爲 undefined 類型,做爲邏輯值時爲假;反之,如有輸入內容,其值爲 string 類型,爲邏輯真。

2.ng-change

第二種方法中的 ng-change 指令嚴格的說來並不像數據綁定,而是相似 ng-click 那樣的事件綁定,只是事件的觸發源不是點擊操做,而是在每當該元素的 Value 發生變化的時候觸發。

view 中:

...
<input ng-model='activity_name' ng-change='check_input' placeholder="如:活動一"/>
...
<button ng-disabled='no_create' ng-click='create_activity'>建立</button>
...

controller 中:

...
$scope.check_input = function () {
  $scope.no_create = !$scope.activity_name);
  //將紅色警告取消顯示
}
...

使用 ng-change 綁定了一個函數,雖然更爲複雜但也能實現更多功能,好比在用戶從新輸入時把以前的提示活動名稱重複給去掉。

3.$scope.$watch

$scope.$watch 的用法和 ng-change 很是類似,但做用範圍不一樣。 ng-change 只能做用於 HTML 元素,而 $scope.$watch 能夠用來監測 $scope 中的任何變量或函數的變化。(函數變化指其返回值改變)

在 controller 中,咱們定義一個函數做爲 $watch 的回調函數,具備三個參數:newValue, oldValue, scope。固然,由於有 scope 參數,實際上該函數也能夠不在 controller 內定義。

function watch_callback(newValue, oldValue, scope) {
  scope.no_create = !scope.name_to_create;
  //將紅色警告取消顯示
};

接着在 controller 中執行 $scope.$watch 內容:

$scope.$watch('name_to_create', watch_callback, true);

其中,第一個參數爲待監視變量或函數,能夠傳名稱也能夠傳引用,即也可寫成:

$scope.$watch($scope.name_to_create, watch_callback, true);

第二個參數爲回調函數。

第三個函數爲是否深度監視,適用於對象或者數組。若是不加或爲 false 只監視其引用值是否發生改變,而不會監視其內部的元素或者屬性是否發生改變。對於字符串變量而言,沒有實質差別。

另一個需求是名稱不能重複,讀者可根據前面的內容在 model 中添加一個 Activity.check_repeat(name) 方法來實現,並根據返回值修改警告框的 ng-showng-hide 屬性值便可,此處不作過多介紹。


第一張卡片中主要用的技術和心得體會主要就是這些,若是有任何疑問歡迎在下方回覆 ^.^

本站地址: http://trotyl.github.io/

相關文章
相關標籤/搜索