02_Conceptual Overview概念概述

Conceptual Overview

概念概述

This section briefly touches on all of the important parts of AngularJS using a simple example. For a more in-depth explanation, see the tutorial.

本节使用一个简单的例子,简要介绍了AngularJS的所有重要组成部分。为了更深入的说明,请参见教程。

Concept

Description

Template

HTML with additional markup

模板

添加额外标记的HTML

Directives

extend HTML with custom attributes and elements

指令

扩展HTML用自定义的属性和元素

Model

the data shown to the user in the view and with which the user interacts

模型

在View中展示给用户的数据和与用户交互的数据。

Scope

context where the model is stored so that controllers, directives and expressions can access it

作用域

模型存储的上下文,这样控制器、指令和表达式可以访问。

Expressions

access variables and functions from the scope

表达式

从作用域中访问变量和函数。

Compiler

parses the template and instantiates directives and expressions

编译器

解析模板和实例化指令和表达式

Filter

formats the value of an expression for display to the user

过滤器

格式化表达式显示给用户的值

View

what the user sees (the DOM)

视图

用户看到的内容(即DOM)

Data Binding

sync data between the model and the view

数据绑定

在模型和视图之间同步的数据。

Controller

the business logic behind views

控制器

在视图背后的业务逻辑

Dependency Injection

Creates and wires objects and functions

依赖注入

创建和关联对象与函数

Injector

dependency injection container

注入器

依赖注入的容器

Module

a container for the different parts of an app including controllers, services, filters, directives which configures the Injector

模块、组件

一个容器用于一个应用的不同部分,包括控制器、服务、过滤器、指令,用于配制注入器。

Service

reusable business logic independent of views

服务

可重用的独立视图上的业务逻辑

 

A first example:Data binding

第一个例子:数据绑定。

In the following example we will build a form to calculate the costs of an invoice in different currencies.

Let's start with input fields for quantity and cost whose values are multiplied to produce the total of the invoice:

在下面的例子中,我们将建立一个表单来计算不同货币的发票的成本。

让我们从quantity和cost的输入内容开始,他们的值相乘得到invoice字段的结果:

<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>

Try out the Live Preview above, and then let's walk through the example and describe what's going on.

尝试实时预览上面,然后让我们查看例子,说明发生了什么事情。


This looks like normal HTML, with some new markup. In Angular, a file like this is called a "template".When Angular starts your application, it parses and processes this new markup from the template using the so-called "compiler".The loaded, transformed and rendered DOM is then called the "view".


这个看起来像带有一些新标记的普通HTML。在Angular,像这样的文件叫做“模板”。当Angular启动你的应用程序,它解析和运行这个新的标记从模板中,这个叫“编译”。加载,转换和呈现DOM,然后“视图”。
The first kind of new markup are the so-called "directives". They apply special behavior to attributes or elements in the HTML. In the example above we use the ng-app attribute, which is linked to a directive that automatically initializes our application. Angular also defines a directive for the input element that adds extra behavior to the element. The ng-model directive stores/updates the value of the input field into/from a variable.
第一类新的标记是所谓的“指令”。它们提供特殊行为在HTML属性或元素上。在上面的例子中,我们使用了ng-app的属性,它链接到一个指令,这个指令自动初始化我们的应用程序。Angular还定义了一个指令为这个input元素,增加了额外的行为给元素。指令ng-model 存储/更新了input区域的value属性的进入/取出变量的值。

Custom directives to access the DOM: In Angular, the only place where an application should access the DOM is within directives. This is important because artifacts that access the DOM are hard to test. If you need to access the DOM directly you should write a custom directive for this. The directives guide explains how to do this.
自定义指令访问DOM:在Angular,应用程序应该访问DOM的唯一的地方就是指令范围内。这是重要的,因为该访问DOM是难以测试的。如果您需要直接地访问DOM,你可以写一个自定义指令。该directives guide介绍了如何做到这一点。

The second kind of new markup are the double curly braces {{ expression | filter }}: When the compiler encounters this markup, it will replace it with the evaluated value of the markup. An "expression" in a template is a JavaScript-like code snippet that allows to read and write variables. Note that those variables are not global variables. Just like variables in a JavaScript function live in a scope, Angular provides a "scope" for the variables accessible to expressions. The values that are stored in variables on the scope are referred to as the "model" in the rest of the documentation. Applied to the example above, the markup directs Angular to "take the data we got from the input widgets and multiply them together".
第二类新的标记是双花括号{{ expression | filter }}:当编译器遇到这样的标记,它会用标记的计算值替换。在模板中的“表达式”是一个JavaScript类的代码片段,允许读取和写入变量。需要注意的是这些变量不是全局变量。就像在一个JavaScript函数变量作用在一个作用域内,Angular提供了“作用域”的访问表达式的变量。存储在作用域内的变量的值被称为在文件的其余部分的“模型”。应用到上面的例子中,标记指示Angular“获取我们从输入部件得到的数据,并将它们相乘在一起”。
The example above also contains a "filter". A filter formats the value of an expression for display to the user. In the example above, the filter currency formats a number into an output that looks like money.
上面的例子中还包含一个“过滤器”。过滤器格式化了显示给用户的表达式的值。在上面的例子中,过滤器currency把数格式化成看起来像钱的输出。
The important thing in the example is that Angular provides live bindings: Whenever the input values change, the value of the expressions are automatically recalculated and the DOM is updated with their values. The concept behind this is "two-way data binding".

