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/

相关文章
相关标签/搜索