65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 MVC 6、Web API 2、ASPNET 5 和 Gulp 的 Angular JS 应用程序 – 第 1 部分

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (51投票s)

2015年5月17日

CPOL

7分钟阅读

viewsIcon

152975

downloadIcon

3870

使用 MVC 6 中的 Web API 构建 Angular JS 应用程序。

引言

在本文中,我将解释如何为 Angular JS 中的单页应用程序 (SPA) 设置 ASP.NET 5 项目。我已使用 MVC 6 Web API 显示一些静态数据,最终我将使用数据库通过 Web API 存储和显示数据。我还会展示如何使用 gulp、bower 和 npm 自动复制前端 JavaScript 文件。在后续模块中,我将描述如何合并和最小化前端 JavaScript 文件。

在我的演示中,我使用的是 Visual Studio 2015 RC,这是用于跨平台开发的下一代 Visual Studio 的最新预发布版本。我还会使用 Entity Framework 7DNX (.NET 执行环境) 来迁移数据库并在不同的环境中运行网站。

背景

由于近年来前端工具和技术的进步,单页应用程序 (SPA) 现在已是众所周知的术语。其中,Angular JS 是构建 SPA 最受欢迎的选择。微软最近发布的 Visual Studio 2015 RC 是开发 SPA 的绝佳平台,可以在其中轻松管理和维护应用程序的前端工具。此外,MVC 6 中的 Web API 2 被用作后端服务,可以高效地支持从前端调用它。

我将全程使用 Visual Studio 2015 RC、MVC 6 Web API2、gulp、bowernpm 来进行前端和后端开发。

创建项目

首先,我打开了 Visual Studio 2015 RC,从开始页面中,我选择了新建项目,然后从可用模板中,我选择了 ASP.NET Web 应用程序。我将项目命名为 BooksAngularApp

点击确定后,会出现模板选择向导,我将从可用的 ASP.NET 5 预览模板中选择网站。我选择此模板的原因是它为我们提供了一个有用的框架,而不是从空模板开始。

需要注意的是,网站默认带有 gulp 任务运行器,我将使用它来合并和最小化前端 JavaScript 和 CSS 文件。

点击确定后,我创建了以下项目,项目结构和预览页面如下所示

任务运行器

如果我们查看解决方案资源管理器中的项目结构,可以看到 bower 文件夹中预安装的包,例如 bootstrapjqueryhammer 等。此外,我们还可以看到 npm 文件夹,其中已安装 gulprimraf 作为前端工具,用于管理前端脚本,特别是 JavaScript 和 CSS 文件。

现在让我们看一下 gulpfile.js,它将用于管理我们的前端代码或脚本。

var gulp = require("gulp"),
  rimraf = require("rimraf"),
  fs = require("fs");

eval("var project = " + fs.readFileSync("./project.json"));

var paths = {
  bower: "./bower_components/",
  lib: "./" + project.webroot + "/lib/"
};


gulp.task("clean", function (cb) {
  rimraf(paths.lib, cb);

});

gulp.task("copy", ["clean"], function () {
  var bower = {
    "bootstrap": "bootstrap/dist/**/*.{js,map,css,ttf,svg,woff,eot}",
    "bootstrap-touch-carousel": "bootstrap-touch-carousel/dist/**/*.{js,css}",
    "hammer.js": "hammer.js/hammer*.{js,map}",
    "jquery": "jquery/jquery*.{js,map}",
    "jquery-validation": "jquery-validation/jquery.validate.js",
    "jquery-validation-unobtrusive": 
            "jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"
  }

  for (var destinationDir in bower) {
    gulp.src(paths.bower + bower[destinationDir])
      .pipe(gulp.dest(paths.lib + destinationDir));

  }
});

我们可以看到脚本运行一些任务,在开发过程中文件发生任何更改时,可以复制清理 wwwroot 文件夹中的 JavaScript 和 CSS 文件。

让我们看看这些是如何发生的。我从 视图->其他窗口->任务运行器 中打开了任务运行器

我们可以看到 gulpfile.js 中定义的 cleancopy 任务。在我们运行它们中的任何一个之前,我们可以看到一个 lib 文件夹默认包含所有 JavaScript 和 CSS 文件的副本。

我运行了 clean 任务,它清除了 wwwroot 目录中的 lib 文件夹,这意味着 gulp 被用于运行 clean 任务以删除静态文件。

我运行了 copy 任务,lib 文件夹又回到了复制的文件。

安装 Angular 包

对于我们的 SPA,我将使用三个 Angular 包:angularangular-routeangular-resource。目前,默认的 bower.json 文件如下所示

