AngularJS 教程

简介

创建一个简单的邮件应用程序,学习核心的AngularJS概念。在结束该教程的学习时,你将能够看到(虚构的)邮件应用程序,在该程序中,可以根据邮件主题查询邮件,还可以阅读或删除邮件。

 

学习的先决条件:

  • 会使用jQuery创建简单的JavaScript应用
  • 懂得如何启动简单的HTTP服务器(如 python -m SimpleHTTPServer)
  • 会从Github上克隆一个项目

涵盖的主题

  • 单页面应用(SPA)
  • 客户端MVC模式
  • 双向数据绑定
  • 路由与模板
  • AngularJS构建块:指令标签(Directives),工厂(Factories)以及控制器(Controllers)

客户端MVC

  • 模型(Model):可以说是数据;一个应用的业务信息;
  • 视图(View):HTML和数据的呈现。是用户所见到和交互的界面;
  • 控制器(Controller):让应用中的所有不同模块协同工作的连接器。

开始使用AngularJS

在页面中引入AngularJS

在HTML页面的底部<body>结束标签之前引入AngularJS的.js文件。

[html]  view plain  copy
  1. <html>  
  2.     <head>  
  3.     </head>  
  4.     <body>  
  5.         <div></div>  
  6.         <script src="lib/jquery-v1.11.1.js"></script>  
  7.         <script src="lib/angular-v1.2.22.js"></script>  
  8.     </body>  
  9. </html>  

Angular自身内置了一个叫做“jQLite”的东西,它是一个类似jQuery的微型库。jQLite是一个非常轻量级的库,因此也就没有你可能需要的强大的jQuery方法。这里引入jQuery主要是出于可能后续会引入一些依赖于jQuery库的插件。

 

设置作用域(Scopes)和指令标签(Directives)

Angular非常基础的一个概念是作用域(scopes)。Scopes保存着你的Models(也就是你的数据),它与你的控制器(Controllers)一起协作,同时为视图(Views)提供了所需的一切。Angular的scopes概念与常见的JavaScript中的作用域(scope)概念非常相似。

 

第一步是需要设置整个应用的作用域,它标识出了Angular应用起作用的范围。通常我们会在<html>标签中通过ng-app属性来设置它。

[html]  view plain  copy
  1. <html ng-app="myApp">  
  2.     <head></head>  
  3.     <body></body>  
  4. </html>  

另外一个与scope相关的指令是ng-controller,它决定了一个控制器的作用域范围。在一个应用中可以包含多个控制器。每个控制器都有其自身的作用域范围。

[html]  view plain  copy
  1. <div ng-controller="InboxCtrl">  
  2.     <!-- inside InboxCtrl scope -->  
  3. </div>  

ng-app和ng-controller都是Angular指令标签。可以把Angular指令标签看作是对HTML的扩展。

 

你的第一个控制器:一个简单的例子

把我们刚刚学到的用在实践中

1 创建一个空的HTML文档

2 引入Angular和jQuery文件

[html]  view plain  copy
  1. <script src="lib/jquery-v1.11.1.js"></script>  
  2. <script src="lib/angular-v1.2.22.js"><script>  

3 在<html>标签中添加ng-app="myApp"

4 在body中创建一个控制器示例

[html]  view plain  copy
  1. <div ng-controller="TestCtrl"><h1></h1>  
  2.    <input type="text" ng-model="title">  
  3. </div>  

   稍后我们会解释ng-model="title"的含义。

5 创建内嵌的JavaScript脚本,确保该脚本在所引用库文件之后。

[javascript]  view plain  copy
  1. <script>  
  2.    function TestCtrl($scope) {  
  3.       $scope.title = 'Write a title here...';  
  4.    };  
  5. </script>  

    是否已发现函数的名字跟ng-controller的值是一样的?Angular会在JavaScript中寻找相同名称的函数来作为控制器。

 

以下是完整的代码:

