这个系列一共会涉及两个JavaScript框架的讲解,一个是Express用做后端,一个是Angular用于前端。和Express一样,Angular分离内容,处理视图、数据和逻辑。和MVC模式很相似,但其实Angular定义是MVW框架,W代表(what ever works for you)。意味着它可以是控制器或者视图模型,或者服务,就看你怎么定义的。这一节会介绍基本的Angular知识;然后改造我们之前做的页面;并且调用之前的定义的api来获取数据。

Angular的数据绑定是指视图的改变会更新模型,而模型的改变也会更新视图。

不像我们用jquery去绑定dom事件然后改变dom。类似于wpf中的双向绑定。

一、第一个例子:数据绑定初体验

这样的例子你可能见得多了,但不要着急,我们循序渐进。

  1. <input />
  2. <h1>Hello </h1>
如何让input中的输入立刻显示在hello的后面?如果是JavaScript或者jquery自然是绑定按键事件,然而Angular都不需要再编写JavaScript代码。先引入
  1. <script src="angular.min.js"></script>
  2. <script src="http://apps.bdimg.com/libs/angular.js/1.4.6/angular.min.js"></script>

然后我们还需要告诉Angular这个页面是一个Angular应用,也就是在html标签修改如下:

  1. <html ng-app>
ng-app这个属性其实可以加到任意元素上,这个属性标记了Angular的作用范围。添加在html上,意味着这整个页面Angular都能工作。接下来我们将input绑定到一个模型:myinput,
  1. <input ng-model="myInput" />

接下来再将模型放到我们要输出的地方,Angular使用{{}}来绑定。如下,这里两个地方的名称必须一致。

  1. <h1>Hello {{ myInput }}</h1>

剩下就交给Angular去做了,演示:

这和第一幅图中所表达的意思一致,视图的改变更新了模型,模型的改变又更新了视图。

二、设置模型初始值

感受了Angular的神奇之后,接下来通过给模型一个初始值,来了解更多Angular的相关知识。要达到这个目的,我们需要创建一个Angular应用模块(module),一个controller来管理作用域。

1.module

  1. <html ng-app="myApp">

module的名称用于标签的ng-app属性。新建一个JavaScript文件,定义如下:

  1. angular.module('myApp', []);

这样就申明了一个Angular应用模块,作用于<html>元素。

2.controller

有了module之后,就可以定义controller了,控制器是在JavaScript代码中定义,附加在某个的html元素上,表示能在这个元素内部工作。如下,我们将控制器附加到body上,命名为myController :

  1. <body ng-controller="myController">

再看JavaScript部分:

  1. function myController() {
  2.  
  3. };
  4. angular.module('myApp').controller('myController', myController);

定义了一个‘myController’,添加在‘myApp’这个模块中。接下来我们通过scope给模型一个初始值。

3.scope

像JavaScript代码一样,Angular也有作用域,Angular有一个rootScope,类似于JavaScript中的全局作用域,包含整个应用。rootScope包含一个或多个子作用域,例如 ng-controller 指令就会创建一个子作用域。作用域关联着视图、模型和控制器。上面定义的myController方法可以带一个$scope参数,且必须为这个名字。它代表着作用域,Angular已自动创建。通过这个参数可以获取到模型。这样,设置初始值就简单了:

  1. var myController = function ($scope) {
  2. $scope.myInput = "Angular!";
  3. };

这种感觉就是依赖注入,$scope由$scopeProvider提供。再看下效果:

输入框和h1元素都出现了模型的初始值。

三、页面改造

Angular是运行于客户端的JavaScript文件,我们需要告诉Express框架,在请求Angular的脚本文件时当成静态文件传送就行,而不需要运行它。Public文件夹已经设置为静态。

  1. app.use(express.static(path.join(__dirname, 'public')));

所以可以在public文件下新建一个angular文件夹,放置Angular脚本文件,并新建一个readApp.js. 在里面定义:

  1. angular.module('readApp', []);

然后在layout.jade里添加文件:

  1. script(src='/angular/angular.min.js')
  2. script(src='/angular/readApp.js')
  1. html(ng-app='readApp')

