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

SPA with AngularJS in .NET

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.81/5 (15投票s)

2015 年 9 月 13 日

CPOL

7分钟阅读

viewsIcon

42847

downloadIcon

1900

这是两部分演示,第一部分将展示如何向数据库插入一条记录,并从数据库获取所有记录并进行显示。第二部分将演示删除和更新操作。

我尝试在线学习单页应用程序(SPA),它们对主要针对单个数据库表进行CRUD操作的初学者来说是不错的。在实际应用中,会有很多表需要从中检索数据并插入,我找不到这样的文章。因此,我决定自己创建一个,并感谢我的项目经理要求我演示使用AngularJS框架的SPA。

这是两部分演示,第一部分将展示如何向数据库插入一条记录,并从数据库获取所有记录并进行显示。第二部分将演示删除和更新操作。

文章概述:SPA中对多个数据库表进行CRUD操作。

引言

如今,用户希望更快的Web应用程序,拥有丰富的UI,并且能够从多个设备(如平板电脑、手机等)访问应用程序。借助HTML5、JQuery或JavaScript框架和AJAX,可以开发SPA。

SPA在现实中的意义

简单来说,它只是一个Web应用程序,有一个父页面,子页面(部分视图)根据需求加载。在SPA中,所有必需的资源都会随初始请求一起加载,其余资源会根据需求加载。

SPA vs 传统Web应用

传统Web应用

正如您在下图中看到的,传统的应用程序在用户请求时,会返回服务器并重新加载页面以渲染新页面。

来源: https://msdn.microsoft.com/en-us/magazine/dn463786.aspx?f=255&MSPPError=-2147217396

SP应用

在SPA中,当用户请求页面时,它会动态加载,并通过Ajax调用服务器,数据通过JSON检索。在SPA中,数据渲染和路由在客户端完成,使其响应速度非常快,就像桌面应用程序一样。

来源: http://www.slideshare.net/hsplmkting/benefits-of-developing-single-page-web-applications-using-angular-js

SPA的优点

  • 无页面闪烁。原生应用感觉。
  • 客户端路由和客户端数据渲染。
  • 服务器数据为JSON格式。

SPA的缺点

  • 用户必须启用JavaScript。
  • 安全性。

在Visual Studio中创建项目

在本演示中,我使用了VS 2012、.NET Framework 4.5、AngularJS、Bootstrap和.mdf文件进行数据库操作。我提供了所有必要的截图和源代码供下载。

打开Visual Studio 2012,并使用MVC4模板创建一个项目,我将项目命名为SingePageAngularJS,但您可以根据需要命名。

添加JQuery和AngularJS

下图显示我从NuGet包安装了Bootstrap、AngularJS和Angular路由。并在scripts文件夹中添加了一个名为App的新文件夹,并添加了一个名为myApp的新JavaScript文件。稍后我将在myApp中添加AngularJS代码。

现在将这些已安装的文件添加到您的MVC项目中。转到 -> App_Start -> BundleConfig。像我在下图中用红色矩形显示的添加以下几行。

现在您必须确保这些文件已添加到您的Master页面(_Layout.cshtml)中,该页面位于应用程序的Views -> Shared文件夹中。我总是将我的JQuery或必需文件放在项目head标签内,以便它们可以被您的整个应用程序访问,如下面的图像所示。

因此,现在我们已经将JQuery、angular、angular路由、bootstrap css和bootstrap.js文件添加到我们的布局页面,并且我们需要确保它们在浏览器中正确渲染而没有任何错误。为了仔细检查,请在Chrome浏览器中运行应用程序,然后按F12打开浏览器工具。通过单击Sources选项卡检查所有文件是否已加载,并检查console选项卡中没有错误。下图确认所有文件都已正确加载,并且console选项卡中没有错误。

设置数据库

我将添加一个.mdf文件用于数据库操作,并将其命名为AngularDB.mdf。在.mdf文件中,我创建了两个表(EmpDetailsEmpAddress),脚本如下。EmpDetails表包含员工信息,EmpAddress表包含员工的多个地址。在这两个表之间,我们通过EmpId建立了一个外键关系,因为每当您选择一名员工时,我都想显示该员工的所有地址。

EmpDetails 表

