使用 Angular 2 實現單頁應用程序

qtuy9671 7年前發布 | 42K 次閱讀 Web框架 angularjs

由于具有實現更高性能的瀏覽器和智能電話應用程序的潛力,單頁應用程序 (SPA) 技術在軟件行業引起了廣泛的興趣。在過去 5 年多的時間里,開發人員對 Angular (一個開源 SPA 框架)的興趣遠超他們對其他 Web 框架(React、Backbone、Meteor 和 Ember)的興趣,這從 StackOverflow 網站上針對每種 Web 框架的問題數量上可以判斷出:

使用 Angular 2 實現單頁應用程序

Web 和移動開發人員非常喜歡 Angular 2(2016 年 9 月發布)。Angular 2 不是 Angular 1 的一次升級,而是一個全新的、不同的、更高級的框架。精通 Angular 2 已成為構建高性能、可擴展、穩健、現代的跨平臺應用程序的一種很吃香的技能。

TypeScript

Angular 2 支持 JavaScript、Dart 和 TypeScript。您將使用 TypeScript 作為本教程的項目的開發語言。該框架構建于 TypeScript 之上,大多數有關 Angular 的文檔、圖書和教程都側重于將 TypeScript 作為開發語言。

本教程將介紹 Angular 2 的重要構建基塊,演示如何在開發計算機和沙箱服務器中使用 Angular 2 編寫和運行 SPA。要充分掌握本教程,您需要具有一定的 Web 編程經驗,包括 JavaScript、TypeScript 和 HTML 的各方面知識。無需提前擁有 Angular 經驗。練習完示例項目后,您就會為使用 Angular 2 創建自己的 SPA 做好充足的準備。

可以從教程末尾的 “可下載資源” 部分下載針對沙箱應用程序的完整示例代碼。

為什么使用 SPA 和 Angular 2?

在用戶啟動 SPA 時,該應用程序僅呈現來自服務器的一個 HTML 頁面。除了這個 HTML 頁面,服務器還會向客戶端發送一個應用程序引擎。該引擎控制整個應用程序,包括HTML 頁面的處理、輸入、輸出、繪制和加載。通常,90–95% 的應用程序代碼是在瀏覽器中運行;當用戶需要新數據時或必須執行身份驗證等安全操作時,就會在服務器中運行剩余代碼。由于幾乎消除了對服務器的依賴,所以 SPA 能在 Angular 2 環境中自動擴展:無論有多少用戶同時訪問服務器,在 90–95% 的時間里,應用程序的性能絕不會受到影響。

另外,因為大部分負載都在客戶端上運行,所以服務器在大部分時間里都處于空閑狀態。對服務器資源的低需求顯著減少了服務器上的壓力,也潛在地降低了服務器成本。

Angular 2 的另一個優勢是,它可以幫助 SPA 有效地使用微服務。

“ 通常,90–95% 的 SPA 代碼在瀏覽器中運行;當用戶需要新數據或必須執行身份驗證等安全操作時,就會在服務器中運行剩余代碼。 ”

Angular 2 概念

Angular 2 中的關鍵概念包括:

  • 模塊
  • 組件
  • 服務
  • 路由

從現在開始,我將 Angular 2 簡稱為 Angular。

模塊

Angular 應用程序是模塊化的。每個 Angular 應用程序擁有至少一個 模塊 ,即根模塊,傳統上將其命名為 AppModule 。根模塊可以是小型應用程序中唯一的模塊,但大多數應用程序都擁有更多的模塊。作為開發人員,您需要決定如何使用模塊概念。通常,會將主要功能或特性映射到一個模塊。假設您的系統中有 5 個主要區域。除了根模塊之外,每個區域都將擁有自己的模塊,總計 6 個模塊。

組件

組件 控制頁面的一片區域,該區域被稱為 視圖 。您定義組件的應用程序邏輯 — 它如何支持類中的視圖。類通過屬性和方法的 API 與視圖交互。一個組件包含一個類、一個模板和元數據。模板是一個 HTML 表單,用于告訴 Angular 如何呈現組件。一個組件只能屬于一個模塊。

服務

服務 提供應用程序所需的任何值、功能或特性。服務通常是一個具有明確定義的較小用途的類;它應高效地執行具體的操作。組件大量使用服務。服務大量使用微服務。

路由

路由 支持在用戶執行應用程序任務時,從一個視圖導航到下一個視圖。路由等效于用于控制菜單和子菜單的機制。