这样意味着所有页面都支持Angular了。但如果要用Angular展示数据,那么Angular得先拿到数据(不再使用jade去渲染视图),实现这个有三步

1)拿掉Express中对应的首页控制器中的api的调用。

2)在Angular应用的scope中添加编码。

3)更新视图模板并绑定到Angular数据。

第一步,可以先注释掉index方法中请求api的代码,直接返回视图,给Angular腾出场子。

  1. module.exports.index = function (req, res) {
  2. //var requestOptions, path;
  3. //path = "/api/topics";
  4. //requestOptions = {
  5. // url: apiOptions.server + path,
  6. // method: "GET",
  7. // json: {},
  8. //}
  9. //request(requestOptions, function (err, response, body) {
  10. // if (response.statusCode == 200) {
  11. // res.render('index', { title: 'Index', topics: body });
  12. // } else {
  13. // res.render('error', { message: err.message, error: err });
  14. // }
  15. //});
  16. res.render('index', { title: 'Index' });
  17. };

第二步再在readApp.js中增加一个控制器,先用静态数据:

  1. var topics = [
  2. {
  3. title: "书山有路第十一期:程序员修炼之道-第八章-注重实效的项目--第二十二天",
  4. type: "读书",
  5. visitedCount: 80,
  6. commentCount: 2,
  7. createdOn: '2016/7/05 21:32',
  8. author: 'stoneniqiu',
  9. img: 'http://upload.jianshu.io/users/upload_avatars/133630/d5370e672fd4.png?imageMogr/thumbnail/90x90/quality/100'
  10. },
  11. {
  12. title: "《明朝那些事儿》之闲言散语",
  13. type: "书评",
  14. visitedCount: 180,
  15. commentCount: 20,
  16. createdOn: '2016/5/15 21:32',
  17. author: '卡卡卡萨布兰卡 ',
  18. img: 'http://upload.jianshu.io/users/upload_avatars/1675188/2d0810ccc03d.jpg?imageMogr/thumbnail/90x90/quality/100'
  19. },
  20. {
  21. title: "有《程序员修炼之道》高清版吗?",
  22. type: "求书",
  23. visitedCount: 90,
  24. commentCount: 1,
  25. createdOn: '2016/5/15 21:32',
  26. author: '吾不知 ',
  27. img: 'http://upload.jianshu.io/users/upload_avatars/1125491/3910f3825f73.jpg?imageMogr/thumbnail/90x90/quality/100',
  28. }];
  1. var homeController = function($scope) {
  2. $scope.data = topics;
  3. };

并注册:

  1. angular.module('readApp')
  2. .controller('homeController', homeController)

第三步:更新视图(index.jade)

  1. .col-md-9.page(ng-controller="homeController")
  2. .row.topictype
  3. a.label.label-info(href='/') 全部
  4. a(href='/') 读书
  5. a(href='/') 书评
  6. a(href='/') 求书
  7. a(href='/') 求索
  8. .row.topiclist(ng-repeat='topic in data')
  9. img(ng-src='{{topic.img}}')
  10. span.count
  11. i.coment {{topic.commentCount}}
  12. i /
  13. i {{topic.visitedCount}}
  14. span.label.label-info {{topic.type}}
  15. a(href='/') {{topic.title}}
  16. span.pull-right {{topic.createdOn|formdate}}
  17. a.pull-right.author(href='/') {{topic.author}}

我们将homeController加载.page这个元素上。在第二步里面,我们定义了一个Data的模型,其实是一个数组集合,这里用

  1. (ng-repeat='topic in data')

循环输出。代替了jade的each语法:‘each topic in topics‘,和jade不同的是,jade的循环语句位于.topiclist的上方,而Angular的repeat指令要添加在你需要重复的元素了。然后还要注意的是,所有的元素内容中的{{}}前的等号要拿掉,换成空格赋值的语法(等号表示变量,空格表示是字符串),不然无法输出。再注意一个是img的src要用ng-src,不然不能输出url。这个时候运行,已经可以看见输出了:

4.filter

