AngularJS概念概述和第一个使用例子

点击查看AngularJS系列目录
转载请注明出处:http://www.cnblogs.com/leosx/


 

概念概述

本节使用一个简单的例子简要介绍了AngularJS的重要组成部分。

概念 描述
模板(Template) HTML的附加标记
指令(Directives) 通过元素或者客户属性去扩展HTML
模型(Model) 用户和界面交互的数据的模型。
上下文(Scope) 语境上下文,这样控制器,指令和表达式可以访问它里面的数据。
表达式(Expressions) 可以从Scope(上下文)中访问变量和函数。
编译器(Compiler) 解析模板和实例化指令和表达式
过滤器(Filter) 格式表达式的值以显示给用户
视图(View) 用户所看到的(DOM)
数据绑定(Data Binding) 模型和视图间同步数据的动作
控制器(Controller) 试图(view)背后的业务逻辑
依赖注入(Dependency Injection) 创建对象和函数的工厂
注入器(Injector) 依赖注入的容器
模块(Module) 对应用程序的不同部分,包括控制器,服务器,过滤器,指令进行配置注射器的容器
服务(Service) 对于试图可重用的业务逻辑独立的功能部分

 

第一个例子:数据绑定

在下面的例子中,我们将建立一个表格来计算总价格。

<div ng-app ng-init="qty=1;cost=2">
  <b>Invoice:</b>
  <div>
    Quantity: <input type="number" min="0" ng-model="qty">
  </div>
  <div>
    Costs: <input type="number" min="0" ng-model="cost">
  </div>
  <div>
    <b>Total:</b> {{qty * cost | currency}}
  </div>
</div>

结果如下图:

image

这看起来像普通的HTML和一些新的标记的组合。在AngularJS中,这样的文件称为模板。当AngularJS启动应用程序时,它会使用编译器去分析和处理来自新的标记标识的模板。然后在加载,改造,呈现一个新的DOM到用户视图。

第一个新的标记是指令(directives)。它是HTML元素或者属性的一个特殊的行为。在上面的例子当中,我们使用了 ng-app 属性,这是一个指令,用于自动初始化我们的应用程序。AngularJS还为input元素定义了一些额外的指令,用于增加一些行为来扩展元素。例如:ng-model 指令,用来存储或者更新输入字段的值。

 

自定义指令访问DOM:在一个AngularJS应用程序中,唯一的一个允许去访问DOM的地方就是指令的范围内(Scope)。这点很重要,因为访问DOM元素是一件很难预测,也很难测试的事情。如果您需要直接访问DOM,你应该写一个自定义的指令去做这件事。

指令章节,我们会去介绍如何自定义。

第二种新的标记是双花括号 {{表达|过滤器}} ,当编译器遇到这个标记,将其与标记的评估值替换。在模板中的表达式是一个类似于JavaScript的代码段,允许读取和写入变量。请注意,这些变量不是全局变量。就像AngularJS函数和这些变量在一个function函数里面一样,Angular可以去访问。在上面的例子当中,我们的花括号表达式的功能是计算数量和单价,以获得总价格。后面的过滤器使得我们的输出格式为金钱格式(这里加了一个$符号表示是美元为单位的)。在这个例子中,我们可以看到,AngularJS提供了一个绑定:当输入值发生变化,表达式的值会自动重新计算并且去更新DOM与之对应的值。这使用到的概念是双向数据绑定(学过WPF或者Silverlight的同学一定很清楚明白的)。

 

添加UI逻辑:控制器(controller)

让我们添加更多的逻辑,使我们能够进行不同货币的总价格计算。

第一个文件:invoice1.js

angular.module('invoice1', [])
.controller('InvoiceController', function() {
  this.qty = 1;
  this.cost = 2;
  this.inCurr = 'EUR';
  this.currencies = ['USD', 'EUR', 'CNY'];
  this.usdToForeignRates = {
    USD: 1,
    EUR: 0.74,
    CNY: 6.09
  };

  this.total = function total(outCurr) {
    return this.convertCurrency(this.qty * this.cost, this.inCurr, outCurr);
  };
  this.convertCurrency = function convertCurrency(amount, inCurr, outCurr) {
    return amount * this.usdToForeignRates[outCurr] / this.usdToForeignRates[inCurr];
  };
  this.pay = function pay() {
    window.alert("Thanks!");
  };
});

 

第二个文件:index.html

