本文僅爲培訓期間應試做文,不具任何教學價值,具體問題請參考對應文章。javascript
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 直接添加一些拓展組建,在此全選,主要理由以下:
所有選擇後結果顯示以下:
[?] 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 項目生成完畢後(要肯定終端中是生成完畢中止而不是報錯中止的 =_=||),就會發現當前目錄中多了不少文件和文件夾:
4.在終端中開啓 Grunt 的內置服務器:
$ grunt serve
注意:grunt serve
和 grunt 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.js
和 createactivity.js
三個 controller 文件,以及 app/views/
下的 activitylist.html
, activityregister.html
和 createactivity.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
的主要代碼以下:
function Activity(name, createdAt) { this.name = name; this.createdAt = createdAt; }
在上面的代碼中, Activity
類的對象具備兩個字段(可能有些語言通常用屬性,效果都是差很少的),其中,createdAt
用於記錄活動建立時間,以便於排序。
注意:由於採用對象數組,雖然其自己的物理存儲數據已經能夠實現對活動的排序,可是物理存儲順序是絕對不足以做爲排序依據的,其具備很是明顯的不可靠性。即使不存儲建立時間,也須要指定一個 index
屬性來支持排序功能。除此以外,也能使用哈希表來做爲數據結構,以 index
做爲索引,這裏不做過多介紹。
此博客已棄,請轉至 此處
由於不針對任何實例,因此此處採用類函數(方法)實現,從 localStorage 中取出相應 json 數組並解析爲對象。
Activity.all = function () { return JSON.parse(localStorage.getItem('activities')) || []; };
上面的代碼中,最後的邏輯或操做是爲了防止沒有任何數據時致使程序崩潰,所以返回一個空數組而不是 undefined 。而當非空時,因爲邏輯運算的短路法則,或運算及其後內容會直接被忽略。
爲了保持面向對象的純潔性,此處採用實例函數而非類函數來進行存儲,存儲在瀏覽器本地的 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.js
和 main.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-show
和 ng-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
。
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
類型,爲邏輯真。
第二種方法中的 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
綁定了一個函數,雖然更爲複雜但也能實現更多功能,好比在用戶從新輸入時把以前的提示活動名稱重複給去掉。
$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-show
或 ng-hide
屬性值便可,此處不作過多介紹。
第一張卡片中主要用的技術和心得體會主要就是這些,若是有任何疑問歡迎在下方回覆 ^.^
本站地址: http://trotyl.github.io/