大家可能留意到,上面有这样一段代码:

  1. {{topic.createdOn|formdate}}

属性的后面跟上|号和一个名字,这就叫过滤器(filter),故名思议,通过特定的规则,将源数据转换成需要的数据格式。这里的formdate的定义是:

  1. var formdate = function() {
  2. return function(dateStr) {
  3. var date = new Date(dateStr);
  4. var d = date.getDate();
  5. var monthNames = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"];
  6. var m = monthNames[date.getMonth()];
  7. var y = date.getFullYear();
  8. var output = y + '-' + m + '-' + d;
  9. return output;
  10. };
  11. };
  12. angular.module('readApp')
  13. .controller('homeController', homeController)
  14. .filter('formdate', formdate)

形式上和注册controller是一样,不同的是,filter需要返回一个函数。其实Angular自带一些数据格式,比如data,currency等。如下

  1. <div>{{50.25|currency }}</div>
  2. $50.25
  3. <div>{{50.25|currency:'¥' }}</div>
  4. ¥50.25
  1. <div>{{ "Let's shout" | uppercase }}</div>
  2. <!-- 输出: LET’S SHOUT -->
  3. {{ timestamp | date:"d MMM yyyy" }}
  4. <!-- 输出: 21 Aug 2014 -->

5.directive

指令主要是用来创建html片段,一个html代码片段可以被多个不同的controller和view使用,这容易保持一致性且容易维护。这些片段运行在Angular应用的上下文中,照样可以使用数据绑定,而且浏览器可以缓存这些指令为html文件,当用户在不同的view来回切换时,这有利于加速应用。接下来演示如何添加一个指令,改造之前显示星星的部分(切换到books.jade)。

先模拟的个数据,在homeController中加入models

  1. $scope.models = [{ rating: 4 }, { rating: 5 }];

然后定义一个ratings指令:

  1. var ratingStars = function () {
  2. return {
  3. template : "{{book.rating}}",
  4. };
  5. };

并注册,

  1. angular.module('readApp')
  2. .controller('homeController', homeController)
  3. .filter('formdate', formdate)
  4. .directive('ratingStars', ratingStars)  

在视图上输出。这里有一个注意的地方,html属性是大小写不敏感的,驼峰式的命名需要用转换一下,也就是ratingStars 匹配的是rating-stars,即大写字母转成'-' 加小写字母。

  1. p(ng-repeat='book in models')
  2. small(rating-stars)

页面上会输出4和5.这自然还不能满足我们的要求。有两点:1属性名限制的太死了,不能总是'book.rating'。2是需要把数字变成星星。第一个问题是一个作用域的问题,需要创建一个作用域变量。

  1. return {
  2. scope: {
  3. thisRating : '=rating'
  4. },
  5. template : "{{ thisRating }}"
  6. };

创建了一个thisRating的作用域变量,而'=rating'告诉Angular去匹配带有‘rating'的属性。然后在Angular文件夹下创建一个rating-stars.html。

  1. <span class="glyphicon glyphicon-star{{ thisRating<1 ? '-empty' : ''}}"></span>
  2. <span class="glyphicon glyphicon-star{{ thisRating<2 ? '-empty' : ''}}"></span>
  3. <span class="glyphicon glyphicon-star{{ thisRating<3 ? '-empty' : ''}}"></span>
  4. <span class="glyphicon glyphicon-star{{ thisRating<4 ? '-empty' : ''}}"></span>
  5. <span class="glyphicon glyphicon-star{{ thisRating<5 ? '-empty' : ''}}"></span>

然后用templateUrl指向这个片段

  1. var ratingStars = function () {
  2. return {
  3. scope: {
  4. thisRating : '=rating'
  5. },
  6. templateUrl: '/angular/rating-stars.html'
  7. };
  8. };

在视图上运用:

  1. p(rating-stars, rating=book.rating)

得到星星

指令的这个scope显得不是很方便。最开始用模拟数据是因为 small(rating-stars) 这种写法不识别jade中的循环(each book in books)中的book对象,而'p(rating-stars, rating=book.rating)'又能识别。

6.service