CREATE TABLE [dbo].[EmpDetails] (
    [EmpID]    INT          IDENTITY (1, 1) NOT NULL,
    [EmpName]  VARCHAR (50) NULL,
    [EmpPhone] VARCHAR (50) NULL,
    PRIMARY KEY CLUSTERED ([EmpID] ASC)
);

EmpAddress 表

CREATE TABLE [dbo].[EmpAddress] (
    [EmpAddressId] INT           IDENTITY (1, 1) NOT NULL,
    [Address1]      VARCHAR (500) NULL,
    [Address2]      VARCHAR (500) NULL,
    [Address3]      VARCHAR (500) NULL,
    [EmpID]        INT           NULL,
    PRIMARY KEY CLUSTERED ([EmpAddressId] ASC),
    FOREIGN KEY ([EmpID]) REFERENCES [dbo].[EmpDetails] ([EmpID])
);

注意:在本应用程序中,我的重点不仅在于CRUD操作,还在于展示如何从多个表中显示数据到UI,并将数据传递到多个表中。

MVVM 模式

对于这个演示,我将在AngularJS脚本和Visual Studio中都使用MVVM模式。下图显示了我创建了一个名为ViewModels的文件夹(它引用了多个模型),并在models文件夹中添加了两个模型,我将它们命名为EmpDetailsModelEmpAddressModel

Angularjs 代码

在上一节中,我们完成了与数据库相关的部分。现在我们开始进入本次演示的主要主题。

在“添加JQuery和Angularjs”部分:我们创建了一个myApp.js文件并将其留空,只需将以下代码复制并粘贴进去。

angular.module('App', ['AngularDemo.EmpAddController',
                       'AngularDemo.AddressController',
                       'AngularDemo.DeleteController'
])

.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {

    $routeProvider.when('/', {
        templateUrl: '/Home/AddEmployee',
        controller: 'EmpAddCtrl',
    });
    $routeProvider.when('/Edit', {
        templateUrl: '/Home/EditEmployee',
        controller: 'EditCtrl'
    });
    $routeProvider.when('/Delete', {
        templateUrl: '/Home/DeleteEmployee',
        controller: 'DeleteCtrl'
    });
    $routeProvider.otherwise({
        redirectTo: '/'
    });
    // Specify HTML5 mode (using the History APIs) or HashBang syntax.
    $locationProvider.html5Mode(false).hashPrefix('!');

}]);

//Add Employee Controller
angular.module('AngularDemo.EmpAddController', ['ngRoute'])
.controller('EmpAddCtrl', function ($scope, $http) {

    $scope.EmpAddressList = {};
    $http.get('/Home/ShowEmpList').success(function (data) {
        $scope.EmpAddressList = data;
      
    });


    $scope.EmpDetailsModel =
     {
         EmpID: '',
         EmpName: '',
         EmpPhone: ''
     };

    $scope.EmpAddressModel =
    {
        Address1: '',
        Address2: '',
        Address3: ''
    };

    $scope.EmployeeViewModel = {
        empDetailModel: $scope.EmpDetailsModel,
        empAddressModel: $scope.EmpAddressModel
    };


    $scope.AddEmployee = function () {
        //debugger;
        $.ajax({
            url: '/Home/AddEmpDetails',
            type: 'POST',
            dataType: 'json',
            contentType: 'application/json',
            traditional: true,
            data: JSON.stringify({ EmployeeViewModelClient: $scope.EmployeeViewModel }),
            success: function (data) {
                $scope.EmpAddressList.push(data[0]);
                $scope.$apply();
                //$scope.$apply();
                alert("Record is been added");
            }
        });
    };
});


//Address Controller
angular.module('AngularDemo.AddressController', ['ngRoute'])
.controller('EditCtrl', function ($scope, $http) {
    $scope.Message = "Edit in Part 2 is coming soon";
});

angular.module('AngularDemo.DeleteController', ['ngRoute'])
.controller('DeleteCtrl', function ($scope, $http) {
    $scope.Message = "Delete in Part 2 is coming soon";
});

Angular代码解释

angular.module('App', ['AngularDemo.EmpAddController',
                       'AngularDemo.AddressController',
                       'AngularDemo.DeleteController'
])