在本例中,重要的是,Angular提供现场绑定:无论何时只要输入值发生变化,表达式的值会自动重新计算,DOM会用这些他们的值更新。这背后的概念是“双向数据绑定”。


Adding UI logic:Controllers

添加UI逻辑:控制器

Let's add some more logic to the example that allows us to enter and calculate the costs indifferent currencies and also pay the invoice.

让我添加更多的逻辑给例子,例子允许我们输入和计算价值以不同的货币,同时也可以支付发票。

<div class="runnable-example-file" name="invoice1.js" language="js" type="js"><!-- index.html --></div><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>

//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!");
  };
});

What changed?

什么变化了?

First, there is anew JavaScript file that contains a so-called "controller".More exactly, the file contains a constructor function that creates the actual controller instance. The purpose of controllers is to expose variables and functionality to expressions and directives.

首先,有一个新的包含了叫controller的javascript文件。确切的说,这个文件包含了一个构造函数用于创建实际的控制器实例。这个控制器的目的是暴露变量和函数给表达式和指令。

 

Besides the new file that contains the controller code we also added an ng-controller directive to the HTML. This directive tells Angular that the new InvoiceController is responsible for the element with the directive and all of the element's children. The syntax InvoiceController as invoice tells Angular to instantiate the controller and save it in the variable invoice in the current scope.

除了新的文件包含控制器代码,我们也要添加一个ng-controller指令到HTML上。这个指令告诉Angular新的InvoiceController负责这个带有指令的元素和其子元素。语法InvoiceController as invoice告诉Angular实例化控制器,保存到变量invoice上在当前作用域内。(经测试当前控制器InvoiceController内的$scope对象中的invoice变量保存了此控制器的引用)

 

We also changed all expressions in the page to read and write variables within that controller instance by prefixing them with invoice. . The possible currencies are defined in the controller and added to the template using ng-repeat. As the controller contains a total function we are also able to bind the result of that function to the DOM using {{ invoice.total(...) }}.

我们也改变了所有表达式在页面中的读写变量,用invoice前缀来表示控制器的实例。货币被定义在控制器中,被添加到模板中使用ng-repeat指令。控制中含有一个total函数,我们也可以绑定这个函数的结果到DOM中,使用{{invoice.total(…)}}。

 

Again, this binding is live, i.e. the DOM will be automatically updated whenever the result of the function changes. The button to pay the invoice uses the directive ngClick. This will evaluate the corresponding expression whenever the button is clicked.

同样,这个绑定是动态的,当函数的结果发生变化时DOM将会自动的更新。按钮pay使用的是指令ngClick。当按钮被点击时,将要计算对应按钮的表达式。

In the new JavaScript file we are also creating a module at which we register the controller. We will talk about modules in the next section.

在新的javascript文件中,我们同样创建了一个module用来注册控制器。我们将要在下一节讨论关于modules的内容。

The following graphic shows how everything works together after we introduced the controller:

介绍完控制器后,下图介绍了每件事情是如何一起工作的:

View independent business logic: Services

视图的独立业务逻辑:服务

Right now, the Invoice Controller contains all logic of our example. When the application grows it is a good practice to move view independent logic from the controller into a so called "service",so it can be reused by other parts of the application as well. Later on, we could also change that service to load the exchange rates from the web, e.g. by calling the Yahoo Finance API, without changing the controller.

当前,控制器invoiceController包含了我们例子中所有的逻辑。当应用内容增加时,一个很好的做法是移动视图中独立的逻辑从控制器到服务中,这样他可以被应用程序的其他部分所重复使用。之后,我们还可以变更服务,来从网络中获取汇率等,调用Yahoo的财经API,而不同去改变控制器的内容。

Let's refactor our example and move the currency conversion into a service in another file:

让我们来重构我们的代码,移动货币转换功能作为一个服务保存在另一个文件中。

<!-- 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>
//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!");
  };
}]);

//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
  };
});

What changed? We moved the convertCurrency function and the definition of the existing currencies into the new file finance2.js. But how does the controller get a hold of the now separated function?