了解 SPA 的優勢并掌握 Angular 概念后,是時候在示例項目上開始實踐了。

需要做的準備工作

要完成示例項目,您需要在開發 PC 上安裝 Node.js 和 Angular CLI (一個用于 Angular 的命令行接口):

  • 安裝 Node.js:
    1. 下載 適合您的系統的版本,選擇默認選項來完成安裝。
    2. 從操作系統命令行運行 node -v 來驗證版本號 — 在我的情況下,版本為 v6.9.1 。
    Node Package Manager (NPM) 會隨 Node 自動安裝。鍵入 npm -v 來查看它的版本號。當您安裝來自公共 NPM 存儲庫的包時,會在后臺運行 NPM。一個常見的 NPM 命令是 npm install ,它用于下載您的 Angular 項目的文件中列出的包版本。
  • 安裝 Angular CLI:
    1. 運行 npm install -g angular-cli@1.0.0-beta.21 來安裝我用于示例應用程序的版本(在編寫本文時,仍處于公測階段)。(如果您想嘗試一個不同的編譯版,可訪問 CLI 站點 。)需要花約 10 分鐘才能完成安裝。
    2. 成功完成安裝后,在操作系統命令行上鍵入 ng -v ,以查看您的 CLI 版本號 — 在我的操作系統下:
      angular-cli: 1.0.0-beta.21
      node: 6.9.1
      os: win32 x64
      如果您需要獲得有關 ng 語法的幫助,可在操作系統命令行上鍵入 ng help 。

package.json 文件

package.json 文件(Angular 應用程序中一個關鍵的元數據文件)包含應用程序和它的依賴包的細節。此文件是 Angular 應用程序中最重要的文件,尤其是在將代碼從一臺計算機遷移到另一臺時,或者是在升級期間。package.json 文件包含需要安裝的包版本。

您無需為適合本教程練習的 package.json 感到擔心。但在開發生產級應用程序時,必須多多留意此文件中列出的版本號。

以下是來自一個 package.json 文件的一些有效語句:

{ "dependencies" :
  { "foo" : "1.0.0 - 2.9999.9999"
  , "bar" : ">=1.0.2 <2.1.2"
  , "baz" : ">1.0.2 <=2.3.4"
  , "boo" : "2.0.1"
  , "qux" : "<1.0.0 || >=2.3.1 <2.4.5 || >=2.5.2 <3.0.0"
  , "til" : "^1.2"
  , "elf" : "~1.2.3"
  , "two" : "2.x"
  , "thr" : "3.3.x"
  , "lat" : "latest"
  }
}

npm install 命令對冒號右側的部分語句的解釋如下(其中 版本 表示所使用的版本號):

  • " 版本 ":必須與 版本 準確匹配
  • "> 版本 ":必須大于 版本
  • "~ 版本 ":約等于 版本
  • "^ 版本 ":兼容 版本
  • "1.2.x":1.2.0、1.2.1 等,但不能是 1.3.0
  • "*" :匹配任何 版本
  • ""(空字符串)":與 * 相同
  • " 版本 1 - 版本 2 ":與 ">= 版本 1 <= 版本 2 " 相同

要進一步了解 package.json,可在操作系統命令行上鍵入 npm help package.json ,以便在瀏覽器中查看幫助內容。

示例項目概述

示例項目包含一個開箱即用的 Angular 應用程序,以及您將在開箱即用的應用程序上開發的一個自定義應用程序。當您完成上述操作后,您將擁有一個包含 3 個微型應用程序的 Angular 應用程序,每個微型應用程序中的特性使用了 3 個 Web 服務 API:

  • 來自雅虎的天氣組件
  • 貨幣兌換
  • 電影細節

所有應用程序邏輯都將在您的瀏覽器中運行。僅在瀏覽器需要新數據時,才需要服務器。事實上,您可以關閉服務器進程,它仍會在您的應用程序中工作,因為它是一個 SPA。

下圖顯示了應用程序拓撲結構:

使用 Angular 2 實現單頁應用程序

您將使用 Angular CLI 創建您的項目(該項目默認情況下會包含 AppModule 和 AppComponent )和 4 個自定義組件:

  • 菜單組件
  • 天氣組件
  • 電影組件
  • 貨幣組件

您將創建用于菜單導航的路由,并將以下服務注入到天氣、電影和貨幣組件中:

  • 來自使用微服務的 HTTP 的數據
  • 在使用這些服務時跨組件的資源共享