[html]  view plain  copy
  1. <!doctype html>  
  2. <html ng-app>  
  3.   <head>  
  4.     <title>Sample AngularJS Controller</title>  
  5.   </head>  
  6.   <body>  
  7.     <div ng-controller="TestCtrl">  
  8.         <h1></h1>  
  9.         <input type="text" ng-model="title">  
  10.     </div>  
  11.   
  12.     <script src="lib/jquery-v1.11.1.js"></script>  
  13.     <script src="lib/angular-v1.2.22.js"></script>  
  14.   
  15.     <script>  
  16.       function TestCtrl($scope) {  
  17.         $scope.title = 'Write a title here...';  
  18.       };  
  19.     </script>  
  20.   </body>  
  21. </html>  

ngView与路由(Routes)

将URL与作用域关联

另外一个重要的构建块是ng-view指令标签,它能够将应用中的某个URL关联到作用域中。

[html]  view plain  copy
  1. <html ng-app="myApp">  
  2.     <head>  
  3.     </head>  
  4.     <body>  
  5.         <div ng-view></div>  
  6.     </body>  
  7. </html>  

这里的ng-view标签能够根据用户访问的URL告诉Angular我们想插入的HTML。

 

模块(Modules)

angular.module()

每个应用需要一个模块,而Angular通过angular.module()为我们提供了模块命名空间的能力。该方法既可以设置模块也可以获取一个模块,关键取决于如何使用它。创建(设置)一个新的模块,如:

[javascript]  view plain  copy
  1. angular.module('myApp', []);  

获取一个模块的引用,用以注册controllers,factories,filters等,可以不用后面的那个数组而只需通过模块的名称:

[javascript]  view plain  copy
  1. angular.module('myApp');  

路由(Routing)与依赖注入(Dependency Injection)

路由

接下来是配置路由,路由使应用能够根据我们访问的URL来确定加载哪个视图。

RouteProvider

从Angular 1.2.0版本开始,核心的Angular中不在包含$routeProvider,因此需要将其作为一个独立的模块引入(angular-route.js)。

[html]  view plain  copy
  1. <body>  
  2.     <!-- ... -->  
  3.     <!-- Extra routing library -->  
  4.     <script src="lib/angular-route-v1.2.22.js"></script>  
  5. </body>  

该文件为我们提供了一个名为ngRoute的模块,在我们自己的模块定义的时候需要将其作为依赖引入:
[javascript]  view plain  copy
  1. var app = angular.module('app', [  
  2.    'ngRoute'  
  3. ]);  

配置阶段

Angular中每个模块具有一个.config()方法,可以给该方法传入一个回调函数,该回调函数将在很多其他函数执行前调用。就是在这个地方配置我们的路由:

[javascript]  view plain  copy
  1. app.config(function () {/*...*/});  

依赖注入

在config回调函数中,我们需要$routeProvider来配置路由,可以简单地把$routeProvider作为config函数的参数传入,剩下的事情交由Angular处理。

[javascript]  view plain  copy
  1. app.config(function ($routeProvider) {  
  2.    /* Now we can use the routeProvider! :D */  
  3. });  

配置路由

[javascript]  view plain  copy
  1. app.config(function ($routeProvider) {  
  2.    $routeProvider  
  3.       .when('/inbox', {  
  4.          templateUrl: 'views/inbox.html',  
  5.          controller: 'InboxCtrl',  
  6.          controllerAs: 'inbox'  
  7.       })  
  8.       .when('/inbox/email/:id', {  
  9.          templateUrl: 'views/email.html',  
  10.          controller: 'EmailCtrl',  
  11.          controllerAs: 'email'  
  12.       })  
  13.       .otherwise({  
  14.          redirectTo: '/inbox'  
  15.       });  
  16. });  

控制器(Controllers)

连接模型与视图

一个好的控制器只包含尽可能少的逻辑在里面,且只完成两项工作:将模型绑定到视图(初始化视图);为视图添加帮助函数。

[javascript]  view plain  copy
  1. app.controller('InboxCtrl'function () {  
  2.    // Model and View bindings  
  3.    // Small helper function not needed anywhere else  
  4. });  