<div ng-app="invoice1" ng-controller="InvoiceController as invoice">
  <b>Invoice:</b>
  <div>
    Quantity: <input type="number" min="0" ng-model="invoice.qty" required >
  </div>
  <div>
    Costs: <input type="number" min="0" ng-model="invoice.cost" required >
    <select ng-model="invoice.inCurr">
      <option ng-repeat="c in invoice.currencies">{{c}}</option>
    </select>
  </div>
  <div>
    <b>Total:</b>
    <span ng-repeat="c in invoice.currencies">
      {{invoice.total(c) | currency:c}}
    </span>
    <button class="btn" ng-click="invoice.pay()">Pay</button>
  </div>
</div>

效果图如下:

image

和上一个例子相比,有什么改变呢?

首先,增加了一个包含控制器的新的JavaScript文件。更确切地说,该文件包含一个构造函数用于去创建一个真实的Controller控制器的实例。控制器的目的就是为我们的表达式和指令去提供一些变量和方法。

除了增加了新的文件之外,我们还在HTML代码中增加了一个ng-controller指令,这个指令就是在告诉AngularJS,去创建一个InvoiceController去负责这个元素,以及这个元素下的所有子元素。语法:ng-controller="InvoiceController as invoice" 是告诉AngularJS去创建一个控制器,并重命名此控制器为invoce,这样下面访问控制器中的变量的时候,就可以使用invoce这个昵称去访问控制器所在的Scope中的变量了,HTML 中,还使用了ng-repeat指令去循环指定的元素。也定义了一个方法total (function)方法去计算总价格。

同样,这种结合是很灵活的,即当数据变化时,DOM会自动更新显示变化后的结果。这就省却了我们去修改DOM元素。我们使用了ng-click指令去绑定到了按钮点击事件上。这样,当点击按钮的时候,就会触发与controllerscope所对应的方法进行执行。

 

在新的JavaScript文件,我们还在Module中注册了我们的控制器模块。我们将在下一节讨论模块(module),下图显示了所有的东西是如何在Controller里面对应显示出来的。

concepts-databinding2

 

独立的业务逻辑:服务

眼下,InvoiceController包含我们的例子中的所有逻辑。当我们的逻辑变得复杂之后,我们应该把一些可以独立出来的业务逻辑提出来,放到服务里面去,这样这些独立的业务逻辑块儿就可以重复使用了。之后,我们就可以从网络上加载一些独立的业务逻辑,如装载并调用雅虎的财经API来实现汇率的转换,而我们的控制器不用变。

服务文件:finance2.js

angular.module('finance2', [])
.factory('currencyConverter', function() {
  var currencies = ['USD', 'EUR', 'CNY'];
  var usdToForeignRates = {
    USD: 1,
    EUR: 0.74,
    CNY: 6.09
  };
  var convert = function (amount, inCurr, outCurr) {
    return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr];
  };

  return {
    currencies: currencies,
    convert: convert
  };
});

 

控制器文件:invoice2.js

angular.module('invoice2', ['finance2'])
.controller('InvoiceController', ['currencyConverter', function(currencyConverter) {
  this.qty = 1;
  this.cost = 2;
  this.inCurr = 'EUR';
  this.currencies = currencyConverter.currencies;

  this.total = function total(outCurr) {
    return currencyConverter.convert(this.qty * this.cost, this.inCurr, outCurr);
  };
  this.pay = function pay() {
    window.alert("Thanks!");
  };
}]);

 

HTML文件:index.html

<div ng-app="invoice2" ng-controller="InvoiceController as invoice">
  <b>Invoice:</b>
  <div>
    Quantity: <input type="number" min="0" ng-model="invoice.qty" required >
  </div>
  <div>
    Costs: <input type="number" min="0" ng-model="invoice.cost" required >
    <select ng-model="invoice.inCurr">
      <option ng-repeat="c in invoice.currencies">{{c}}</option>
    </select>
  </div>
  <div>
    <b>Total:</b>
    <span ng-repeat="c in invoice.currencies">
      {{invoice.total(c) | currency:c}}
    </span>
    <button class="btn" ng-click="invoice.pay()">Pay</button>
  </div>
</div>

效果图:

image

 

这次又有什么变化呢?

我们新建了一个文件:finance2.js 并且把 convertCurrency功能移动到了文件当中去。但是我们又如何使得控制器依旧保持着这个功能呢?

这就是依赖注入的用武之地。依赖注入(DI)是软件设计的一部分,它实现了如何创建对象(object)和功能函数(function),以及如何正确找到它们的依赖关系。在Angular中,所有的东西(指令,过滤器,控制器,服务,...)的创建都有使用依赖注入。在Angular中,DI的容器是注入器(injector)

要使用DI,我们首先是要在需要使用的地方进行注册。在Angular中,我们是在模块(modules)中进行注册的。当Angular启动时,它会执行模块的配置(config)然后根据ng-app指令开始执行,在执行前,模块会根据配置的名称去加载所有依赖的模块。