創建基礎應用程序和模塊

準備好開始了嗎?首先在操作系統命令行上轉到您想放置項目目錄的位置。

創建一個 Angular 項目

運行下面的命令來生成一個新 Angular 項目(其中 dw_ng2_app 是項目名稱):

ng new dw_ng2_app --skip-git

安裝所有需要的包和 Angular 基礎應用程序(這將花費大約 10 分鐘時間)后,您將返回到操作系統命令提示符上。如果您隨后列出 /dw_ng2_app 目錄的內容,就可以看到項目結構:

|— e2e
|— node_modules
|— src
angular-cli.json
karma.conf.js
package.json
protractor.conf.js
README.md
tslint.json

../dw_ng2_app/src 目錄的內容包括:

|— app
|— assets
|— environments
favicon.ico
index.html
main.ts
polyfills.ts
styles.css
test.ts
tsconfig.json
typings.d.ts

../dw_ng2_app/src/app 目錄( 根模塊文件夾 )包含以下文件:

app.component.css
app.component.html
app.component.spec.ts
app.component.ts
app.module.ts
index.ts

運行開箱即用的 Angular 應用程序

更改到項目目錄,運行 ng serve 來啟動開箱即用的 Angular 應用程序。

默認情況下,該進程在端口 4200 上啟動。如果您的 port 系統環境變量的值不是 4200,該進程將在此端口上啟動。您可以運行 ng serve --port 4200 命令來覆蓋默認端口號,這是一項可選操作。

打開您的瀏覽器并輸入 URL http://localhost:4200/ 。您的 Angular 應用程序會顯示 app works! ,這表明應用程序已啟動、運行并準備就緒:

使用 Angular 2 實現單頁應用程序

如果在應用程序運行過程中更改代碼,Angular 會非常智能地監視并自動重新啟動應用程序。嘗試編輯 app.component.ts 文件,更改 title 的值。您可以看到,您的瀏覽器頁面反映了這一更改:

使用 Angular 2 實現單頁應用程序

如何將模塊鏈接到組件

在清單 1 中,第 20 行顯示了 AppModule 模塊的聲明。

清單 1. app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

此模塊僅包含一個組件 — AppComponent — 如上第 10 行所示。第 18 行表明,在引導進程下啟動的第一個組件是 AppComponent 。

組件內部結構

清單 2 給出了 app.component.ts 文件中的主要應用程序組件的內容。

清單 2. app.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'My Angular 2 app works!';
}

這是 app.component.html 文件中的主要應用程序組件的內容:

<h1>
  {{title}}
</h1>

元數據位置

元數據告訴 Angular 如何處理類。事實上,在您通過向 AppComponent 附加元數據給類時,告訴 Angular 它還不是組件。

可以使用 @Component 修飾器附加元數據,這會將類標識為組件。 @Component 修飾器接受一個必需的配置對象,該對象包含 Angular 創建和呈現該組件和它的視圖所需的信息。

中的代碼使用可用的 @Component 配置選項中的 3 個選項:

  • selector :一個 CSS 選擇器,告訴 Angular 在父 HTML 文件中找到 selector 標記的地方創建和插入此組件的一個實例
  • templateUrl :組件的 HTML 文件
  • styleUrls :組件的樣式表,比如 .css 文件

組件內的模板位置

模板是一個 HTML 表單,用于告訴 Angular 如何呈現組件。在的第 5 行上, templateUrl 指向一個名為 app.component.html 的視圖。

組件內的數據綁定

數據綁定是一個 HTML 表單,用于告訴 Angular 如何呈現組件。在 app.component.ts 文件中, title 的值在類內設置,在 app.component.html 文件中使用。數據綁定可以是單向或雙向的。在本例中,如果您在雙花括號 {{ }} 內提及該變量,則該映射是單向的。值從類傳遞到 HTML 文件。

創建自定義組件和路由

此刻,您的 Angular 應用程序已準備就緒并能工作正常。這個基礎應用程序有一個模塊、一個組件、一個類、一個模板、元數據和數據綁定 — 但它仍缺少 4 個其他的重要部分:

  • 多個組件
  • 路由
  • 服務
  • 微服務的使用

接下來,您將創建這些自定義組件。

創建自定義組件