service在Angular中应用比较多,大部分的应用逻辑都可以用service来实现,而且可以给多个controller调用。接下来将controller的中数据移到一个service中去,然后让controller调用service。

创建一个方法,命名为topicData,用来返回topic数据。

  1. var topicData = function ($http) {
  2. return topics;
  3. };

注册service:

  1. .service('topicData', topicData);

使用service:

  1. var homeController = function($scope, topicData) {
  2. $scope.data = topicData;
  3. };

不要忘记在参数里面加入需要调用的服务名称。到现在完成了一个服务的调用,接下来我们从api来获取数据。JavaScript发送http请求不是什么新鲜事了,Jquery的ajax,node里面的request模块,而Angular有一个自带的服务:$http,用来处理请求。接下来就用它来请求api。

  1. var topicData = function ($http) {
  2. return $http.get('/api/topics');
  3. };

$http有一个get方法,参数就是一个url。这里调用我们在第三节定义好的api。--> 使用Mongoose创建模型及API

这是一个异步的方法,所以还需要改造Controller中的代码:

  1. var homeController = function ($scope, topicData) {
  2. topicData.success(function (data) {
  3. console.log(data);
  4. $scope.data = data;
  5. }).error(function (e) {
  6. console.log(e);
  7. });
  8. };

这时候运行,可以看到全部的数据了。

异步加载数据的一个问题是用户刚打开页面的时候可能是一片空白,所以加一个过渡的内容好一点。

  1. var homeController = function ($scope, topicData) {
  2. $scope.message = "loading...";
  3. topicData.success(function (data) {
  4. console.log(data);
  5. $scope.message = data.length > 0 ? "" : "暂无数据";
  6. $scope.data = data;
  7. }).error(function (e) {
  8. console.log(e);
  9. $scope.message = "Sorry, something's gone wrong ";
  10. });
  11. };

页面加一个:

  1. .error {{ message }}

这样避免出现一个短时间的空白。

小结:这一节我们体验了Angular的数据绑定模式,并学习了如何定义并使用module、Controller、directive、filter和service。简单了改造了首页,将Express的一部分工作转移到了前端。其实涉及到Angular的每一个部分都不够深入,只是按需分配,用到多少就讲多少。我觉得这样循序渐进的比较好。下一节将介绍用Angular做一个单页应用(SPA),讲解Angular路由等知识。

Nodejs之MEAN栈开发(五)---- Angular入门与页面改造的更多相关文章

  1. Nodejs之MEAN栈开发(九)---- 用户评论的增加/删除/修改

    由于工作中做实时通信的项目,需要用到Nodejs做通讯转接功能,刚开始接触,很多都不懂,于是我和同事就准备去学习nodejs,结合nodejs之MEAN栈实战书籍<Getting.MEAN.wi ...

  2. Nodejs之MEAN栈开发(三)---- 使用Mongoose创建模型及API

    继续开扒我们的MEAN栈开发之路,前面两节我们学习了Express.Jade引擎并创建了几个静态页面,最后通过Heroku部署了应用. Nodejs之MEAN栈开发(一)---- 路由与控制器 Nod ...

  3. Nodejs之MEAN栈开发(七)---- 用Angular创建单页应用(下)

    上一节我们走通了基本的SPA基础结构,这一节会更彻底的将后端的视图.路由.控制器全部移到前端.篇幅比较长,主要分页面改造.使用AngularUI两大部分以及一些优化路由.使用Angular的其他指令的 ...

  4. Nodejs之MEAN栈开发(二)----视图与模型

    上一节做了对Express做了简单的介绍,提出了controller,介绍了路由.这一节将重点放到视图和模型上,完成几个静态页面并部署到heroku上. 导航 前端布局使用bootstrap,从官网下 ...

  5. Nodejs之MEAN栈开发(六)---- 用Angular创建单页应用(上)

    在上一节中我们学会了如何在页面中添加一个组件以及一些基本的Angular知识,而这一节将用Angular来创建一个单页应用(SPA).这意味着,取代我们之前用Express在服务端运行整个网站逻辑的方 ...

  6. Nodejs之MEAN栈开发(一)---- 路由与控制器

    因为工作需要,最近再次学习了node,上一次学习node是2014年,纯粹是个人兴趣,学了入门之后没有运用,加上赶别的项目又不了了之.这次正好捡起来.废话不多说,这里的MEAN指的是Mongodb.E ...

  7. Nodejs之MEAN栈开发(八)---- 用户认证与会话管理详解

    用户认证与会话管理基本上是每个网站必备的一个功能.在Asp.net下做的比较多,大体的思路都是先根据用户提供的用户名和密码到数据库找到用户信息,然后校验,校验成功之后记住用户的姓名和相关信息,这个信息 ...

  8. Nodejs之MEAN栈开发(四)---- form验证及图片上传

    这一节增加推荐图书的提交和删除功能,来学习node的form提交以及node的图片上传功能.开始之前需要源码同学可以先在git上fork:https://github.com/stoneniqiu/R ...

  9. Python 全栈开发五 迭代器 生成器 装饰器

    一.迭代器 迭代协议:对象必须提供一个next方法,执行该方法后会返回迭代的下一项或者抛出Stopiteration异常,终止迭代.切只能往前,不能倒退. 可迭代对象:遵循迭代写一点对象就是可迭代对象 ...