注意:angular.module是创建、注册和检索Angular模块的全局位置。所有需要提供给应用程序的模块都必须通过这种机制进行注册。(https://docs.angularjs.org/api/ng/function/angular.module)。

第一个参数是您的Angular应用程序的名称,对于我们的演示,它命名为“App”。

第二个参数是一个依赖项数组,目前我们只有三个依赖项,分别是“AngularDemo.EmpAddController”、“AngularDemo.AddressController”和“AngularDemo.DeleteController”。

ng-app指令:一旦您为AngularJS命名,您就必须根据您的需求在HTML页面中使用它。对于本次演示,我在_Layout页面中的html标签中使用了ng-app,如下所示。

客户端路由

因此,我们已经定义了AngularJS应用程序的名称并列出了依赖项数组。现在,让我们在客户端创建路由。为此,我创建了以下三个路由,带有templateUrl和controller。

.config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {

    $routeProvider.when('/', {
        templateUrl: '/Home/AddEmployee',
        controller: 'EmpAddCtrl',
    });
    $routeProvider.when('/Edit', {
        templateUrl: '/Home/EditEmployee',
        controller: 'EditCtrl'
    });
    $routeProvider.when('/Delete', {
        templateUrl: '/Home/DeleteEmployee',
        controller: 'DeleteCtrl'
    });
    $routeProvider.otherwise({
        redirectTo: '/'
    });
    // Specify HTML5 mode (using the History APIs) or HashBang syntax.
    $locationProvider.html5Mode(false).hashPrefix('!');

}]);

每当AngularJS找到路由中提到的URL时,它就会转到相应的AngularJS控制器,在那里我们需要创建模型和Ajax调用到服务器。以下是控制器代码。

//Add and display Employee Controller
angular.module('AngularDemo.EmpAddController', ['ngRoute'])
.controller('EmpAddCtrl', function ($scope, $http) {

$scope.EmpDetailsModel =
     {
         EmpID: '',
         EmpName: '',
         EmpPhone: ''
     };

    $scope.EmpAddressModel =
    {
        Address1: '',
        Address2: '',
        Address3: ''
    };

    $scope.EmployeeViewModel = {
        empDetailModel: $scope.EmpDetailsModel,
        empAddressModel: $scope.EmpAddressModel
    };

    $scope.EmpAddressList = {};
    $http.get('/Home/ShowEmpList').success(function (data) {
        $scope.EmpAddressList = data;
    });
   
    $scope.AddEmployee = function () {
        $.ajax({
            url: '/Home/AddEmpDetails',
            type: 'POST',
            dataType: 'json',
            contentType: 'application/json',
            traditional: true,
            data: JSON.stringify({ EmployeeViewModelClient: $scope.EmployeeViewModel }),
            success: function (data) {
                $scope.EmpAddressList.push(data[0]);
                $scope.$apply();
                alert("Record is been added");
            }
        });
    };
});

$scope负责设置特定控制器的模型属性和函数/行为。

EmpAddCtrl控制器中,我们有两个模型(EmpDetailsModelEmpAddressModel)和一个视图模型(EmployeeViewModel),我们将这个视图模型传递给服务器以将数据保存到数据库,使用$scope.AddEmployee函数,并且$http.get将在页面加载时获取所有记录列表。

视图:现在我们已经完成了AngularJS部分,我们必须在HTML页面中使用它。所以,这是我的_layout页面看起来的样子。_Layout.html

<!DOCTYPE html>
<html lang="en" ng-app="App">
<head>
    <meta charset="utf-8" />
    <title>@ViewBag.Title - My ASP.NET MVC Application</title>
    <link href="~/favicon.ico" rel="shortcut icon" type="image/x-icon" />
    <meta name="viewport" content="width=device-width" />
    @Styles.Render("~/Content/css")
    @Scripts.Render("~/bundles/jquery")
    @Scripts.Render("~/bundles/bootstrap")
    @Scripts.Render("~/bundles/angular")
    @Scripts.Render("~/bundles/CustomAngular")
    @Scripts.Render("~/bundles/modernizr")
</head>
<body>
    <header>
        <nav class="navbar navbar-default navbar-static-top">
            <div class="container">
                <div class="navbar-header">
                    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                        <span class="sr-only">Toggle navigation</span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
                                    </div>
                <div id="navbar" class="navbar-collapse collapse">
                    <ul class="nav navbar-nav">
                        <li class="active"><a href="#!/">Add</a></li>
                        <li><a href="#!/Address">Edit/Update</a></li>
                        <li><a href="#!/Delete">Delete</a></li>
                    </ul>
                    
                </div>
                <!--/.nav-collapse -->
            </div>
        </nav>
    </header>
    <div id="body">
        @RenderSection("featured", required: false)
        <section class="content-wrapper main-content clear-fix">
            @RenderBody()
        </section>
    </div>

    @RenderSection("scripts", required: false)


