原文地址: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数据,不完整版,很是抱歉.