SPA with AngularJS in .NET






4.81/5 (15投票s)
这是两部分演示,第一部分将展示如何向数据库插入一条记录,并从数据库获取所有记录并进行显示。第二部分将演示删除和更新操作。
我尝试在线学习单页应用程序(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中,数据渲染和路由在客户端完成,使其响应速度非常快,就像桌面应用程序一样。
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文件中,我创建了两个表(EmpDetails
和EmpAddress
),脚本如下。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文件夹中添加了两个模型,我将它们命名为EmpDetailsModel
和EmpAddressModel
。
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
控制器中,我们有两个模型(EmpDetailsModel
、EmpAddressModel
)和一个视图模型(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控制器操作。下面有两个屏幕截图显示数据被传递到模型(empDetailsModel
和empAddressModel
)。
将记录保存到数据库后,您将收到一条确认消息,表明记录已添加。
数据库
我们在UI上输入的数据已保存到数据库。
结论
与传统Web应用程序相比,单页应用程序将为您提供更好的性能。但是,在安全方面切勿妥协,在开发单页应用程序之前,您应该考虑安全性并采取足够的措施。