什么改变了?我们移动convertCurrency函数,将现存的currencies定义到一个新的文件finance2.js中。但是当前的控制器是如何获得一个当前分离出的函数的?。

This is where "Dependency Injection" comes into play. Dependency Injection (DI) is a software design pattern that deals with how objects and functions get created and how they get a hold of their dependencies. Everything within Angular (directives,filters, controllers, services, ...) is created and wired using dependency injection. Within Angular, the DI container is called the "injector".

这就要依赖注入来发挥作用了。依赖注入(DI)是一个软件设计模式,这个设计模式处理如何获得创建的对象和函数以及如何获得他们的依赖关系。在Angular内的任何内容(指令,过滤器,控制器,服务等)都通过使用依赖注入被创建并连接的。在Angular中,依赖注入的容器叫做注入器。

To use DI, there needs to be a place where all the things that should work together are registered. In Angular, this is the purpose of the so-called "modules". When Angular starts, it will use the configuration of the module with the name defined by the ng-app directive, including the configuration of all modules that this module depends on.

要使用依赖注入,需要一个空间将所有一起工作的内容注册进去。在Angular中,这就是模块modules的目的。当Angular开启时,它将会使用以ng-app指令的名称定义的模块属性,包括这个模块依赖的所有模块的属性。

In the example above: The template contains the directive ng-app="invoice2". This tells Angular to use the invoice2 module as the main module for the application. The code snippet angular.module('invoice2', ['finance2']) specifies that the invoice2 module depends on the finance2 module. By this, Angular uses the InvoiceController as well as the currencyConverter service.

在上面的例子中:模板包含指令ng-app=”invoice2”。这告诉Angular,对于当前应用要使用invoice2模板作为主模板。代码片段angular.module('invoice2',['finance2'])描述了invoice2模块依赖finacnce2模块。由此,Angular能够使用InvocieController控制器和currencyConverter服务。

Now that Angular knows of all the parts of the application, it needs to create them. In the previous section we saw that controllers are created using a factory function.For services there are multiple ways to define their factory (see the service guide). In the example above, we are using a function that returns the currencyConverterfunction as the factory for the service.

Back to the initial question: How does the InvoiceController get a reference to the currencyConverter function? In Angular, this is done by simply defining arguments on the constructor function. With this, the injector is able to create the objects in the right order and pass the previously created objects into the factories of the objects that depend on them. In our example, the InvoiceController has an argument named currencyConverter. By this, Angular knows about the dependency between the controller and the service and calls the controller with the service instance as argument.

现在Angular知道应用程序的所有部分,它需要去创建他们。在上一节中我们了解到控制器能够使用工厂函数被创建。服务有很多方法定义他们的工厂(参考服务向导)。在上面的李自重,我们使用一个函数,这个函数返回currencyConverter函数作为工厂的服务。回到最初的问题:InvoiceController控制器是如何获得currencyConverter函数的引用的?在Anugular中,这是通过在构造函数中简单的定义参数完成的。与此相关,注入器能够创建对象按照右侧的顺序,并把之前创建的对象传递到依赖于他们的对象工厂内。在我们的例子中,InvoiceController控制器有一个参数叫currencyConverter。由此,Angular能够知道在控制器和服务之间的依赖关系,并调用与该服务实例作为参数的控制器。

 

The last thing that changed in the example between the previous section and this section is that we now pass an array to the module.controller function, instead of a plain function. The array first contains the names of the service dependencies that the controller needs. The last entry in the array is the controller constructor function. Angular uses this array syntax to define the dependencies so that the DI also works after minifying the code, which will most probably rename the argument name of the controller constructor function to something shorter like a.

例子中上一小节和这一节之间变化的最后内容是我们传递了一个数组到module.contoller函数中,代替了一个简单的函数。这个数组的第一个参数包含了控制器需要的服务依赖关系的名称。数组第二个参数是控制器的构造函数。Angular使用数组语法定义了依赖关系使得在减少代码后依赖注入也可以起作用,可以重新命名控制器的构造函数的参数名称,使其更加短小,例如a。


Accessing the backend

访问后端

Let's finish our example by fetching the exchange rates from the Yahoo Finance API. The following example shows how this is done with Angular:

让我们通过雅虎财经提供的API获取汇率来完成我们的例子。下例先显示了在Angular中适合使用。

<!-- 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>

//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
  };
}]);


What changed? Our currencyConverter service of the finance module now uses the $http, a built-in service provided by Angular for accessing a server backend. $http is a wrapper around XMLHttpRequest and JSONP transports.

什么变化了?我们finance模块的currencyConverter服务使用了$http,$http是Angular提供的一个访问后端的内置服务。$http包装了XMLHttpRequest和JSONP传输。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章