>
{
  "name": "ASP.NET",
  "private": true,
  "dependencies": {
    "bootstrap": "3.0.0",
    "jquery": "1.10.2",
    "jquery-validation": "1.11.1",
    "jquery-validation-unobtrusive": "3.2.2",
    "hammer.js": "2.0.4",
    "bootstrap-touch-carousel": "0.8.0"
  }
}

我已添加以下三个 angular

{
  "name": "ASP.NET",
  "private": true,
  "dependencies": {
    "bootstrap": "3.0.0",
    "jquery": "1.10.2",
    "jquery-validation": "1.11.1",
    "jquery-validation-unobtrusive": "3.2.2",
    "hammer.js": "2.0.4",
    "bootstrap-touch-carousel": "0.8.0",
    "angular": "*",
    "angular-route": "*",
    "angular-resource": "*"
  }
}

添加三行 angular 后,我尚未保存 bower.json 文件,并且在解决方案资源管理器Bower 文件夹中,没有 angular 模块。

保存文件后,几秒钟后我们可以在 bower 文件夹的依赖项列表中看到 angular 组件。

您可能会看到 bower 文件夹如下所示,其中未安装 angular 模块

如果它们未安装,请右键单击 Bower 文件夹并点击恢复包,它将恢复添加到 bower.json 文件中的所有未安装包。

为 Angular 脚本创建 Gulp 任务

现在我已经安装了 angular 包,我们需要更新任务运行器,以便将 angular 文件与其他 bower 组件一起复制到 lib 文件夹。

我已更新 copy 任务,如下所示