在上面的例子当中,包含了一个指令ng-app="invoice2"。这是在告诉Angular,应用程序的主模块适用名为:invoice2的模块作为主模块。代码段:angular.module('invoice2', ['finance2'])指定了invoice2的模块依赖于finance2模块。也就是说,InvoiceController 控制器使用了currencyConverter 服务中的功能。执行到这里Angular知道该应用程序的所有部分了,接下来就是去创建它们了。在上一节我们的控制器使用工厂函数来创建的。在服务方面有多种方式来定义自己的工厂(见服务指南)。在上面的例子中,我们在服务的工厂(factory)中使用了一个函数去返回了一个currencyConverter 函数功能。

concepts-module-service

回到最初的问题:如何在InvoiceController控制器中得到一个服务中的currencyConverter功能?在Angular中,这是通过在构造函数中简单地定义参数进行实现的。有了这个,injector能够根据顺序去创建正确的对象,并且在创建前,会先调用创建依赖对象并调用它们的工厂函数。在InvoiceController 中,有一个currencyConverter的参数,有了它,Angular就知道控制器和服务之间的依赖性,并调用与服务实例作为控制器的参数。

无论是上一个例子还是这一个例子,我们都可以看到,我们在module.controller 函数中,有一个数组,数组首先包含了控制器需要服务相关的名称,它就是在注册所依赖的模块。数组中的最后一项是控制器构造函数。Angular使用此数组语法定义的依赖关系,使得减少了代码污染DI。

 

访问后台服务

让我们来实现一下我们上面说到的,使用雅虎汇率服务API,去通过网络获得实时的汇率信息。

第一个文件:invoice3.js

angular.module('invoice3', ['finance3'])
.controller('InvoiceController', ['currencyConverter', function(currencyConverter) {
  this.qty = 1;
  this.cost = 2;
  this.inCurr = 'EUR';
  this.currencies = currencyConverter.currencies;

  this.total = function total(outCurr) {
    return currencyConverter.convert(this.qty * this.cost, this.inCurr, outCurr);
  };
  this.pay = function pay() {
    window.alert("Thanks!");
  };
}]);

 

第二个文件:finance3.js

angular.module('finance3', [])
.factory('currencyConverter', ['$http', function($http) {
  var YAHOO_FINANCE_URL_PATTERN =
        '//query.yahooapis.com/v1/public/yql?q=select * from '+
        'yahoo.finance.xchange where pair in ("PAIRS")&format=json&'+
        'env=store://datatables.org/alltableswithkeys&callback=JSON_CALLBACK';
  var currencies = ['USD', 'EUR', 'CNY'];
  var usdToForeignRates = {};

  var convert = function (amount, inCurr, outCurr) {
    return amount * usdToForeignRates[outCurr] / usdToForeignRates[inCurr];
  };

  var refresh = function() {
    var url = YAHOO_FINANCE_URL_PATTERN.
               replace('PAIRS', 'USD' + currencies.join('","USD'));
    return $http.jsonp(url).success(function(data) {
      var newUsdToForeignRates = {};
      angular.forEach(data.query.results.rate, function(rate) {
        var currency = rate.id.substring(3,6);
        newUsdToForeignRates[currency] = window.parseFloat(rate.Rate);
      });
      usdToForeignRates = newUsdToForeignRates;
    });
  };

  refresh();

  return {
    currencies: currencies,
    convert: convert,
    refresh: refresh
  };
}]);

 

第三个文件:index.html

<div ng-app="invoice3" ng-controller="InvoiceController as invoice">
  <b>Invoice:</b>
  <div>
    Quantity: <input type="number" min="0" ng-model="invoice.qty" required >
  </div>
  <div>
    Costs: <input type="number" min="0" ng-model="invoice.cost" required >
    <select ng-model="invoice.inCurr">
      <option ng-repeat="c in invoice.currencies">{{c}}</option>
    </select>
  </div>
  <div>
    <b>Total:</b>
    <span ng-repeat="c in invoice.currencies">
      {{invoice.total(c) | currency:c}}
    </span>
    <button class="btn" ng-click="invoice.pay()">Pay</button>
  </div>
</div>

效果图:

image

点击这里查看示例

这个示例又有什么变化呢?我们的finance 模块的currencyConverter 服务,使用了$http,$http是一个AngularJS内置的用于访问服务器的后端数据的服务。$http 使用的是XMLHttpRequest 技术和 JSONP 进行传输.

posted @ 2014-10-24 13:52  Entertainment  Views(695)  Comments(0Edit  收藏  举报