</body>
</html>

Index.html 页面

只需复制以下代码并将其粘贴到index html页面中。ng-view是一个占位符,部分视图在此处动态加载。

<div ng-view="" ></div>

AddEmployee.html 部分视图

将以下代码复制并粘贴到您的AddEmployee部分视图中。在此视图中,用户可以添加新的员工和地址信息,并且还以网格格式显示员工列表。

ng-model指令将HTML控件(input、select、textarea)的值绑定到应用程序数据。

<div style="width: 50%; margin: 50px auto;">

    <table>
        <tr>
            <td>
                <strong>Employee Name:</strong>
            </td>
            <td>
                <input type="text" class="form-control" ng-model="EmpDetailsModel.EmpName" placeholder="Employee Name" />
            </td>
        </tr>
        <tr>
            <td>
                <strong>Employee Phone:</strong>

            </td>
            <td>
                <input type="text" class="form-control" ng-model="EmpDetailsModel.EmpPhone" placeholder="Employee Phone" />
            </td>
        </tr>
        <tr>
            <td>
                <strong>Address 1:</strong>

            </td>
            <td>
                <input type="text" class="form-control" ng-model="EmpAddressModel.Address1" placeholder="Address 1" />
            </td>
        </tr>
        <tr>
            <td>
                <strong>Address 2:</strong>

            </td>
            <td>
                <input type="text" class="form-control" ng-model="EmpAddressModel.Address2" placeholder="Address 2" />
            </td>
        </tr>

        <tr>
            <td>
                <strong>Address 3:</strong>

            </td>
            <td>
                <input type="text" class="form-control" ng-model="EmpAddressModel.Address3" placeholder="Address 3" />
            </td>
        </tr>
        <br />
        <tr>
            <td>    
            </td>
            <td>
                <button type="button" ng-click="AddEmployee();" class="btn btn-primary">Save</button>
            </td>
        </tr>

    </table>
</div>

<hr style="color: black" />

<div style="width: 50%; margin: 50px auto;">
    <div class="panel panel-default">
        <!-- Default panel contents -->
        <div class="panel-heading"><b>Employee Details </b></div>
        <div class="table-responsive">
            <table id="EmployeeTable" class="table table-striped table-bordered table-hover table-condensed">
                <thead>
                    <tr>
                        <th>Employee Name</th>
                        <th>Employee Phone</th>
                        <th>Employee Address1</th>
                        <th>Employee Address2</th>
                        <th>Employee Address3</th>
                    </tr>
                </thead>
                <tbody>
                    <tr data-ng-repeat="Emp in EmpAddressList">

                        <td>{{Emp.empDetailModel.EmpName}}</td>
                        <td>{{Emp.empDetailModel.EmpPhone}}</td>
                        <td>{{Emp.empAddressModel.Address1}}</td>
                        <td>{{Emp.empAddressModel.Address2}}</td>
                        <td>{{Emp.empAddressModel.Address3}}</td>

                    </tr>

                    @*<tr ng-if="states.NewRow">*@
                    <tr ng-if="EmpAddressList.length == 0">
                        <td class="text-center" colspan="4">There are no Employee details to display
                        </td>
                    </tr>
                </tbody>
            </table>

        </div>
    </div>
    
</div>

输出屏幕

现在我们已经完成了所有编码,只需按F5运行即可看到以下屏幕截图。最初我们没有数据,因此它显示“没有要显示的员工详细信息”。

保存按钮

输入员工和地址数据,当用户单击保存按钮时,它将触发AngularJS中的AddEmployee函数,该函数调用相应的控制器操作。下图显示我们已输入信息。

一旦用户单击保存,视图模型将从AngularJS传递到MVC控制器操作。下面有两个屏幕截图显示数据被传递到模型(empDetailsModelempAddressModel)。

将记录保存到数据库后,您将收到一条确认消息,表明记录已添加。

数据库

我们在UI上输入的数据已保存到数据库。

结论

与传统Web应用程序相比,单页应用程序将为您提供更好的性能。但是,在安全方面切勿妥协,在开发单页应用程序之前,您应该考虑安全性并采取足够的措施。

© . All rights reserved.