gulp.task("copy", ["clean"], function () {
  var bower = {
    "bootstrap": "bootstrap/dist/**/*.{js,map,css,ttf,svg,woff,eot}",
    "bootstrap-touch-carousel": "bootstrap-touch-carousel/dist/**/*.{js,css}",
    "hammer.js": "hammer.js/hammer*.{js,map}",
    "jquery": "jquery/jquery*.{js,map}",
    "jquery-validation": "jquery-validation/jquery.validate.js",
    "jquery-validation-unobtrusive": 
            "jquery-validation-unobtrusive/jquery.validate.unobtrusive.js",
    "angular": "angular/angular*.{js,map}",
    "angular-route": "angular-route/angular-route*.{js,map}",
    "angular-resource": "angular-resource/angular-resource*.{js,map}"
  }

现在我再次运行 copy 任务,可以看到 angular 文件已复制到 wwwrootlib 文件夹中。

我已配置 copycopyapp 任务在应用程序的每次构建后运行,以确保更改被复制。

现在我们已准备好开始使用 SPA BooksAngularApp

创建数据模型和 Web API

对于 SPA,我将使用一个虚构的书店,我们可以在其中查看书籍列表及其详细信息。在此模块中,我将使用一些静态数据作为书籍项目。为此,我需要一个数据模型,用于访问和显示书籍。

我在项目 Models 文件夹中添加了一个名为 Book 的新类,模型如下所示

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.ComponentModel.DataAnnotations;

namespace BooksAngularApp.Models
{
   /// <summary>
   /// The entity class with Book properties
   /// </summary>
    public class Book
    {
        [Key]
        public int Id { get; set; }
        public string Title { get; set; }
        public string Author { get; set; }
        public string Type { get; set; }
        public DateTime DatePublished { get; set; }
        public decimal Price { get; set; }
    }
}

之后,我创建了一个名为 BooksControllerMVC 控制器类,它将作为我们数据访问的 API 控制器

默认情况下,Controller 类如下所示

我已将该类更改为 API 控制器,通过添加...

 [Route("api/[controller]")]

...在类定义之前,这样通过 Route 属性,它将充当 API 控制器

我已添加 HttpGet 方法以显示静态书籍列表,如下所示

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using BooksAngularApp.Models;

namespace BooksAngularApp.Controllers
{
    [Route("api/[controller]")]
    public class BooksController : Controller
    {
        [HttpGet]
        public IEnumerable<Book> Get()
        {
            return new List<Book>
                {
                    new Book {Id=1, Title="Wonders of the Sky", 
                    Author="Martin James", DatePublished=Convert.ToDateTime("1/1/2013"), 
                    Type="Science",Price=23.23m },
                    new Book {Id=2, Title="Secrets of the Mind ", 
                    Author="Allan Sue ", DatePublished=Convert.ToDateTime("2/1/2011"), 
                    Type="Psychology", Price=12.50m },
                    new Book {Id=3, Title="We are Alive", 
                    Author="Dick Smith", DatePublished=Convert.ToDateTime("2/11/2010"), 
                    Type="Science Fiction", Price=21.25m } ,
                    new Book {Id=4, Title="Last day of the world", 
                    Author="Martin James", DatePublished=Convert.ToDateTime("1/1/2013"), 
                    Type="History", Price=10.40m },

                };
        }
    }
}

为了查看我创建的 API 控制器是否正常工作,让我们从浏览器运行 API

我们可以看到数据以 JSON 格式从 API 控制器显示,这意味着我们已准备好使用 API 来显示数据。

编写 Angular 代码

我在应用程序根目录中创建了一个 app 文件夹,我将在此处放置所有与 Angular 相关的模块,例如控制器、服务等。

我添加了 app.js 作为第一个模块,如下所示

(function () {

    'use strict';

    angular.module('booksApp', ['booksServices']);

})();

booksApp Angular 模块将依赖于名为 booksServices 的服务,我将立即定义它。

我在 app 文件夹中创建了一个名为 services 的新文件夹,并添加了 booksServices.js,如下所示

(function () {
    'use strict';
    var booksServices = angular.module('booksServices', ['ngResource']);

    booksServices.factory('Books', ['$resource',
      function ($resource) {
          return $resource('/api/books/', {}, {
              query: { method: 'GET', params: {}, isArray: true }
          });
      }]);

})();

该服务将注入 ngResource 作为依赖项,它最终将调用 API 控制器以从 API 获取数据。

现在我们需要另一个 Angular 模块,我们将其命名为 booksController,我已在 app 文件夹中的 controllers 文件夹中创建,命名为 booksController.js

(function () {
    'use strict';
    angular
    .module('booksApp')
    .controller('booksController',booksController)

    booksController.$inject = ['$scope', 'Books'];
    function booksController($scope, Books)
    {
        $scope.books = Books.query();
    }
})();

在这里我们可以看到,控制器注入了 scope 对象,该对象返回 API 的查询结果。

为应用程序脚本编写任务

我们需要编写任务,将文件复制到 wwwroot 文件夹,就像我们之前对其他 JavaScript 文件所做的那样。

var paths = {
  bower: "./bower_components/",
  lib: "./" + project.webroot + "/lib/",
  app: "./" + project.webroot + "/app/",
  srcapp:  "./app/",
};

gulp.task("cleanappp", function (cb) {
    rimraf(paths.app, cb);

});

gulp.task("copyapp",["cleanappp"], function () {
    var app = {
        "controllers": "controllers/booksController.js",
        "services": "services/booksServices*.js",
        "/": "app.js"
    }

    for (var destinationDir in app) {
        gulp.src(paths.srcapp + app[destinationDir])
          .pipe(gulp.dest(paths.app + destinationDir));
    }
});

我已在 paths 变量中添加了 appsrcapp,以定义应用程序脚本的源和目标。我添加了一个名为 cleanapp 的新任务,它将用于清理 wwwroot 中的 app 文件夹。最后,我添加了另一个名为 copyapp 的任务,它注入了 cleanapp 任务,这意味着在复制应用程序脚本之前,它将首先清理文件夹,然后复制所有脚本文件。

<!DOCTYPE html>
<html ng-app="booksApp">
<head>
    <meta charset="utf-8" />
    <title>List of Books</title>
    <link href="lib/bootstrap/css/bootstrap.css" rel="stylesheet" />
    <link href="lib/bootstrap/css/bootstrap-theme.css" rel="stylesheet" />
    <script src="lib/jquery/jquery.min.js"></script>
    <script src="lib/angular/angular.js"></script>
    <script src="lib/bootstrap/js/bootstrap.js"></script>
    <script src="lib/angular-resource/angular-resource.js"></script>
    <script src="app/app.js"></script>
    <script src="app/controllers/booksController.js"></script>
    <script src="app/services/booksServices.js"></script>
</head>
<body ng-cloak>
    <div ng-controller="booksController">
        <table class="table table-bordered table-striped">
            <thead>
                <tr>
                    <th>Title</th>
                    <th>Author</th>
                    <th>Type</th>
                    <th>Date Published</th>
                    <th>Price</th>
                </tr>
            </thead>
            <tbody>
                <tr ng-repeat="book in books">
                    <td>{{book.Title}}</td>
                    <td>{{book.Author}}</td>
                    <td>{{book.Type}}</td>
                    <td>{{book.DatePublished|date:"dd/MM/yyyy"}}</td>
                    <td>${{book.Price}}</td>
                </tr>
            </tbody>
        </table>
    </div>
</body>
</html>

在这里,网站同时运行 IIS Express 和 WebListener。

历史

  • 2015 年 5 月 16 日:首次提交
  • 2015 年 5 月 24 日:向文章添加了 ZIP 解决方案

源代码

本文的源代码可在 GitHub 上获取:https://github.com/mostafaasad/ASPNET5/tree/master/BooksAngular-Part1/BooksAngularApp

下一篇

在下一个模块中,我将探讨数据的搜索和过滤,以及使用 Entity Framework 7 和 DNX 进行数据库迁移。

© . All rights reserved.