每个控制器都能够访问$scope。通过$scope可以往视图上添加属性和方法。

[javascript]  view plain  copy
  1. app.controller('InboxCtrl'function ($scope) {  
  2.    // initialize the title property to an array for the view to use  
  3.    $scope.title = "This is a title";  
  4. });  

在标签中可以这样与之对应:
[html]  view plain  copy
  1. <div ng-controller="InboxCtrl">  
  2.    {{ title }}  
  3. </div>  

工厂(Factories)

工厂常用于通过HTTP与服务端交互。

可以通过angular.factory()方法创建一个工厂,如:

[javascript]  view plain  copy
  1. app.factory('ExampleFactory'function ExampleFactory($rootScope, $http, $location) {  
  2.    return function myReusableFunction() {  
  3.       // do something fancy  
  4.    };  
  5. });  

在该应用中,需要获取邮件信息,可以创建一个方法来实现。Angular使用$http服务来与服务端交互,因此需要将它也引入:

[javascript]  view plain  copy
  1. app.factory('InboxFactory'function InboxFactory ($http) {  
  2.    var exports = {};  
  3.   
  4.    exports.getMessages = function () {  
  5.       return $http.get('json/emails.json')  
  6.          .error(function (data) {  
  7.             console.log('There was an error!', data);  
  8.       });  
  9.    };  
  10.   
  11.    return exports;  
  12. });  

关联工厂与控制器

[javascript]  view plain  copy
  1. app.controller('InboxCtrl'function($scope, InboxFactory) {  
  2.    InboxFactory.getMessages()  
  3.       .success(function(jsonData, statusCode) {  
  4.          console.log('The request was successful!', statusCode, jsonData);  
  5.          // Now add the Email messages to the controller's scope  
  6.          $scope.emails = jsonData;  
  7.    });  
  8. });  

模板(Templating)

视图渲染

[javascript]  view plain  copy
  1. <!-- JavaScript inside expression -->  
  2. <h1>{{ [ firstName, lastName ].join(" ") }}</h1>  
  3. <!-- currency filter applied to a multiplication of 2 numbers -->  
  4. <div class="total-info">{{ cost * quantity | currency }}</div>  
  5. <!-- Using a ternary expression inside the expression -->  
  6. <div class="budget">{{ ( total > budget ) ? "Too Expensive" : "You can buy it!" }}</div>  

指令标签概览

自定义HTML标签

指令标签是Angular提供的自定义HTML标签,它为封装数据、模板与行为提供了一种可重用的方式。

在我们的视图中,有时可以见到如下的标签:

[html]  view plain  copy
  1. <div id="someview">  
  2.    {{ data.scopePropertyAsUsual }}  
  3.    <my-custom-element></my-custom-element>  
  4. </div>  

上面的<my-custom-element>标签会被我们定义的标签模板和逻辑替代。以下为一个简单的指令标签结构示例:

[javascript]  view plain  copy
  1. app.directive('myCustomElement'function myCustomElement() {  
  2.    return {  
  3.       restrict: 'EA',  
  4.       replace: true,  
  5.       scope: true,  
  6.       template: [  
  7.          "<div>",  
  8.          "  <h1>My Custom Element's Heading</h1>",  
  9.          "  <p>Some content here!</p>",  
  10.          "  <pre>{{ ctrl.expression | json }}</pre>,"  
  11.          "</div>"  
  12.       ].join(""),  
  13.       controllerAs: 'ctrl',  
  14.       controller: function ($scope) {  
  15.          this.expression = {  
  16.             property: "Example"  
  17.          }  
  18.       },  
  19.       link: function (scope, element, attrs) {}  
  20.    }  
  21. });  

工厂与指令协同工作