按 Ctrl-C 停止 Angular 進程(確保您在 Angular 項目的目錄中,在本例中為 dw_ng2_app)。在命令提示符下,運行以下命令:

  • ng g c Menu -is --spec false --flat :在 AppModule 根模塊(同一個文件夾中)內創建 Menu 組件。
  • ng g c Weather -is --spec false :在 AppModule 根模塊內名為 weather 的子文件夾中創建 Weather 組件。
  • ng g c Currency -is --spec false :在 AppModule 根模塊內名為 currency 的子文件夾中創建 Currency 組件。
  • ng g c Movie -is --spec false :在 AppModule 根模塊內名為 movie 的子文件夾中創建 Movie 組件。

現在,有了創建的新組件 — 包括類、元數據和模板 — 您可以看到 AppModule 如何鏈接到這些組件。在清單 3 中,第 28 行包含 AppModule 模塊的聲明。此模塊包含 5 個組件,包括根組件和其他 4 個組件,如第 14–18 行所示。

清單 3. app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { AppComponent } from './app.component';
import { MenuComponent } from './menu.component';
import { WeatherComponent } from './weather/weather.component';
import { CurrencyComponent } from './currency/currency.component';
import { MovieComponent } from './movie/movie.component';

@NgModule({
  declarations: [
    AppComponent,
    MenuComponent,
    WeatherComponent,
    CurrencyComponent,
    MovieComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

創建路由

路由創建命令

我在這里提供了手動創建路由的操作說明。截至編寫本文時,創建路由的 CLI 命令正在開發之中。您可以查閱 CLI 網站 ,看看它現在是否可用。

要讓 Angular 能在組件之間導航,需要創建 路由 。使用清單 4 的內容覆蓋 menu.component.html 文件,以便 HTML 包含所有組件的正確菜單。

清單 4. menu.component.html

<div class="row">
   <div class="col-xs-12">
    <ul class="nav nav-pills">
     <li routerLinkActive="active"> <a [routerLink]="['/weather']" >Weather</a></li>
     <li routerLinkActive="active"> <a [routerLink]="['/movie']" >Movie Details</a></li>
     <li routerLinkActive="active"> <a [routerLink]="['/currency']" >Currency Rates</a></li>
    </ul>
  </div>
 </div>

清單 4 中的代碼提供了 GUI 與 URL 路徑之間的映射。例如,當用戶單擊 GUI 中的 Movie Details 按鈕時,Angular 知道它需要像 URL 路徑為 http://localhost:4200/movie 一樣運行。

接下來,將 URL 路徑映射到組件。在根模塊的相同文件夾中,創建一個名為 app.routing.ts 的配置文件,使用清單 5 中的代碼作為其內容。

清單 5. app.routing.ts

import { Routes, RouterModule } from '@angular/router';
import { CurrencyComponent } from "./currency/currency.component";
import { WeatherComponent } from "./weather/weather.component";
import { MovieComponent } from "./movie/movie.component";
const MAINMENU_ROUTES: Routes = [
    //full : makes sure the path is absolute path
    { path: '', redirectTo: '/weather', pathMatch: 'full' },
    { path: 'weather', component: WeatherComponent },
    { path: 'movie', component: MovieComponent },
    { path: 'currency', component: CurrencyComponent } 
];
export const CONST_ROUTING = RouterModule.forRoot(MAINMENU_ROUTES);

在本例中,如果您的 URL 相對路徑是 movie ,則會告訴 Angular 調用 MovieComponent 組件。換句話說,相對路徑 movie 映射到 URL http://localhost:4200/movie 。

現在,您需要將此視圖鏈接到它的父組件。使用以下代碼覆蓋 app.component.html 文件內容:

<div class="container">
 <app-menu></app-menu>
 <hr>
 <router-outlet></router-outlet>
</div>

<app-menu></app-menu> 選擇器會包含菜單。 <router-outlet></router-outlet> 選擇器是當前組件的占位符。根據 URL 路徑,該值可以是以下 3 個組件中的任意一個:天氣、電影或貨幣。

您還必須向該模塊告知此路由。在 app.module.ts 文件中添加兩項,如清單 6 中的第 11 和 25 行所示。

清單 6. app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { AppComponent } from './app.component';
import { MenuComponent } from './menu.component';
import { WeatherComponent } from './weather/weather.component';
import { CurrencyComponent } from './currency/currency.component';
import { MovieComponent } from './movie/movie.component';
import { CONST_ROUTING } from './app.routing'; 

@NgModule({
  declarations: [
    AppComponent,
    MenuComponent,
    WeatherComponent,
    CurrencyComponent,
    MovieComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    CONST_ROUTING
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

現在,如果您運行應用程序并單擊 Weather 鏈接,應用程序將會顯示 weather works!

使用 Angular 2 實現單頁應用程序

如果單擊 Movie Details 鏈接,應用程序會顯示 movie works!

使用 Angular 2 實現單頁應用程序

如果單擊 Currency Rates 鏈接,應用程序將會顯示 currency works!

使用 Angular 2 實現單頁應用程序

您已成功修改了您的 Angular 應用程序,以便包含多個自定義組件和路由。現在您已經準備好執行最后兩個重要操作:

  • 創建和配置服務
  • 使用微服務

創建服務

按下 Ctrl-C 停止 Angular 進程。運行下面的命令:

ng g service Shared --spec false

此命令在根模塊文件夾中的 shared.service.ts 文件中創建該服務:

使用 Angular 2 實現單頁應用程序

將 shared.service.ts 的內容替換為清單 7 中的代碼。

清單 7. shared.service.ts

import { Injectable } from '@angular/core';
import { Http, Headers, Response } from "@angular/http";
import 'rxjs/Rx';
import { Observable } from "rxjs";

@Injectable()
export class SharedService {
    weatherURL1 = "https://query.yahooapis.com/v1/public/yql?q=select%20*%20from%20weather.forecast%20where%20woeid%20in%20(select%20woeid%20from%20geo.places(1)%20where%20text%3D%22";
    weatherURL2 = "%2C%20";
    weatherURL3 = "%22)&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys";
    findMovieURL1 = "http://www.omdbapi.com/?t=";
    findMovieURL2 = "&y=&plot=short&r=json"; 
    currencyURL = "http://api.fixer.io/latest?symbols="; 
    totReqsMade: number = 0;
    constructor(private _http: Http) { }

    findWeather(city, state) { 
        this.totReqsMade = this.totReqsMade + 1; 
        return this._http.get(this.weatherURL1 + city + this.weatherURL2+ state + this.weatherURL3)
            .map(response => {
                { return response.json() };
            })
            .catch(error => Observable.throw(error.json()));
    }

    findMovie(movie) { 
        this.totReqsMade = this.totReqsMade + 1; 
        return this._http.get(this.findMovieURL1 + movie + this.findMovieURL2)
            .map(response => {
                { return response.json() };
            })
            .catch(error => Observable.throw(error.json().error));
    }

    getCurrencyExchRate(currency) { 
        this.totReqsMade = this.totReqsMade + 1; 
        return this._http.get(this.currencyURL + currency)
            .map(response => {
                { return response.json() };
            })
            .catch(error => Observable.throw(error.json()));
    }
}

清單 7 中的 import ... 語句是任何服務正常運行所必不可少的。 @Injectable() 語句特別重要;它表明此 service 可注入到其他組件中 — 該技術通常被稱為 依賴注入

totReqsMade 變量在這里聲明,將用于在 3 個組件之間傳遞它的值。這將跟蹤為獲得微服務結果而發出的服務請求總數。

您有 3 個方法,它們的名稱表明了自己的功能: findWeather() 、 findMovie() 和 getCurrencyExchRate() 。在方法執行期間,您的 Angular 應用程序將讓瀏覽器訪問網絡來使用微服務。現在您將把組件鏈接到所創建的服務。

將 movie.component.ts 文件內容替換為清單 8 中的代碼。

清單 8. movie.component.ts

import { Component, OnInit } from '@angular/core';
import { SharedService } from "./../shared.service";

@Component({
  selector: 'app-movie',
  templateUrl: './movie.component.html',
  styles: []
})
export class MovieComponent implements OnInit {
    id_movie: string = "";
    mv_Title: string = "";
    mv_Rated: string = "";
    mv_Released: string = "";
    mv_Director: string = "";
    mv_Actors: string = "";
    mv_Plot: string = "";
    constructor(private _sharedService: SharedService) {
    }

    ngOnInit() {
    }

    callMovieService() { 
        this._sharedService.findMovie(this.id_movie)
            .subscribe(
            lstresult => { 
                this.mv_Title = lstresult["Title"];
                this.mv_Rated = lstresult["Rated"];

                this.mv_Released = lstresult["Released"];
                this.mv_Director = lstresult["Director"];
                this.mv_Actors = lstresult["Actors"];
                this.mv_Plot = lstresult["Plot"];
            },
            error => {
                console.log("Error. The findMovie result JSON value is as follows:");
                console.log(error);
            }
            ); 
    }
}

這個重要的代碼端調用服務方法來獲取新數據。在本例中,它調用 callMovieService() ,然后調用 this._sharedService.findMovie() 方法。

類似地,將 currency.component.ts 文件內容替換為清單 9 中的代碼。

清單 9. currency.component.ts

import { Component, OnInit } from '@angular/core';
import { SharedService } from "./../shared.service";

@Component({
  selector: 'app-currency',
  templateUrl: './currency.component.html',
  styles: [] 
})
export class CurrencyComponent implements OnInit {

  id_currency: string = "";
  my_result: any;
  constructor(private _sharedService: SharedService) {
  }

  ngOnInit() {
  }

  callCurrencyService() {  
    this._sharedService.getCurrencyExchRate(this.id_currency.toUpperCase())
      .subscribe(
      lstresult => { 
                this.my_result = JSON.stringify(lstresult); 
      },
      error => {
        console.log("Error. The callCurrencyService result JSON value is as follows:");
        console.log(error);
      }
      ); 
  }
}

將 weather.component.ts 文件內容替換為清單 10 中的代碼。

清單 10. weather.component.ts

import { Component, OnInit } from '@angular/core';
import { SharedService } from "./../shared.service";

@Component({
  selector: 'app-weather',
  templateUrl: './weather.component.html',
  styles: []

})
export class WeatherComponent implements OnInit {
  id_city: string = "";
  id_state: string = "";
  op_city: string = "";
  op_region: string = "";
  op_country: string = "";
  op_date: string = "";
  op_text: string = "";
  op_temp: string = "";
  constructor(private _sharedService: SharedService) {
  }

  ngOnInit() {
  }

  callWeatherService() { 
    this._sharedService.findWeather(this.id_city, this.id_state)
      .subscribe(
      lstresult => { 
        this.op_city = lstresult["query"]["results"]["channel"]["location"]["city"];
        this.op_region = lstresult["query"]["results"]["channel"]["location"]["region"];
        this.op_country = lstresult["query"]["results"]["channel"]["location"]["country"];
        this.op_date = lstresult["query"]["results"]["channel"]["item"]["condition"]["date"];
        this.op_text = lstresult["query"]["results"]["channel"]["item"]["condition"]["text"];
        this.op_temp = lstresult["query"]["results"]["channel"]["item"]["condition"]["temp"]; 
      },
      error => {
        console.log("Error. The findWeather result JSON value is as follows:");
        console.log(error);
      }
      ); 
  }
}

現在,更新該模塊以包含這些服務。編輯 app.module.ts 文件,以包含清單 11 的第 12 和 28 行的兩條語句。

清單 11. app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';

import { AppComponent } from './app.component';
import { MenuComponent } from './menu.component';
import { WeatherComponent } from './weather/weather.component';
import { CurrencyComponent } from './currency/currency.component';
import { MovieComponent } from './movie/movie.component';
import { CONST_ROUTING } from './app.routing';
import { SharedService } from "./shared.service";

@NgModule({
  declarations: [
    AppComponent,
    MenuComponent,
    WeatherComponent,
    CurrencyComponent,
    MovieComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    CONST_ROUTING
  ],
  providers: [SharedService],
  bootstrap: [AppComponent]
})
export class AppModule { }

修改組件視圖

現在還剩難題的最后一部分。您需要告訴 HTML 文件調用正確的服務方法。為此,請將 movie.component.html 文件的內容替換為清單 12 中的代碼。

清單 12. movie.component.html

<h2>Open Movie Database</h2>
<div class="col-md-8 col-md-offset-2">
 <div class="form-group">
  <input type="text" required [(ngModel)]="id_movie" (change)="callMovieService()" class="form-control" placeholder="Enter Movie name ...">
  <br><br>
  <h3>Movie Details</h3>
  <br>
  <p class="well lead">
      <i> Title :</i> {{ this.mv_Title }} <br>
      <i> Plot :</i> {{ this.mv_Plot }} <br>
      <i> Actors :</i> {{ this.mv_Actors }} <br>
      <i> Directed by :</i> {{ this.mv_Director }} <br>
      <i> Rated :</i> {{ this.mv_Rated }} <br>
      <i> Release Date :</i> {{ this.mv_Released }} <br>
  </p>
  <p class="text-info">Total # of all the service requests including Weather, Movie, and Currency is :
      <span class="badge">{{this._sharedService.totReqsMade}}</span>
  </p>
 </div>
</div>

movie.component.html 中編碼了一些重要的信息:

  • {{ this._sharedService.totReqsMade }} :這是在服務級別上跟蹤的值,它會在所有 3 個應用程序組件之間共享。
  • [(ngModel)]="id_movie" :用戶輸入的 GUI 輸入被傳遞到調用此 HTML 的類。在本例中,該類為 MovieComponent 。
  • (change)="callMovieService() ":當此字段值更改時,就會告訴系統調用 movie.component.ts 文件中包含的 callMovieService() 方法。
  • {{ this.mv_Title }}, {{ this.mv_Plot }}, {{ this.mv_Actors }}, {{ this.mv_Director }}, {{ this.mv_Rated }}, {{ this.mv_Released }} :顯示從 callMovieService() -> this._sharedService.findMovie(this.id_movie) 執行的服務調用的結果。

將 weather.component.html 文件的內容替換為清單 13 中的代碼。

清單 13. weather.component.html

<h2>Yahoo! Weather </h2>
<div class="col-md-8 col-md-offset-2">
 <div class="form-group">
  <input type="text" [(ngModel)]="id_city" class="form-control" placeholder="Enter City name ..."><br>
  <input type="text" [(ngModel)]="id_state" class="form-control" placeholder="Enter State. Example CA for California ..."><br>
  <button type="button" class="btn btn-primary" (click)="callWeatherService()">Submit</button>
  <br><br><br>
  <br>
  <p class="well lead">
    <i>City, State, Country :</i> {{ this.op_city }} {{ this.op_region }} {{ this.op_country }} <br>
    <i>Current Condition :</i> {{ this.op_text }} <br>
    <i>Current Temperature :</i> {{ this.op_temp }} <br>
  </p>
  <p class="text-info">Total # of all the service requests including Weather, Movie, and Currency is :
    <span class="badge">{{this._sharedService.totReqsMade}}</span>
  </p>
 </div>
</div>

最后,將 currency.component.html 文件的內容替換為清單 14 中的內容。

清單 14. currency.component.html

<h2>Currency Exchange Rates</h2>
<div class="col-md-8 col-md-offset-2">
 <div class="form-group">
  <input type="text" [(ngModel)]="id_currency" (change)="callCurrencyService()" class="form-control" placeholder="Enter Currency Symbol. Example: GBP(,AUD,INR)...">
  <br><br>
  <h3>Rate Details</h3>
  <br>
  <p class="well lead">Exchange rate relative to Euro in a JSON format: : {{ this.my_result }} </p>
  <p class="text-info">Total # of all the service requests including Weather, Movie, and Currency is :
    <span class="badge">{{this._sharedService.totReqsMade}}</span>
  </p>
 </div>
</div>

現在,如果各部分均按預期運行,應用程序可接受瀏覽器中的用戶輸入。

運行應用程序并改進 UI

現在運行應用程序,輸入一些值,然后查看結果。例如,單擊 Weather 鏈接,輸入 San Francisco 來查看該城市的天氣條件:

使用 Angular 2 實現單頁應用程序

測試您的新 Angular 技能

目前,您的應用程序的輸入字段沒有實現驗證或錯誤處理。您可以自行嘗試添加這些功能。提示:在服務中添加方法 validateMovie(movie-name)validateCurrency(currency-name)validateCity(city-name)validateState(state-name) 。然后從相應的組件調用這些方法。

一切正常,但還可以讓 UI 更具吸引力。改進 GUI 的一種方法是使用 Bootstrap 。( Angular 2 資料 是理想的選擇,但截至編寫本文時,它尚未正式發布)。

轉到 Bootstrap 入門頁面 ,將該頁的以下兩行復制到剪貼板:

<!-- Latest compiled and minified CSS -->
<link rel="stylesheet"  integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">

打開 index.html 文件,將剛復制的語句粘貼到第 8 行下。

清單 15. index.html

<!doctype html>
<html>

<head>
  <meta charset="utf-8">
  <title>DwNg2App</title>
  <base href="/">

  <!-- Latest compiled and minified CSS -->
  <link rel="stylesheet"  integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
    crossorigin="anonymous">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" type="image/x-icon" href="favicon.ico">
</head>

<body>
  <app-root>Loading...</app-root>
</body>

</html>

現在該應用程序在瀏覽器中看起來更加美觀,擁有更容易理解的樣式和菜單按鈕,而不是 Weather、Movie Details 和 Currency Rates 鏈接:

使用 Angular 2 實現單頁應用程序

嵌套的 index.html

現在花一分鐘時間體會一下您剛完成的應用程序為什么被稱為 SPA。

當 Angular 應用程序啟動時,服務器將 index.html 文件發送到瀏覽器,而且 index.html 是瀏覽器顯示的唯一文件。Angular 對該頁面執行的任何操作都會插入到此視圖中:

使用 Angular 2 實現單頁應用程序

index.html 末尾的 <app-root> 選擇器被 app.component.html 的內容所取代。app.component.html 包含兩個選擇器: <app-menu> 和 <router-outlet> 。 <app-menu> 選擇器中填入 menu.component.html 的內容, <router-outlet> 依據菜單選項而動態填充 — 也就是說,填入 weather.component.html、currency.component.html 或 movie.component.html 的內容。

除了 Angular 保留選擇器 <router-outlet></router-outlet> 外,所有選擇器都是靜態的。此選擇器在運行時期間依據路由器值而填充。僅顯示 index.html,您編碼的其他所有 HTML 文件都嵌套在 index.html 文件內。

模擬服務器

您的 Angular 項目已成功在開發計算機中運行。如果您能訪問遠程沙箱服務器,那么可以將代碼遷移到那里,看看應用程序在被用戶運行時的行為。(否則,可以跳到教程的部分。)

確保 Node.js 和 Angular CLI 已安裝在遠程沙箱中。壓縮本地項目文件夾中的所有內容,node_modules 目錄及其內容除外。將壓縮后的項目文件復制到沙箱服務器并解壓。轉到包含 package.json 的服務器目錄并運行 npm install 命令。package.json 文件使 npm install 命令能轉到 NPM 公共存儲庫,安裝所有需要的包版本。運行此命令也會自動在服務器上創建 node_modules 目錄和它的內容。

運行 ng serve 命令,以便在沙箱服務器中啟動該應用程序,就像在開發計算機中所做的一樣。按下 Ctrl-C 停止該進程。同樣地,如果您想了解 ng serve 的其他選項,可以運行 ng help 命令。

使用 ng serve --port sandbox-port# --host sandbox-hostname 命令運行該應用程序。

現在 Angular 應用程序可在 URL http:// sandbox-hostname : sandbox-port# 上訪問。在開發計算機瀏覽器中運行該 URL 上的應用程序時,在沙箱服務器上按下 Ctrl-C 停止服務器 Angular 進程。請注意,整個應用程序都在開發計算機的瀏覽器中運行,盡管服務器 Angular 進程已關閉。這會告訴您,SPA 技術正在運行。該應用程序加載到瀏覽器中后,除非應用程序需要新數據,否則控制權絕不會轉交給服務器。

在 SPA 領域,瀏覽器就是新型的服務器。如果 10 個用戶在運行該應用程序,就會有 10 個瀏覽器處理該負載。如果 1,000 個用戶在運行該應用程序,則有 1,000 個瀏覽器處理該負載。整個 Angular 應用程序都在瀏覽器的控制之下,除了在服務器中運行的任何邏輯,比如身份驗證和數據庫操作。

更高的性能和服務器壓力的減輕,最終會提高用戶體驗和用戶滿意度 — 這是任何企業獲得成功的關鍵。

“ 在 SPA 領域,瀏覽器就是新型的服務器。 ”

結束語

您學習了如何使用 Angular 2 編寫一個 SPA,并在開發計算機和沙箱服務器中運行它。對于生產需求,請與您的 IT 部門協商。大多數生產應用程序擁有的一項主要功能是身份驗證和授權,應在服務器中運行該功能(主要出于安全原因)。您可能需要一個專用服務器來處理這些操作。對于該服務器,可以使用 Node.js,在 Angular 2 在前端運行時,它可以充當在后端運行的服務器。(Node 和 Angular 都起源于 Google,所以它們能有效地協同運行。)

您可以考慮的用來提高應用程序性能的其他技術包括:

  • 捆綁:該進程將您的許多程序組合到一個文件中。
  • 微型化:壓縮捆綁的文件,以便盡可能地減小項目大小。
  • 提前 (AoT) 編譯:服務器復雜在構建過程中提前編譯,而不是瀏覽器在運行時期間執行即時 (JIT) 編譯。

 

 

來自:http://www.ibm.com/developerworks/cn/web/wa-implement-a-single-page-application-with-angular2/index.html?ca=drs-

 

 本文由用戶 qtuy9671 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!