Angular 2 结合 ASP.NET Core Web API – 构建一个简单的笔记本应用 – 第一部分






4.67/5 (5投票s)
逐步构建一个运行在 IIS 和 ASP.NET Core 上的 Angular 2 应用。该项目还包含实时示例。这是构建 Angular 2 笔记本 WebApp 的第一部分。
本文介绍了创建 Angular2 应用的分步方法,该应用将消耗 ASP.NET Core REST WebAPI。它延续了之前的文章 使用 MongoDB 结合 ASP.NET Core WebAPI,并对 Angular 2 框架进行了初步介绍。
这是第一部分,本系列的范围是逐步展示如何构建
一个可以轻松存储想法的 Web 应用,添加文本笔记,无论是从桌面还是移动设备,具有以下特点:运行速度快,即时保存你写的内容,并且具有相当的可靠性和安全性。
涵盖的主题
- Angular 2 模块
- Angular 2 组件
- Angular 2 依赖注入
- Angular 2 生命周期
- Angular 2 服务和 Observable
- 创建一个连接到 ASP.NET Core WebApi 项目的 Angular 2 应用
为什么选择 Angular 2?
Angular2 是一个框架,开箱即用地提供了大量功能。它拥有路由、WebApi 调用、依赖管理等库。Angular2 还采用了 TypeScript。TypeScript 是 JavaScript 的超集。它与 Visual Studio 集成得非常好,在您输入代码时提供建议,并在您输入代码时发现错误。
新版本的 Angular 提供了更一致、更简洁的开发体验。 它也更快,开箱即用地提供服务器端渲染,跨平台,支持旧版浏览器等。
使用 Plunker 进行实时代码演示
即使 Angular 2 框架有其优势,但与 ASP.NET Core 结合的解决方案一开始似乎很困难。为了让事情更轻松,每个部分都附带一个实时的 Angular 2 示例。这些示例从简单的“hello world
”类型演变而来,到从 WebAPI 读取数据。您可以在阅读文章时查看它们,或者从此摘要中快速访问它们
- 预览 A – Angular 2 – 入门应用 这是一个使用 Angular 2 的“Hello world”应用程序。
- 预览 B – Angular 2 – 第一个组件 这个组件创建了第一个 Angular2 组件,显示了几个笔记。
- 预览 C – Angular 2 – 可注入 Angular 2 中的依赖注入。
- 预览 D – Angular 2 – 连接到 REST WebAPI 这个示例检索并显示来自 WebAPI 的笔记。
轻松访问代码
博客从一个预配置的 ASP.NET Core 解决方案开始,最终到一个连接到 WebAPI 的应用程序。您可以使用下面的链接访问这两个项目
- 下载入门项目 (GitHub)。
- 下载包含此处展示的所有代码示例的项目 (GitHub)。
安装说明
以下是本地安装所需的所有内容
- Visual Studio Community 2015 然后 Visual Studio 2015 Update 3 和 .NET Core 1.0.1 – VS 2015 Tooling Preview 2。Update 3 更好,因为它修复了一些 NPM 问题,并且是 TypeScript 2.0 的先决条件。
- NPM
开始使用 Angular 2 和 TypeScript
开始学习 Angular 2 和 TypeScript 的最佳方法是克隆一个应用程序启动器,这是一个极简的 Angular 2 应用,它具有 Angular 2、TypeScript 和模块加载器的完整设置。
首先,一旦安装了上面介绍的所有项目,我们需要确保 node 和 npm 版本正确。Angular 2 使用 node v4.x.x 或更高版本以及 npm 3.x.x 或更高版本运行。
为了能够检查版本,我打开了一个命令提示符并运行了
c:\windows\system32>npm --version
3.10.8
c:\windows\system32>node --version
v6.9.1
我的本地设置满足最低要求。如果您在 npm
版本方面遇到问题,请参阅 下一篇文章。
一旦这些版本正确,我们就可以继续从 Github 克隆 初始项目(或者更简单地 下载它)。
在打开初始解决方案之前的最后一件事是安装 Gulp。在命令提示符中,在项目文件夹中运行此命令
npm install gulp --save-dev
此命令将本地安装 Gulp。入门项目具有所有现成的配置,以及一个完全配置的 Gulp 文件。
在 Visual Studio 中打开解决方案。根据互联网连接的速度,本地下载所有必需的包可能需要一段时间。一旦完成,可能在几分钟后,构建解决方案并运行它。
以下是为使我们能够运行 Angular 2 和 ASP.NET Core MVC 而添加或更新的文件
// Angular 2 source code details
- app folder # Angular2 source folder
- /css & /js # Folders for CSS & additional Java script files
- note.app.module.ts # The Angular module & sample component
// ASP.NET Core - Updated files
- Controllers/HomeController.cs # Default ASP.NET MVC Controller, at the beginning with no change
- Views/Home/Index.cshtml # Default ASP.NET MVC View, loads the Angular2 module
- Startup.cs # Make the static files servable
// TypeScript configuration
- gulpfile.js # Gulp configuration file for automating the deployment flow
- hosting.js # Configuration file added to run the application on a specific port
- package.json # NPM could identify the project as well as handle the project's dependencies.
- systemjs.config.js # Allows to configure SystemJS to load modules compiled using the TypeScript compiler.
- tsconfig.json # TypeScript compiler configuration
- typings.json # TypesScript declaration file
模块、引导、组件
- 在 Angular 1 中,我们使用
ng-app
指令将 Angular 指向应用程序的起始点。在 Angular 2 中,我们使用引导程序。Angular 2 是平台无关的。我们可以在浏览器中运行它,但也可以在 Web Worker、服务器上运行,并可能使用不同的引导程序在移动设备上原生运行。platformBrowserDynamic().bootstrapModule(AppModule);
- Angular 2 模块和新的
NgModule
装饰器允许我们在一个地方声明应用程序的所有依赖项和组件,而无需像以前那样按组件进行声明。在这里,我们指示 App 组件应该首先加载。@NgModule({ imports: [BrowserModule], declarations: [App], bootstrap: [App] }) export class AppModule { }
- 组件是可重用的 UI 部分,由自定义 HTML 元素显示。它是自包含的,至少由一段称为模板的 HTML 代码、封装该模板可用数据和交互的类,以及也称为选择器的上述 HTML 元素组成。
@Component({ selector: 'notes-app', template: `<div> <h2>NotebookApp with {{name}}</h2> </div>` }) export class App { name: string; constructor() { this.name = 'Angular 2'; } }
- 要显示,我们在 ASP.NET Core 视图中包含新的 HTML 标签
<notes-app></notes-app> <script> System.import('dist/note.app.module') .catch(function (err) { console.error(err); }); </script>
您可以通过运行解决方案来查看结果,或者使用 Plunker 中的第一个示例:预览 A – Angular 2 – 入门应用。
添加第一个组件:列出笔记
我们要做的第一件事不需要服务,现在还不需要。我们将创建第一个组件来显示笔记列表,并开始模拟这些数据。
最好先定义我们问题空间的域模型,在本例中是 NoteItem
。我们将利用 TypeScript 接口并在 noteModel.ts 文件中创建一个 NoteItem
。为简单起见,我们将暂时将所有字段(包括 date
)都设为 string
。组件名称为:NotesComponent
export interface NoteItem {
Id: string,
Body: string,
UpdatedOn: string,
CreatedOn: string,
UserId: number
}
要遍历笔记列表,我们将使用 *ngFor
,这是一个重复器指令。代码片段将显示如下
<ul>
<li *ngFor="let note of noteItems">
{{note.Body}}
</li>
</ul>
现在,我们更新 NotesComponent
组件并显示数据
import { Component } from '@angular/core'
import { NoteItem } from './note.model'
@Component({
selector: 'notes-app',
template: `
<ul>
<li *ngFor="let note of noteItems">
{{note.Body}}
</li>
</ul>
`
})
export class NotesComponent {
noteItems: NoteItem[] = [
{Id:'1', Body: 'First note', UpdatedOn: '2016-11-21 10:20:23',
CreatedOn: '2016-11-21 10:20:23', UserId: 1},
{Id:'2', Body: 'Second note with more details', UpdatedOn: '2016-11-21 10:20:23',
CreatedOn: '2016-11-21 10:20:23', UserId: 1},
{Id:'3', Body: 'Third note, and the last sample', UpdatedOn: '2016-11-21 10:20:23',
CreatedOn: '2016-11-21 10:20:23', UserId: 1},
];
}
您可以在 Plunker 中看到相同的 Angular 2 代码:预览 B – Angular 2 – 第一个组件。
依赖注入和通用设置
为了更容易地呈现 Angular 2 中的依赖注入 (DI),让我们使用一个通用的设置类,该类需要被其他组件访问。它的构建方式可以进一步扩展(例如:从配置文件读取),但更简单的模型有助于我们更好地呈现 Angular 2 中的依赖注入。
让我们从一个简单的类开始
export class Configuration {
public ApiServer: string = "https://:6001/";
public ApiUrl: string = "api/notes";
public ServerWithApiUrl: string = this.ApiServer + this.ApiUrl;
}
为了通过 DI 使其可供其他组件访问,我们进行两项更改
- 使该类可注入
import { Injectable } from '@angular/core'; @Injectable() export class Configuration { public ApiServer: string = "https://:6001/"; public ApiUrl: string = "api/notes"; public ServerWithApiUrl: string = this.ApiServer + this.ApiUrl; }
- 然后,在模块配置中将其作为提供者可用
@NgModule({ imports: [BrowserModule], declarations: [NotesComponent], providers: [Configuration], bootstrap: [NotesComponent] }
这些更新允许我们通过构造函数将 Settings 注入到我们的组件中。
export class NotesComponent { constructor(private _dataService: NoteService) {
在 Plunker 中实时查看此内容:预览 C – Angular 2 – 可注入。
使用 Angular 2 组件生命周期
当组件创建时,会调用其构造函数,然后初始化我们的组件。如果我们依赖于其他组件的属性或数据,那么我们就需要等待其他组件先初始化。为了能够做到这一点,我们使用 ngOnInit
生命周期。这将允许我们在初始化此服务时调用 WebApi。
import { Component, OnInit } from '@angular/core';
...
export class NotesComponent implements OnInit {
ngOnInit() {
// access the WebAPI service
}
}
创建 Angular 2 服务
Angular 2 服务只是一个封装功能的 ES6 类。它被应用程序的其余部分使用,并被称为服务。
在下面的示例中,我们创建了一个使用原生 Angular 2 http 服务的类,它允许我们接收 json 详细信息。该类还被标记为可注入,以便更容易访问和使用。
import { Injectable } from "@angular/core";
import { Http } from "@angular/http";
import "rxjs/add/operator/map";
import { Observable } from "rxjs/Observable";
import { NoteItem } from "../../models/note/noteModel";
import { Configuration } from "../../app.constants";
@Injectable()
export class NoteService {
constructor(private _http: Http, private _configuration: Configuration) {
}
public getAll = (): Observable<NoteItem[]> => {
return this._http.get(this._configuration.ServerWithApiUrl)
.map(data => data.json());
};
我们在上面的代码片段中使用了更多术语,这里有一些细节
- Angular 2 http 客户端服务提供了进行 HTTP 请求的支持,并附带了与 HTTP 动词(如
get
、post
、put
等)对应的所有方法。 - Observable 是 Angular 2 中使用的异步模式。Observable 的概念来源于观察者设计模式,它是一个对象,当发生某事时会通知感兴趣的观察者集合。在 RxJs 中,它已被泛化为管理数据或事件序列,使其能够与其他 observable 组合,并提供大量称为操作符的实用函数。
map
将序列中的项转换为我们应用程序的域模型 – 在本例中是noteItems
。
要使用此模式,我们应该订阅 observable。我们通过 .subscribe
到 Observable<noteitem[]>
来做到这一点。一旦异步接收到详细信息,我们就填充局部变量 myItems
。
export class NotesComponent implements OnInit {
public myItems: NoteItem[];
constructor(private _dataService: NoteService) {
}
ngOnInit() {
this._dataService
.getAll()
.subscribe((data: NoteItem[]) => this.myItems = data,
() => console.log("getAllItems() complete from init"));
}
仅进行检索(GET
),我们可以通过读取 JSON 文件来模拟外部服务。在 Plunker 中查看 Angular 2 服务的概念:预览 D – Angular 2 服务连接到 REST WebAPI。
使用 Observable 进行错误处理
第一级错误处理应发生在服务级别。在此较低级别可以处理与 HTTP 请求相关的问题。在此简单应用程序中,我们将仅记录错误,并将其转换为应用程序级别的错误
export class NotesComponent implements OnInit {
public myItems: NoteItem[];
constructor(private _dataService: NoteService) {
}
ngOnInit() {
this._dataService
.getAll()
.subscribe((data: NoteItem[]) => this.myItems = data)
.catch(handleException)
() => console.log("getAllItems() complete from init"));
}
}
function handleException(error: any) {
// log error
let errorMsg = error.message || `Problem accessing the data!`
console.error(errorMsg);
// throw an application level error
return Observable.throw(errorMsg);
让我们添加一个内部 REST 服务,使用 ASP.NET Core WebAPI 控制器
在访问另一个 ASP.NET Core 项目之前,我们可以通过在我们的 ASP.NET 项目中添加一个新控制器来轻松模拟。这有助于我们使用单个解决方案更轻松地运行和测试。
using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
namespace NotebookAppWeb.Controllers
{
[Produces("application/json")]
[Route("api/[controller]")]
public class NotesController : Controller
{
private class NoteItem
{
public string Id;
public string Body;
public string UpdatedOn;
public string CreatedOn;
public int UserId;
}
// GET: api/values
[HttpGet]
public string Get()
{
NoteItem[] arrayOfNotes = new NoteItem[]
{ new NoteItem() { Id = "1",
Body = "Hello note !",
UpdatedOn = "2016-11-16 10:50:23",
CreatedOn = "2016-11-16 10:50:23", UserId = 1 },
new NoteItem() { Id = "2",
Body = "Hello 2 should come after",
UpdatedOn = "2016-11-16 10:50:23",
CreatedOn = "2016-11-16 10:50:23", UserId = 2 },
new NoteItem() { Id = "3",
Body = "Hello 3 should come latest",
UpdatedOn = "2016-11-17 10:50:23",
CreatedOn = "2016-11-17 10:50:23", UserId = 3 }};
return JsonConvert.SerializeObject(arrayOfNotes);
}
}
整合
我们现在可以创建一个应用程序,该应用程序连接上述所有概念并模拟一个基本的笔记本应用程序。此应用程序连接到本地控制器作为 REST 服务(Web API 控制器),然后显示接收到的笔记。
连接到 ASP.NET Core WebAPI 和 MongoDB 项目
连接到 ASP.NET Core WebAPI 项目。打开 Github 页面 – https://github.com/fpetru/WebApiMongoDB,在项目说明中,您可以找到如何运行该项目。
一旦设置完成,更改配置以指向此 REST 服务(而不是使用本地控制器)。运行项目。
下一步
这将在下一部分继续,介绍笔记的所有操作。然后,我们将能够添加、编辑或删除笔记。