[javascript]  view plain  copy
  1. angular.module('EmailApp')  
  2.    .factory('InboxFactory'function InboxFactory ($q, $http, $location) {  
  3.       'use strict';  
  4.       var exports = {};  
  5.   
  6.       exports.messages = [];  
  7.   
  8.       exports.goToMessage = function(id) {  
  9.          if ( angular.isNumber(id) ) {  
  10.             // $location.path('inbox/email/' + id)  
  11.          }  
  12.       }  
  13.   
  14.       exports.deleteMessage = function (id, index) {  
  15.          this.messages.splice(index, 1);  
  16.       }  
  17.   
  18.       exports.getMessages = function () {  
  19.          var deferred = $q.defer();  
  20.          return $http.get('json/emails.json')  
  21.             .success(function (data) {  
  22.                exports.messages = data;  
  23.                deferred.resolve(data);  
  24.             })  
  25.             .error(function (data) {  
  26.                deferred.reject(data);  
  27.             });  
  28.          return deferred.promise;  
  29.       };  
  30.   
  31.       return exports;  
  32.    });  

[javascript]  view plain  copy
  1. app.directive('inbox'function () {  
  2.    return {  
  3.       restrict: 'E',  
  4.       replace: true,  
  5.       scope: true,  
  6.       templateUrl: "js/directives/inbox.tmpl.html",  
  7.       controllerAs: 'inbox',  
  8.       controller: function (InboxFactory) {  
  9.          this.messages = [];  
  10.          this.goToMessage = function (id) {  
  11.             InboxFactory.goToMessage(id);  
  12.          };  
  13.          this.deleteMessage = function (id, index) {  
  14.             InboxFactory.deleteMessage(id, index);  
  15.          };  
  16.          InboxFactory.getMessages()  
  17.             .then( angular.bind( thisfunction then() {  
  18.                this.messages = InboxFactory.messages;  
  19.             })  );  
  20.       },  
  21.       link: function (scope, element, attrs, ctrl) {  
  22.          /* 
  23.          by convention we do not $ prefix arguments to the link function 
  24.          this is to be explicit that they have a fixed order 
  25.          */  
  26.       }  
  27.    }  
  28. });  

用于上述指令标签的完整HTML模板:

[html]  view plain  copy
  1. <div class="inbox">  
  2.   <div class="inbox__count">  
  3.     You have {{ inbox.messages.length && inbox.messages.length || 'no' }} messages  
  4.   </div>  
  5.   <div ng-hide="!inbox.messages.length">  
  6.     <input type="text" class="inbox__search"   
  7.       ng-model="inbox.search"   
  8.       placeholder="Search by 'from', e.g. TicketMaster">  
  9.   </div>  
  10.   <ul class="inbox__list">  
  11.     <li ng-show="!inbox.messages.length">  
  12.       No messages! Try sending one to a friend.  
  13.     </li>  
  14.     <li ng-repeat="message in inbox.messages | filter:{ from: inbox.search }">  
  15.       <div class="inbox__list-info">  
  16.         <p class="inbox__list-from"> from: {{ message.from }} </p>  
  17.         <p class="inbox__list-date"> {{ message.date | date: 'dd/MM/yyyy' }} </p>  
  18.         <p class="inbox__list-subject"> {{ message.subject }} </p>  
  19.       </div>  
  20.       <div class="inbox__list-actions">  
  21.         <a href="#" ng-click="inbox.goToMessage(message.id);">  
  22.           Read  
  23.         </a>  
  24.         <a href="" ng-click="inbox.deleteMessage(id, $index);">  
  25.           Delete  
  26.         </a>  
  27.       </div>  
  28.     </li>  
  29.   </ul>  
  30. </div>  

内置指令标签

ng-show, ng-repeat, ng-click

[html]  view plain  copy
  1. <ul>  
  2.    <li ng-repeat="item in items">  
  3.       {{ item.name }}  
  4.    </li>  
  5. </ul>  

[html]  view plain  copy
  1. <li ng-repeat="message in inbox.messages | filter:{ from: inbox.search }">  

[html]  view plain  copy
  1. <input type="text" class="inbox__search" placeholder="Search" ng-model="inbox.search">  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值