在本教程中,将介绍如何使用Angular和.NET Core创建一个基础的Web API应用程序,该应用程序将具备添加、编辑和删除客户信息的基本功能。将使用以下框架:
请确保已经安装了以下软件:
在下载的代码中,打开文件:
api/Api/appsettings.Development.json
并将其中的“Data Source”更改为SQL Server地址:
"AngApiStarterConn": "Data Source=服务器名称;Initial Catalog=AngApiStarter;Integrated Security=True;"
构建API解决方案以确保NuGet包已安装,并且解决方案构建成功。在运行Entity Framework数据库更新之前,需要先成功构建解决方案。
打开命令提示符,导航到:
api/Data
然后运行以下命令,该命令将使用应用程序所需的结构更新SQL Server:
dotnet ef database update -s ..\Api\Api.csproj
现在应该在SQL Server中拥有一个名为AngApiStarter的数据库,其中包含一个名为Customers的表。可以使用以下SQL将一个示例客户插入到表中:
insert into customers(firstName, lastName) values('John', 'Smith');
通过运行API项目并访问以下URL,检查API是否可以从数据库获取数据,该URL应该返回数据库中的客户列表:
https://localhost:44381/api/customer
导航到命令提示符中的ui文件夹,然后运行以下命令以获取运行Angular所需的库:
npm update
如果遇到模块未找到的错误,则可以运行以下命令以获取所需的模块:
npm install
通过运行以下命令启动Angular,这将启动服务器并在浏览器中打开应用程序:
ng serve --open
从API的数据层开始,创建了一个Customer类模型:
public class Customer { [Required] public int CustomerId { get; set; } [Required, StringLength(80)] public string FirstName { get; set; } [Required, StringLength(80)] public string LastName { get; set; } }
然后创建了Customer的DbContext类,使用Customer类作为DbSet,它最终将成为数据库中的一个表:
public class AngApiStarterDbContext : DbContext { public DbSet Customer { get; set; } public AngApiStarterDbContext(DbContextOptions<AngApiStarterDbContext> options) : base(options) { } }
在API项目中,定义了数据库连接字符串,并在Startup.cs的ConfigureServices方法中添加了以下代码:
services.AddDbContextPool<AngApiStarterDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("AngApiStarterConn")));
构建解决方案,然后使用命令提示符导航到Data项目文件夹,并运行以下命令以创建数据库迁移的类:
dotnet ef migrations add initialCreate -s ..\Api\Api.csproj
创建类后,运行以下命令在SQL Server中创建数据库和表:
dotnet ef database update -s ..\Api\Api.csproj
Data项目中包含了接口及其使用Entity Framework的实现:
public interface ICustomerData { Task<IEnumerable<Customer>> Get(); Task<Customer> GetCustomerById(int id); Task<Customer> Update(Customer customer); Task<Customer> Add(Customer customer); Task<int> Delete(int customerId); }
public class SqlCustomerData : ICustomerData { public AngApiStarterDbContext DbContext { get; } public SqlCustomerData(AngApiStarterDbContext dbContext) { DbContext = dbContext; } ... }
在API项目中,在ConfigureServices方法中定义了接口的实现类:
services.AddScoped<ICustomerData, SqlCustomerData>();
控制器也通过依赖注入使用接口:
public class CustomerController : ControllerBase { public ICustomerData CustomerData { get; } public CustomerController(ICustomerData customerData) { CustomerData = customerData; } ... }
由于API和UI将从不同的网站运行,因此需要允许UI网站通过CORS访问API。在API的appsettings.Development.json中,它有UI的url:
"CorsUrl": "http://localhost:4200"
然后在Startup.cs文件中指定CORS策略:
private readonly string corsPolicy = "defaultPolicy"; public void ConfigureServices(IServiceCollection services) { services.AddCors(options => { options.AddPolicy(corsPolicy, builder => { builder.WithOrigins(Configuration["CorsUrl"]).AllowAnyMethod().AllowAnyHeader().AllowCredentials(); }); }); ... }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { ... app.UseCors(corsPolicy); ... }
使用Angular的入门项目,在项目中定义了Angular Materials组件:
import { MaterialModule } from './common/material.module';
然后在app.module.ts中只包含Material模块:
import { MaterialModule } from './common/material.module';
API url的位置在environments/environments.ts文件中定义:
export const environment = { production: false, apiCustomer: 'https://localhost:44381/api/customer' };
模型在model/ICustomer.ts文件中定义,与API的模型签名相同:
export interface ICustomer { customerId: number, firstName: string, lastName: string }
与API交互的支持在service/customer.service.ts文件中。它使用BehaviorSubject将客户列表作为Observable(可以将其视为数据流)。当Observable变化时,所有的观察者(可以将其视为监听器或订阅者)都会收到变化通知,并相应地做出反应。在CustomerService类中,当Observable需要通知所有观察者时,调用customerList.next方法:
private customerList = new BehaviorSubject<ICustomer[] | null>(null); customerList$ = this.customerList.asObservable(); ... //get the list of customers get(){ this.http.get<ICustomer[]>(this.url).pipe( ).subscribe(result => { if (result) //notify the Observers of the change this.customerList.next(result) }); }
Customer组件,显示可以管理的客户列表,通过在ngOnInit方法中订阅CustomerService类来监听变化:
export class CustomerComponent implements OnInit { ... ngOnInit(){ this.custService.customerList$.subscribe(data => this.customerList = data); } }
当用户点击添加或编辑客户按钮时,它将打开一个对话框,可以将数据传递给对话框,并定义对话框关闭时采取的操作。下面显示了在添加新客户时传递给对话框的数据,并在对话框关闭时调用CustomerService:
openAddDialog(){ let dialogRef = this.custDialog.open(CustomerDialog, { width: '300px', data: { action: 'add', customer: { customerId: 0, firstName: '', lastName: ''} } }); dialogRef.afterClosed().subscribe(result => { //add the customer if (result) this.custService.add(result) }); }
对于编辑客户,需要将客户数据的副本传递给对话框,而不是实际的客户数据,以便如果用户决定取消,客户值不会被更改:
customer: {...customer} //shallow copy the customer using spread operator
对话框组件在app/customer/dialog/customer-dialog.component.ts中定义,它使用MAT_DIALOG_DATA从Material的Dialog组件接受调用者的数据:
export class CustomerDialog { ... constructor(public dialogRef: MatDialogRef<CustomerDialog>, @Inject(MAT_DIALOG_DATA) public data: any) { this.action = data.action; this.customer = data.customer; } }
最后,使用npm添加Bootstrap库,并将对Bootstrap样式表的引用添加到index.html:
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap...>
<div class="d-flex flex-row justify-content-center"> <div class="d-flex flex-column">