随机推荐

  1. 制作类似ThinkPHP框架中的PATHINFO模式功能

    一.PATHINFO功能简述 搞PHP的都知道ThinkPHP是一个免费开源的轻量级PHP框架,虽说轻量但它的功能却很强大.这也是我接触学习的第一个框架.TP框架中的URL默认模式即是PathInfo ...

  2. 用html5的canvas和JavaScript创建一个绘图程序

    本文将引导你使用canvas和JavaScript创建一个简单的绘图程序. 创建canvas元素 首先准备容器Canvas元素,接下来所有的事情都会在JavaScript里面. <canvas ...

  3. ASP.NET Core 中文文档 第四章 MVC(4.1)Controllers, Actions 和 Action Results

    原文:Controllers, Actions, and Action Results 作者:Steve Smith 翻译:姚阿勇(Dr.Yao) 校对:许登洋(Seay) Action 和 acti ...

  4. RSA算法

    RSA.h #ifndef _RSA_H #define _RSA_H #include<stdio.h> #include<iostream> #include<mat ...

  5. Atitit.研发团队与公司绩效管理的原理概论的attilax总结

    Atitit.研发团队与公司绩效管理的原理概论的attilax总结 1. 四个理念 1 1.1. 绩效管理的三个目的.四个环节.五个关键2 1.2. 绩效目标smart2 2. 考核对象2 3. 绩效 ...

  6. Atitit.软件研发团队建设原理与概论 理论

    Atitit.软件研发团队建设原理与概论 理论 培训 团队文化建设(内刊,ppt,书籍,杂志等) 梯队建设 技术储备人才的问题 团队建设--小红花评比. 团队建设--文化墙.doc 户外拓展 1. 团 ...

  7. 从史上八大MySQL事故中学到的经验

    本文列举了史上八大MySQL宕机事件原因.影响以及人们从中学到的经验,文中用地震级数来类比宕机事件的严重性和后果,排在最严重层级前两位的是由于亚马逊AWS宕机故障(相当于地震十级和九级). 一.Per ...

  8. MySQL常见面试题

    1. 主键 超键 候选键 外键 主 键: 数据库表中对储存数据对象予以唯一和完整标识的数据列或属性的组合.一个数据列只能有一个主键,且主键的取值不能缺失,即不能为空值(Null). 超 键: 在关系中 ...

  9. C#迪杰斯特拉算法

    C#迪杰斯特拉算法 网上有许多版本的,自己还是写一个理解点 Dijkstra.cs public class Dijkstra { private List<Node> _nodes; p ...

  10. 微软将向Linux用户提供SQL Server程序

    微软公司(Microsoft Corp., MSFT)将向Linux操作系统的用户提供旗下一项最赚钱的产品,这是该公司几年前无法想像的举措.这家软件巨头周一表示,将向免费的Linux Server提供 ...