原文地址:using typescript with angularjs and web api 版權歸其做者全部.javascript
在這篇文章中我將向你們展現如何使用TypeScript,Angular Js 和Asp.net Web API 來構建一個基本的實現CRUD功能的Web應用程序. Typescript提供了一系列的功能來方便程序員構造和組織更具備可維護性的Js應用程序.同時也能夠集成現有的第三方庫文件,你能夠在接下來的Demo中看到這項特性.這個基本的HTML應用將藉助Asp.net Web API實現增,刪,查,改功能.css
主要特色以下:html
在這個例子中,我使用VS 2012和Sublime Text來編寫代碼.你也能夠選擇本身喜歡的文本編輯器,若是你須要語法高亮或者代碼自動補全功能,你須要單獨下載程序包.java
對於VS插件和windows下的可執行文件(tsc.exe,安裝包的一部分),請訪問: http://www.typescriptlang.org/#Download. 若是你想選擇一個不一樣的編輯器,(例如:Sublime Text或者Vim),請從這裏尋找幫助 : http://aka.ms/qwe1qu. TypeScript編譯程序是必不可少的工具.
程序員
服務端經過Asp.net Web API實現(.Net平臺下構建Http服務的實現,我我的很喜歡),固然你也能夠選擇本身喜歡的技術.angularjs
使用Asp.net Web API 提供Http服務.web
對於這個例子,我處理的模型由一個單一實體構成----Product.typescript
//Model
public abstract class Entity { public Guid Id { get; set; } } public class Product : Entity { public string Name { get; set; } public decimal Price { get; set; } }
咱們須要一個持久化機制來存儲這些實體.我選擇使用倉儲模式把這些內容存在內存裏.請根據須要,隨意替換成適合你的東西(例如:Entity Framework或者Nhibernate.)bootstrap
// IRepository
public interface IRepository<TEntity> where TEntity : Entity { TEntity Add(TEntity entity); TEntity Delete(Guid id); TEntity Get(Guid id); TEntity Update(TEntity entity); IQueryable<TEntity> Items { get; } }
//InMemoryRepository public class InMemoryRepository<TEntity> : IRepository<TEntity> where TEntity : Entity { private readonly ConcurrentDictionary<Guid, TEntity> _concurrentDictionary = new ConcurrentDictionary<Guid, TEntity>(); public TEntity Add(TEntity entity) { if (entity == null) { //we dont want to store nulls in our collection throw new ArgumentNullException("entity"); } if (entity.Id == Guid.Empty) { //we assume no Guid collisions will occur entity.Id = Guid.NewGuid(); } if (_concurrentDictionary.ContainsKey(entity.Id)) { return null; } bool result = _concurrentDictionary.TryAdd(entity.Id, entity); if (result == false) { return null; } return entity; } public TEntity Delete(Guid id) { TEntity removed; if (!_concurrentDictionary.ContainsKey(id)) { return null; } bool result = _concurrentDictionary.TryRemove(id, out removed); if (!result) { return null; } return removed; } public TEntity Get(Guid id) { if (!_concurrentDictionary.ContainsKey(id)) { return null; } TEntity entity; bool result = _concurrentDictionary.TryGetValue(id, out entity); if (!result) { return null; } return entity; } public TEntity Update(TEntity entity) { if (entity == null) { throw new ArgumentNullException("entity"); } if (!_concurrentDictionary.ContainsKey(entity.Id)) { return null; } _concurrentDictionary[entity.Id] = entity; return entity; } public IQueryable<TEntity> Items { get { return _concurrentDictionary.Values.AsQueryable(); } } }
一旦咱們在這裏完成了對象持久化,咱們就能夠創造一個Http服務,來提供一些基本的操做.我使用的是Asp.net Web API,因此我還要再建立一個 Controller.windows
//Product HTTP Service public class ProductsController : ApiController { public static IRepository<Product> ProductRepository = new InMemoryRepository<Product>(); public IEnumerable<Product> Get() { return ProductRepository.Items.ToArray(); } public Product Get(Guid id) { Product entity = ProductRepository.Get(id); if (entity == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } return entity; } public HttpResponseMessage Post(Product value) { var result = ProductRepository.Add(value); if (result == null) { // the entity with this key already exists throw new HttpResponseException(HttpStatusCode.Conflict); } var response = Request.CreateResponse<Product>(HttpStatusCode.Created, value); string uri = Url.Link("DefaultApi", new { id = value.Id }); response.Headers.Location = new Uri(uri); return response; } public HttpResponseMessage Put(Guid id, Product value) { value.Id = id; var result = ProductRepository.Update(value); if (result == null) { // entity does not exist throw new HttpResponseException(HttpStatusCode.NotFound); } return Request.CreateResponse(HttpStatusCode.NoContent); } public HttpResponseMessage Delete(Guid id) { var result = ProductRepository.Delete(id); if (result == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } return Request.CreateResponse(HttpStatusCode.NoContent); } }
咱們努力去實現標準的Http,所以附加邏輯來處理 Response.在這個步驟結束後,咱們將會有一個功能完整的,(實現了CRUD)Http服務.
開始使用Angular Js 和 TypeScript
服務端建立好之後,咱們接着開始建立真正的網站部分.它將徹底由Html/CSS/Js(TypeScript將會編譯成Js)構成.我選擇的初始模版像這樣:
<!DOCTYPE html> <html ng-app> <head> <title>Product list</title> <link rel="stylesheet" href="Content/bootstrap.css"/> <script type="text/javascript" src="Scripts/bootstrap.js"></script> <script type="text/javascript" src="Scripts/angular.js"></script> <script type="text/javascript" src="Scripts/Controllers/ProductsController.js"></script> </head> <body> <div ng-controller="Products.Controller"> </div> </body> </html>
請注意:ProductsController.js文件是從名爲ProductsController.ts的TypeScript源代碼經過tsc.exe編譯獲得的.(我使用了命令行來執行這個編譯步驟.).讓咱們爲這個頁面建立一個Angular Js的Controller.
//ProductsController.ts module Products { export interface Scope { greetingText: string; } export class Controller { constructor ($scope: Scope) { $scope.greetingText = "Hello from TypeScript + AngularJS"; } } }
Produscts Module將被編譯成一個 Js的命名空間,咱們的Controller將被轉譯爲 Products.Controller供程序使用.如今咱們能夠在頁面中綁定一個歡迎致辭.請注意:結合TypeScript的特色,咱們以Scope接口的形式定義了$scope對象.Typescript也容許咱們使用 any 這種類型來定義,但就我的而言,我更傾向於使用更加嚴格的定義方式,由於他能夠幫助你在編譯時捕獲錯誤信息.(VS2012甚至會在你編寫的錯誤代碼下面加入紅色下劃線,這真是太棒了.)如今,咱們須要編譯ProductsController.ts,在HTML中引用ProductsController.js,而且修改視圖(view)來顯示信息.
<div ng-controller="Products.Controller"> <p>{{greetingText}}</p> </div>
如今AngularJs Controller已經完成,讓咱們繼續添加更多的功能.
建立Model模塊
咱們將建立一個Model模塊,它會包含一個Product類,做爲DTO對象(序列化爲JSON)與Http的服務端進行交互.
module Model {
export class Product {
Id: string;
Name: string;
Price: number;
}
}
這個簡單的模塊包含了一個類型的定義,將會在頁面的contrller中使用.
環境聲明( ambient declarations.)
爲了使用AngularJs提供的Ajax功能,咱們將$Http服務傳入到Controller的構造器中:
class Controller { private httpService: any; constructor ($scope: Scope, $http: any) { this.httpService = $http; //... } //... }
因爲咱們將$Http定義成了any的類型,因此編譯器不會幫助咱們在編譯時發現潛在的錯誤.爲了解決這個問題,咱們使用環境聲明(譯者注:元數據??).環境聲明用來告訴編譯器將要被引入的元素有特殊的意義(在這裏是指Angular Js)而且不會被擴展.換而言之就是第三方類庫的接口.聲明源文件(.d.ts擴展名)被限制爲只能包含環境聲明.下面的angular.d.ts文件定義了兩個接口,被咱們用來調用Http服務.
declare module Angular { export interface HttpPromise { success(callback: Function) : HttpPromise; error(callback: Function) : HttpPromise; } export interface Http { get(url: string): HttpPromise; post(url: string, data: any): HttpPromise; delete(url: string): HttpPromise; } }
declare關鍵字是可選的,他會被隱式聲明在全部的 .d.ts 文件中.
TypeScript 和 Angular Js
爲了讓編譯器知道新增的兩個模塊(Model和Angular),咱們須要添加兩個引用信息到ProductsController.ts中.同時咱們想要定義 Scope接口來包含視圖使用的全部屬性和方法.
頁面將包含一個新增Product的表單(name,price文本框和一個按鈕)同時展現一個包含全部Product的列表,其中的每一個記錄均可以被單獨的刪除.爲了使演示變的簡單,我忽略了更新記錄的功能,可是隻要咱們實現了相應的操做,更新實現起來也是很簡單的.
Scope接口看起來以下:
/// <reference path='angular.d.ts' /> /// <reference path='model.ts' /> module Products { export interface Scope { newProductName: string; newProductPrice: number; products: Model.Product[]; addNewProduct: Function; deleteProduct: Function; } // ... }
只要有product被新增或者被刪除,咱們就從服務端從新獲取全部的product並刷新列表.因爲先前咱們所作的工做,如今咱們可使用強類型的 Angular.Http和Angular.HttpPromise接口.
controller將包含私有方法與咱們的服務端進行通訊(getAllProducts, addProduct and deleteProduct).
export class Controller { private httpService: any; constructor ($scope: Scope, $http: any) { this.httpService = $http; this.refreshProducts($scope); var controller = this; $scope.addNewProduct = function () { var newProduct = new Model.Product(); newProduct.Name = $scope.newProductName; newProduct.Price = $scope.newProductPrice; controller.addProduct(newProduct, function () { controller.getAllProducts(function (data) { $scope.products = data; }); }); }; $scope.deleteProduct = function (productId) { controller.deleteProduct(productId, function () { controller.getAllProducts(function (data) { $scope.products = data; }); }); } } getAllProducts(successCallback: Function): void{ this.httpService.get('/api/products').success(function (data, status) { successCallback(data); }); } addProduct(product: Model.Product, successCallback: Function): void { this.httpService.post('/api/products', product).success(function () { successCallback(); }); } deleteProduct(productId: string, successCallback: Function): void { this.httpService.delete('/api/products/'+productId).success(function () { successCallback(); }); } refreshProducts(scope: Scope) { this.getAllProducts(function (data) { scope.products = data; }); } }
很是棒的一點就是,咱們不須要人爲的引入任何定製的序列化邏輯.當經過$http服務取回數據後,咱們能夠把他們當成強類型的Product集合進行操做.add操做也是同樣----咱們僅僅傳入一個Product,他將被自動序列化並最終在服務端被解析.
建立視圖(View)
最後一步,咱們須要建立一個集合新controller特性的視圖.我將使用bootstrap來使他變簡單一些.
<!DOCTYPE html> <html ng-app> <head> <title>Product list</title> <link rel="stylesheet" href="Content/bootstrap.css" /> <script type="text/javascript" src="Scripts/angular.js"></script> <script type="text/javascript" src="Scripts/Controllers/model.js"></script> <script type="text/javascript" src="Scripts/Controllers/productsController.js"></script> </head> <body> <div ng-controller="Products.Controller"> <form class="form-horizontal" ng-submit="addNewProduct()"> <input type="text" ng-model="newProductName" size="30" placeholder="product name"> <input type="text" ng-model="newProductPrice" size="5" placeholder="product price"> <button class="btn" type="submit" value="add"> <i class="icon-plus"></i> </button> </form> <table class="table table-striped table-hover" style="width: 500px;"> <thead> <tr> <th>Name</th> <th>Price</th> <th></th> </tr> </thead> <tbody> <tr ng-repeat="product in products"> <td>{{product.Name}}</td> <td>${{product.Price}}</td> <td> <button class="btn-small" ng-click="deleteProduct(product.Id)"> <i class="icon-trash"></i> </button> </td> </tr> </tbody> </table> </div> </body> </html>
最終的結果就像這樣:
如今咱們的頁面應該實現了應有的功能,而且能夠與Http 服務端按照預期的狀況進行通訊工做了.
最後:Wordpress和Android應用可能會由於一些緣由影響post數據,不完整版,很是抱歉.