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傳輸。





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