主厨(第4部分)- ASP. netNET Core和Angular 2 CRUD SPA
介绍 在Master Chef(第1部分)和Master Chef(第2部分)中,我介绍了如何使用ASP。Net Core和Angular JS。在Master Chef(第3部分)中,我开始介绍如何创建ASP。NET Core和Angular 2应用程序。在这篇文章中,我将继续讨论如何使用ASP。NET Core MVC, Entity Framework Core和Angular 2来实现一个CRUD SPA(单页应用)。 服务器数据模型 创建、读取、更新和删除(CRUD的首字母缩写)是持久性存储的四个基本功能。 我们需要首先在我们的repository类中实现数据库级的CRUD。添加一个基本实体类。 隐藏,复制Code
public class Entity
{
public virtual Guid Id { get; set; } public virtual Guid? ParentId { get; set; }
}
然后让Recipe、RecipeStep和RecipeItem继承实体类,并使用这些通用名称Id和ParentId替换相应的键和引用。 隐藏,收缩,复制Code
public partial class Recipe : Entity
{
public Recipe()
{
RecipeSteps = new HashSet<RecipeStep>();
} public string Name { get; set; }
public DateTime ModifyDate { get; set; }
public string Comments { get; set; } public virtual ICollection<RecipeStep> RecipeSteps { get; set; }
} public partial class RecipeStep : Entity
{
public RecipeStep()
{
RecipeItems = new HashSet<RecipeItem>();
} public int StepNo { get; set; }
public string Instructions { get; set; } public virtual ICollection<RecipeItem> RecipeItems { get; set; }
[JsonIgnore]
public Recipe Recipe { get; set; }
}
public partial class RecipeItem : Entity
{
public string Name { get; set; }
public decimal Quantity { get; set; }
public string MeasurementUnit { get; set; }
[JsonIgnore]
public RecipeStep RecipeStep { get; set; }
}
现在我们需要更改DbContext类以应用Id和ParentId。 隐藏,复制Code
modelBuilder.Entity<RecipeItem>(entity =>
{
entity.HasKey(e => e.Id)
.HasName("PK_RecipeItems"); entity.Property(e => e.Id).ValueGeneratedNever().HasColumnName("ItemId");
entity.Property(e => e.ParentId).HasColumnName("RecipeStepId");
entity.Property(e => e.MeasurementUnit)
.IsRequired()
.HasColumnType("varchar(20)"); entity.Property(e => e.Name)
.IsRequired()
.HasColumnType("varchar(255)"); entity.Property(e => e.Quantity).HasColumnType("decimal"); entity.HasOne(d => d.RecipeStep)
.WithMany(p => p.RecipeItems)
.HasForeignKey(d=>d.ParentId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("FK_RecipeItems_RecipeSteps");
});
对于RecipeItem实体,我们使用“HasColumnName”来告诉模型构建器映射,“Id”映射到“ItemId”和“ParentId”映射到“RecipeStepId”。然后在引用定义中,将HasForeignKey(d=>d. recipestepid)改为HasForeignKey(d=>d. parentid)。 对于RecipeStep也有同样的解决方法: 隐藏,复制Code
modelBuilder.Entity<RecipeStep>(entity =>
{
entity.HasKey(e => e.Id)
.HasName("PK_RecipeSteps"); entity.Property(e => e.Id).ValueGeneratedNever().HasColumnName("RecipeStepId");
entity.Property(e => e.ParentId).HasColumnName("RecipeId");
entity.Property(e => e.Instructions).HasColumnType("text"); entity.HasOne(d => d.Recipe)
.WithMany(p => p.RecipeSteps)
.HasForeignKey(d => d.ParentId)
.OnDelete(DeleteBehavior.Cascade)
.HasConstraintName("FK_RecipeSteps_Recipes");
});
DeleteBehavior.Cascade是什么?这是在删除父对象时删除子对象的选项。对于我们的示例,删除一个配方将删除该配方的所有配方步骤和配方项,删除一个步骤将删除该步骤的所有项。 Recipe类没有ParentId。所以我们需要告诉model builder忽略映射。 隐藏,复制Code
modelBuilder.Entity<Recipe>(entity =>
{
entity.HasKey(e => e.Id)
.HasName("PK_Recipes");
entity.Ignore(e => e.ParentId);
entity.Property(e => e.Id).ValueGeneratedNever().HasColumnName("RecipeId"); entity.Property(e => e.Comments).HasColumnType("text"); entity.Property(e => e.ModifyDate).HasColumnType("date"); entity.Property(e => e.Name)
.IsRequired()
.HasColumnType("varchar(255)");
});
在应用这些更改之后,现在我们可以在存储库类中使用泛型来实现、创建、读取、更新和删除Recipe、RecipeStep和RecipeItem的功能。 隐藏,收缩,复制Code
public T GetEntity<T>(Guid id) where T : Entity
{
try
{
return _dbContext.Find<T>(id);
}
catch (Exception ex)
{
throw ex;
}
} public T AddEntity<T>(T entity) where T : Entity
{
_dbContext.Add<T>(entity);
_dbContext.SaveChanges();
var result = GetEntity<T>(entity.Id);
return result;
} public void UpdateEntity<T>(T entity) where T : Entity
{
_dbContext.Update<T>(entity);
_dbContext.SaveChanges();
} public void DeleteEntity<T>(Guid id) where T : Entity
{
var entity = GetEntity<T>(id);
_dbContext.Remove<T>(entity);
_dbContext.SaveChanges();
}
Web API控制器 在RecipesController类中,我们设置了处理基本CRUD请求的函数。这里有一个GET请求,要求所有食谱。这里还有另一个获取id的Get函数,因此用户可以请求返回特定的菜谱。我们这里还有更多的功能——允许用户创建一个新的食谱。我们还可以更新现有的食谱。最后,删除——可以删除特定的菜谱。 隐藏,收缩,复制Code
[HttpGet("{id}")]
public IActionResult Get(Guid id)
{
var recipe = _repository.GetEntity<Recipe>(id);
if (recipe != null)
return new ObjectResult(recipe);
else
return new NotFoundResult();
} [HttpPost]
public IActionResult Post([FromBody]Recipe recipe)
{
if (recipe.Id == Guid.Empty)
{
recipe.Id = Guid.NewGuid();
recipe.ModifyDate = DateTime.Now;
return new ObjectResult(_repository.AddEntity<Recipe>(recipe));
}
else
{
var existingOne = _repository.GetEntity<Recipe>(recipe.Id);
existingOne.Name = recipe.Name;
existingOne.Comments = recipe.Comments;
existingOne.ModifyDate = DateTime.Now;
_repository.UpdateEntity<Recipe>(existingOne);
return new ObjectResult(existingOne);
}
} [HttpPut("{id}")]
public IActionResult Put(Guid id, [FromBody]Recipe recipe)
{
var existingOne = _repository.GetEntity<Recipe>(recipe.Id);
existingOne.Name = recipe.Name;
existingOne.Comments = recipe.Comments;
_repository.UpdateEntity<Recipe>(existingOne);
return new ObjectResult(existingOne);
} [HttpDelete("{id}")]
public IActionResult Delete(Guid id)
{
_repository.DeleteEntity<Recipe>(id);
return new StatusCodeResult(200);
}
那么RecipeStep和RecipeItem呢?我们能把不同的HttpGet, HttpPost和HttpDelete放到一个API控制器中吗? 路由是Web API将URI匹配到操作的方式。Web API 2支持一种新的路由类型,称为属性路由。顾名思义,属性路由使用属性来定义路由。属性路由使您可以对web API中的uri进行更多的控制。例如,您可以轻松地创建描述资源层次结构的uri。 现在我们使用属性路由在一个API控制器中定义多个HTTPGet、HTTPPost和HTTPDelete。 隐藏,收缩,复制Code
//GET api/recipes/step/:id
[HttpGet]
[Route("step/{id}")]
public IActionResult GetStep(Guid id)
{
var recipeStep = _repository.GetEntity<RecipeStep>(id);
if (recipeStep != null)
return new ObjectResult(recipeStep);
else
return new NotFoundResult(); } //POST api/recipes/step
[HttpPost]
[Route("step")]
public IActionResult UpdateStep([FromBody]RecipeStep recipeStep)
{
if (recipeStep.Id == Guid.Empty)
{
recipeStep.Id = Guid.NewGuid();
return new ObjectResult(_repository.AddEntity<RecipeStep>(recipeStep));
}
else
{
var existingOne = _repository.GetEntity<RecipeStep>(recipeStep.Id);
existingOne.StepNo = recipeStep.StepNo;
existingOne.Instructions = recipeStep.Instructions;
_repository.UpdateEntity<RecipeStep>(existingOne);
return new ObjectResult(existingOne);
}
} //DELETE api/recipes/step/:id
[HttpDelete]
[Route("step/{id}")]
public IActionResult DeleteStep(Guid id)
{
_repository.DeleteEntity<RecipeStep>(id);
return new StatusCodeResult(200);
} // GET api/recipes/item/:id
[HttpGet]
[Route("item/{id}")]
public IActionResult GetItem(Guid id)
{
var recipeItem = _repository.GetEntity<RecipeItem>(id);
if (recipeItem != null)
return new ObjectResult(recipeItem);
else
return new NotFoundResult(); } //POST api/recipes/item
[HttpPost]
[Route("item")]
public IActionResult UpdateItem([FromBody]RecipeItem recipeItem)
{
if (recipeItem.Id == Guid.Empty)
{
recipeItem.Id = Guid.NewGuid();
if (recipeItem.MeasurementUnit == null)
recipeItem.MeasurementUnit = "";
return new ObjectResult(_repository.AddEntity<RecipeItem>(recipeItem));
}
else
{
var existingOne = _repository.GetEntity<RecipeItem>(recipeItem.Id);
existingOne.Name = recipeItem.Name;
existingOne.Quantity = recipeItem.Quantity;
existingOne.MeasurementUnit = recipeItem.MeasurementUnit;
_repository.UpdateEntity<RecipeItem>(existingOne);
return new ObjectResult(existingOne);
}
} //DELETE api/recipes/item/:id
[HttpDelete]
[Route("item/{id}")]
public IActionResult DeleteItem(Guid id)
{
_repository.DeleteEntity<RecipeItem>(id);
return new StatusCodeResult(200);
}
客户端视图模型 在上一篇文章中,我们创建了一个菜谱视图模型。现在我们继续创建recipestep和recipeitem。 右键点击“viewmodels”添加新的类型脚本文件。它被命名为“recipeStep”,这是一个我们用来在视图中显示的配方步骤视图模型。 隐藏,复制Code
export class RecipeStep {
public parentId: string;
public id: string;
public stepNo: number;
public instructions: string;
constructor() { }
}
右键单击“viewmodels”添加另一个类型脚本文件。它名为“recipeItem”,这是一个用于在视图中显示的菜谱项视图模型。 客户端服务 在我们的客户端服务“app.service”。我们需要添加更多的方法来实现CRUD功能。 首先导入客户端视图模型类。 隐藏,复制Code
import { Recipe } from "../viewmodels/recipe";
import { RecipeStep } from "../viewmodels/recipeStep";
import { RecipeItem } from "../viewmodels/recipeItem";
import { Observable } from "rxjs/Observable";
请注意,我们在web API控制器中实现的URL对于recipe、step和item是不同的。 在服务类中,我们定义了三个常量URL字符串。 隐藏,复制Code
//URL to web api
private recipeUrl = 'api/recipes/';
private stepUrl = 'api/recipes/step/';
private itemUrl = 'api/recipes/item/';
获取、更新和删除菜谱方法: 隐藏,复制Code
getRecipe(id: string) {
if (id == null) throw new Error("id is required.");
var url = this.recipeUrl + id;
return this.http.get(url)
.map(response => <Recipe>response.json())
.catch(this.handleError);
} saveRecipe(recipe: Recipe) {
if (recipe == null) throw new Error("recipe is required.");
var url = this.recipeUrl;
return this.http.post(url, recipe)
.map(response => <Recipe>response.json())
.catch(this.handleError);
} deleteRecipe(id:string) {
if (id == null) throw new Error("id is required.");
var url = this.recipeUrl + id;
return this.http.delete(url)
.catch(this.handleError);
}
获取、更新和删除配方步骤方法: 隐藏,收缩,复制Code
getStep(id: string) {
if (id == null) throw new Error("id is required.");
var url = this.stepUrl + id;
return this.http.get(url)
.map(response => <RecipeStep>response.json())
.catch(this.handleError);
} saveStep(step: RecipeStep) {
if (step == null) throw new Error("recipe step is required.");
var url = this.stepUrl;
return this.http.post(url, step)
.map(response => <RecipeStep>response.json())
.catch(this.handleError);
} deleteStep(id: string) {
if (id == null) throw new Error("id is required.");
var url = this.stepUrl + id;
return this.http.delete(url)
.catch(this.handleError);
} Get, update and delete recipe item methods:
getItem(id: string) {
if (id == null) throw new Error("id is required.");
var url = this.itemUrl + id;
return this.http.get(url)
.map(response => <RecipeItem>response.json())
.catch(this.handleError);
} saveItem(item: RecipeItem) {
if (item == null) throw new Error("recipe item is required.");
var url = this.itemUrl;
return this.http.post(url, item)
.map(response => <RecipeItem>response.json())
.catch(this.handleError);
} deleteItem(id: string) {
if (id == null) throw new Error("id is required.");
var url = this.itemUrl + id;
return this.http.delete(url)
.catch(this.handleError);
}
客户端路由 在ASP中使用MVC。当你指定一个特定的URL时,当你期望你的代码击中什么控制器时,你使用路由。我们还可以选择指定要传递到控制器方法中的参数。这就是服务器端路由。 在SPA中,客户端路由的作用基本相同。唯一的区别是,我们不必调用服务器。这使得我们所有的“页面”都是虚拟的。而不是要求我们的访问者总是从我们的主页开始,并浏览到我们的网站的其余部分;而不是为我们网站的每个页面在服务器上创建一个单独的页面;我们可以预先加载所有站点,用户可以导航到他们想要的页面。它们甚至可以直接链接到该页面,而客户端将适当地处理页面的显示。 通常,在实现了所有公共代码之后,路由会在应用程序的顶部启用。所以,在您希望路由生效的位置,添加以下标记: & lt; router-outlet> & lt; / router-outlet> 应用程序组件 现在我们改变我们的应用程序组件,使客户端路由实现单页应用程序。 隐藏,复制Code
import { Component, OnInit } from "@angular/core";
import { Router } from "@angular/router";
import { Recipe } from "./viewmodels/recipe";
import { AppService } from "./services/app.service"; @Component({
selector: 'masterchef2',
template: `
<h1>{{title}}</h1>
<router-outlet></router-outlet>
`
}) export class AppComponent {
title = "Master Chef Recipes";
}
现在可以看到App组件非常简单。只显示标题。& lt; router-outlet> & lt; / router-outlet>将根据路径带来不同的模板。 菜谱列表组件 在上一篇文章中,我们将食谱列表放入app组件中。因为我们需要实现更复杂的功能,所以我把它从app component中取出来。 右键点击“scripts/app/components”文件夹,添加一个新项目。选择”。Net Core/客户端" TypeScript文件。命名为“recipe-list.component.ts”。 隐藏,收缩,复制Code
import { Recipe } from "../viewmodels/recipe";
import { AppService } from "../services/app.service"; @Component({
selector: 'recipe-list',
templateUrl: '../partials/recipes.html'
}) export class RecipeListComponent implements OnInit { items: Recipe[];
errorMessage: string; constructor(private appService: AppService) {
//called first time before the ngOnInit()
} ngOnInit() {
//called after the constructor and called after the first ngOnChanges()
var service = this.appService.getAllRecipes();
service.subscribe(
items => {
this.items = items;
},
error => this.errorMessage = <any>error
);
} public Expand(recipe:Recipe) {
recipe.show = !recipe.show;
} }
请注意Expand方法中的更改。现在“show”属性已经不在组件级别了。它被移动到配方视图模型。那是因为我想控制每一个食谱,而不是所有的食谱。 详细配方成分 右键点击“scripts/app/components”文件夹,添加一个新项目。选择”。Net Core/客户端" TypeScript文件。命名为“recipe-detail.component.ts”。 隐藏,收缩,复制Code
import { Component, OnInit, OnDestroy } from "@angular/core";
import { Router, ActivatedRoute } from "@angular/router";
import { Recipe } from "../viewmodels/recipe";
import { AppService } from "../services/app.service"; @Component({
selector: 'recipe-detail',
templateUrl: '../partials/edit.html'
}) export class RecipeDetailComponent implements OnInit {
item: Recipe;
sub: any; constructor(private AppService: AppService, private router: Router, private route: ActivatedRoute) { } ngOnInit() {
this.sub = this.route.params.subscribe(params => {
var id = params['id'];
this.AppService.getRecipe(id).subscribe(item => this.item = item);
});
} ngOnDestroy() {
this.sub.unsubscribe();
} public editRecipe() {
this.AppService.saveRecipe(this.item).subscribe(
item => { this.item = item; this.router.navigate(['/recipes']); },
error => console.log(error)
)
}
}
在这个类中,我们首先调用getRecipe服务函数来获取菜谱信息,然后调用saveRecipe服务函数来更新菜谱。 配方新组件 右键点击“scripts/app/components”文件夹,添加一个新项目。选择”。Net Core/客户端" TypeScript文件。命名为“reci- new.component.ts”。 隐藏,收缩,复制Code
import { Component, OnInit, OnDestroy } from "@angular/core";
import { Router, ActivatedRoute } from "@angular/router";
import { Recipe } from "../viewmodels/recipe";
import { AppService } from "../services/app.service"; @Component({
selector: 'recipe-new',
templateUrl: '../partials/add.html'
}) export class RecipeNewComponent implements OnInit {
item: Recipe;
sub: any; constructor(private AppService: AppService, private router: Router, private route: ActivatedRoute) { } ngOnInit() {
this.item = new Recipe();
} ngOnDestroy() {
} public addRecipe() {
this.AppService.saveRecipe(this.item).subscribe(
item => { this.item = item; this.router.navigate(['/recipes']); },
error => console.log(error)
)
} }
在这个类中,我们首先创建一个新菜谱,然后调用saveRecipe服务函数来添加菜谱。 配方删除组件 右键点击“scripts/app/components”文件夹,添加一个新项目。选择”。Net Core/客户端" TypeScript文件。命名为“reci- delete.component.ts”。 隐藏,收缩,复制Code
import { Component, OnInit, OnDestroy } from "@angular/core";
import { Router, ActivatedRoute } from "@angular/router";
import { Recipe } from "../viewmodels/recipe";
import { AppService } from "../services/app.service"; @Component({
selector: 'recipe-delete',
templateUrl: '../partials/delete.html'
}) export class RecipeDeleteComponent implements OnInit {
item: Recipe;
sub: any; constructor(private AppService: AppService, private router: Router, private route: ActivatedRoute) { } ngOnInit() {
this.sub = this.route.params.subscribe(params => {
var id = params['id'];
this.AppService.getRecipe(id).subscribe(item => this.item = item);
});
} ngOnDestroy() {
this.sub.unsubscribe();
} public deleteRecipe() {
this.AppService.deleteRecipe(this.item.id).subscribe(
() => this.router.navigate(['/recipes']),
error => console.log(error)
)
} }
在这个类中,我们首先调用getRecipe服务函数来获取菜谱信息,然后调用deleteRecipe服务函数来删除菜谱。 步骤详细的组件 右键点击“scripts/app/components”文件夹,添加一个新项目。选择”。Net Core/客户端" TypeScript文件。命名为“step-detail.component.ts”。 隐藏,收缩,复制Code
import { Component, OnInit, OnDestroy } from "@angular/core";
import { Router, ActivatedRoute } from "@angular/router";
import { RecipeStep } from "../viewmodels/recipestep";
import { AppService } from "../services/app.service"; @Component({
selector: 'step-detail',
templateUrl: '../partials/editStep.html'
}) export class StepDetailComponent implements OnInit {
item: RecipeStep;
sub: any; constructor(private AppService: AppService, private router: Router, private route: ActivatedRoute) { } ngOnInit() {
this.sub = this.route.params.subscribe(params => {
var id = params['id'];
this.AppService.getStep(id).subscribe(item => this.item = item);
});
} ngOnDestroy() {
this.sub.unsubscribe();
} public editRecipeStep() {
this.AppService.saveStep(this.item).subscribe(
item => { this.item = item; this.router.navigate(['/recipes']); },
error => console.log(error)
)
} }
在这个类中,我们首先调用getStep服务函数来获取配方步骤信息,然后调用saveStep服务函数来更新配方步骤。 一步新组件 右键点击“scripts/app/components”文件夹,添加一个新项目。选择”。Net Core/客户端" TypeScript文件。命名为“step-new.component.ts”。 隐藏,收缩,复制Code
import { Component, OnInit, OnDestroy } from "@angular/core";
import { Router, ActivatedRoute } from "@angular/router";
import { RecipeStep } from "../viewmodels/recipeStep";
import { AppService } from "../services/app.service"; @Component({
selector: 'step-new',
templateUrl: '../partials/addStep.html'
}) export class StepNewComponent implements OnInit {
item: RecipeStep;
sub: any; constructor(private AppService: AppService, private router: Router, private route: ActivatedRoute) { } ngOnInit() {
this.sub = this.route.params.subscribe(params => {
var parentId = params['id'];
this.item = new RecipeStep();
this.item.parentId = parentId;
});
} ngOnDestroy() {
this.sub.unsubscribe();
} public addRecipeStep() {
this.AppService.saveStep(this.item).subscribe(
item => { this.item = item; this.router.navigate(['/recipes']);},
error => console.log(error)
)
} }
在这个类中,我们首先创建一个新步骤,然后调用saveStep服务函数来添加一个配方步骤。 步删除组件 右键点击“scripts/app/components”文件夹,添加一个新项目。选择”。Net Core/客户端" TypeScript文件。命名为“step-delete.component.ts”。 隐藏,收缩,复制Code
import { Component, OnInit, OnDestroy } from "@angular/core";
import { Router, ActivatedRoute } from "@angular/router";
import { RecipeStep } from "../viewmodels/recipeStep";
import { AppService } from "../services/app.service"; @Component({
selector: 'step-delete',
templateUrl: '../partials/deleteStep.html'
}) export class StepDeleteComponent implements OnInit {
item: RecipeStep;
sub: any; constructor(private AppService: AppService, private router: Router, private route: ActivatedRoute) { } ngOnInit() {
this.sub = this.route.params.subscribe(params => {
var id = params['id'];
this.AppService.getStep(id).subscribe(item => this.item = item);
});
} ngOnDestroy() {
this.sub.unsubscribe();
} public deleteStep() {
this.AppService.deleteStep(this.item.id).subscribe(
() => this.router.navigate(['/recipes']),
error => console.log(error)
)
} }
在这个类中,我们首先调用getStep服务函数来获取配方步骤信息,然后调用deleteStep服务函数来删除配方步骤。 项目细节的组件 右键点击“scripts/app/components”文件夹,添加一个新项目。选择”。Net Core/客户端" TypeScript文件。命名为“item-detail.component.ts”。 隐藏,收缩,复制Code
import { Component, OnInit, OnDestroy } from "@angular/core";
import { Router, ActivatedRoute } from "@angular/router";
import { RecipeItem } from "../viewmodels/recipeitem";
import { AppService } from "../services/app.service"; @Component({
selector: 'item-detail',
templateUrl: '../partials/editItem.html'
}) export class ItemDetailComponent implements OnInit {
item: RecipeItem;
sub: any; constructor(private AppService: AppService, private router: Router, private route: ActivatedRoute) { } ngOnInit() {
this.sub = this.route.params.subscribe(params => {
var id = params['id'];
this.AppService.getItem(id).subscribe(item => this.item = item);
});
} ngOnDestroy() {
this.sub.unsubscribe();
} public editRecipeItem() {
this.AppService.saveItem(this.item).subscribe(
item => { this.item = item; this.router.navigate(['/recipes']); },
error => console.log(error)
)
} }
在这个类中,我们首先调用getItem服务函数来获取菜谱项信息,然后调用saveItem服务函数来更新菜谱项。 项新组件 右键点击“scripts/app/components”文件夹,添加一个新项目。选择”。Net Core/客户端" TypeScript文件。命名为“item-new.component.ts”。 隐藏,收缩,复制Code
import { Component, OnInit, OnDestroy } from "@angular/core";
import { Router, ActivatedRoute } from "@angular/router";
import { RecipeItem } from "../viewmodels/recipeItem";
import { AppService } from "../services/app.service"; @Component({
selector: 'item-new',
templateUrl: '../partials/addItem.html'
}) export class ItemNewComponent implements OnInit {
item: RecipeItem;
sub: any; constructor(private AppService: AppService, private router: Router, private route: ActivatedRoute) { } ngOnInit() {
this.sub = this.route.params.subscribe(params => {
var parentId = params['id'];
this.item = new RecipeItem();
this.item.parentId = parentId;
});
} ngOnDestroy() {
this.sub.unsubscribe();
} public addRecipeItem() {
this.AppService.saveItem(this.item).subscribe(
item => { this.item = item; this.router.navigate(['/recipes']);},
error => console.log(error)
)
} }
在这个类中,我们首先创建一个新项,然后调用saveItem服务函数来添加一个菜谱项。 项删除组件 右键点击“scripts/app/components”文件夹,添加一个新项目。选择”。Net Core/客户端" TypeScript文件。命名为“item-delete.component.ts”。 隐藏,收缩,复制Code
import { Component, OnInit, OnDestroy } from "@angular/core";
import { Router, ActivatedRoute } from "@angular/router";
import { RecipeItem } from "../viewmodels/recipeItem";
import { AppService } from "../services/app.service"; @Component({
selector: 'item-delete',
templateUrl: '../partials/deleteItem.html'
}) export class ItemDeleteComponent implements OnInit {
item: RecipeItem;
sub: any; constructor(private AppService: AppService, private router: Router, private route: ActivatedRoute) { } ngOnInit() {
this.sub = this.route.params.subscribe(params => {
var id = params['id'];
this.AppService.getItem(id).subscribe(item => this.item = item);
});
} ngOnDestroy() {
this.sub.unsubscribe();
} public deleteItem() {
this.AppService.deleteItem(this.item.id).subscribe(
() => this.router.navigate(['/recipes']),
error => console.log(error)
)
} }
在这个类中,我们首先调用getItem服务函数来获取菜谱步骤信息,然后调用deleteItem服务函数来删除菜谱项。 更改应用程序模块 Angular模块类描述了应用程序的各个部分是如何组合在一起的。每个应用程序都至少有一个Angular模块,就是你引导来启动应用程序的根模块。你想叫它什么都行。所以我们加载所有创建的组件。 隐藏,收缩,复制Code
///<reference path="../../typings/index.d.ts"/>
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { HttpModule } from "@angular/http";
import { RouterModule } from "@angular/router";
import { FormsModule } from "@angular/forms";
import "rxjs/Rx"; import { AppComponent } from "./app.component";
import { RecipeListComponent } from "./components/recipe-list.component";
import { RecipeDetailComponent } from "./components/recipe-detail.component";
import { RecipeNewComponent } from "./components/recipe-new.component";
import { RecipeDeleteComponent } from "./components/recipe-delete.component";
import { StepDetailComponent } from "./components/step-detail.component";
import { StepNewComponent } from "./components/step-new.component";
import { StepDeleteComponent } from "./components/step-delete.component";
import { ItemDetailComponent } from "./components/item-detail.component";
import { ItemNewComponent } from "./components/item-new.component";
import { ItemDeleteComponent } from "./components/item-delete.component"; import { AppRouting } from "./app.routing";
import { AppService } from "./services/app.service"; @NgModule({
// directives, components, and pipes
declarations: [
AppComponent,
RecipeListComponent,
RecipeDetailComponent,
RecipeNewComponent,
RecipeDeleteComponent,
StepDetailComponent,
StepNewComponent,
StepDeleteComponent,
ItemDetailComponent,
ItemNewComponent,
ItemDeleteComponent,
],
// modules
imports: [
BrowserModule,
HttpModule,
FormsModule,
RouterModule,
AppRouting ],
// providers
providers: [
AppService
],
bootstrap: [
AppComponent
]
})
export class AppModule { }
另外,我们在这里导入route模块。然后我们可以做一个路由配置。 客户端路由配置 一个路由的Angular应用程序有一个单独的路由器服务实例。当浏览器的URL发生变化时,该路由器会查找相应的路由,从而确定要显示的组件。 路由器没有路由器直到您配置它。我们在app.routing.ts中配置客户端路由。 右键点击“scripts/app/components”文件夹,添加一个新项目。选择”。Net Core/客户端" TypeScript文件。命名为“app.routing.ts”。 隐藏,收缩,复制Code
import { ModuleWithProviders } from "@angular/core";
import { Routes, RouterModule } from "@angular/router";
import { RecipeListComponent } from "./components/recipe-list.component";
import { RecipeDetailComponent } from "./components/recipe-detail.component";
import { RecipeNewComponent } from "./components/recipe-new.component";
import { RecipeDeleteComponent } from "./components/recipe-delete.component";
import { StepDetailComponent } from "./components/step-detail.component";
import { StepNewComponent } from "./components/step-new.component";
import { StepDeleteComponent } from "./components/step-delete.component";
import { ItemDetailComponent } from "./components/item-detail.component";
import { ItemNewComponent } from "./components/item-new.component";
import { ItemDeleteComponent } from "./components/item-delete.component"; const routes: Routes = [
{
path: '',
redirectTo: '/recipes',
pathMatch: 'full'
},
{
path: 'recipes',
component: RecipeListComponent
},
{
path: 'recipes/edit/:id',
component: RecipeDetailComponent
},
{
path: 'recipes/add',
component: RecipeNewComponent
},
{
path: 'recipes/delete/:id',
component: RecipeDeleteComponent
},
{
path: 'recipes/editStep/:id',
component: StepDetailComponent
},
{
path: 'recipes/addStep/:id',
component: StepNewComponent
},
{
path: 'recipes/deleteStep/:id',
component: StepDeleteComponent
},
{
path: 'recipes/editItem/:id',
component: ItemDetailComponent
},
{
path: 'recipes/addItem/:id',
component: ItemNewComponent
},
{
path: 'recipes/deleteItem/:id',
component: ItemDeleteComponent
},
]; export const AppRoutingProviders: any[] = [
]; export const AppRouting: ModuleWithProviders = RouterModule.forRoot(routes);
在这里,我们配置数组中的所有路径和组件,然后app module导入这个数组。 菜谱列表模板 隐藏,收缩,复制Code
<div>
<arouterLink="/recipes/add"class="btn breadcrumb m-2">create a new recipe</a>
<div*ngFor="let recipe of items">
<divclass="btn-group tab-pane mb-2">
<buttonclass="btn-info pull-left"(click)="Expand(recipe)"><h5>{{recipe.name}} - {{recipe.comments}}</h5></button>
</div>
<divclass="btn-group">
<arouterLink="/recipes/edit/{{recipe.id}}"class="breadcrumb-item">edit</a>
<arouterLink="/recipes/delete/{{recipe.id}}"class="breadcrumb-item">delete</a>
</div>
<div*ngIf="recipe.show">
<arouterLink="/recipes/addStep/{{recipe.id}}"class="btn breadcrumb m-2">create a new step</a>
<div*ngFor="let step of recipe.recipeSteps">
<divclass="row ml-2">
<divclass="breadcrumb ml-2">
<span>step {{step.stepNo}} : {{step.instructions}}</span>
</div>
<divclass="btn-group m-2">
<arouterLink="/recipes/editStep/{{step.id}}"class="breadcrumb-item">edit</a>
<arouterLink="/recipes/deleteStep/{{step.id}}"class="breadcrumb-item">delete</a>
</div>
</div>
<arouterLink="/recipes/addItem/{{step.id}}"class="btn breadcrumb ml-4">create a new item</a>
<div*ngFor="let item of step.recipeItems">
<divclass="row ml-4">
<divclass="card-text ml-4">
<p> {{item.name}} {{item.quantity}} {{item.measurementUnit}}</p>
</div>
<divclass="btn-group ml-2">
<arouterLink="/recipes/editItem/{{item.id}}"class="breadcrumb-item">edit</a>
<arouterLink="/recipes/deleteItem/{{item.id}}"class="breadcrumb-item">delete</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
锚标记上的RouterLink指令让路由器控制这些元素。导航路径是固定的,因此可以为routerLink分配一个字符串(“一次性”绑定)。 如果导航路径更加动态,则可以将其绑定到返回路由链接参数数组(链接参数数组)的模板表达式。路由器将该数组解析为一个完整的URL。 在菜谱列表模板中,我们同时拥有固定链接和动态链接。我使用ngIf = "食谱。显示,以展开或折叠相应的配方。我需要提到的一点是,对于所有编辑和删除函数,我们传递对象id,但创建新步骤和新项,我们需要传递父对象id,这意味着创建新步骤,我们需要传递菜谱id;创建一个新项,然后需要传递步骤id。显然,创建一个新菜谱不需要传递任何东西。 配方详细模板(edit.html) 右键单击“wwwroot/partials”文件夹,并添加一个新项目。选择”。Net Core/客户端" HTML页面。命名为“edit.html”。 隐藏,复制Code
<divclass="badge badge-info">
<h4>Edit Recipe</h4>
</div>
<div*ngIf="item"class="card-text">
<form(ngSubmit)="editRecipe()">
<divclass="row">
<divclass="col-xl-6 form-group">
<labelfor="name">Name</label>
<input[(ngModel)]="item.name"name="name"type="text"class="form-control"/>
</div>
</div>
<divclass="row">
<divclass="col-xl-6 form-group">
<labelfor="comments">Comments</label>
<input[(ngModel)]="item.comments"name="comments"type="text"class="form-control"/>
</div>
</div>
<divclass="row m-2">
<buttontype="submit"class="btn btn-primary">Save</button>
<arouterLink="/recipes"class="btn btn-default">Cancel</a>
</div>
</form>
</div>
配方详细信息模板实际上是一个提交表单。然而,ngSubmit确保了当处理程序代码抛出(这是提交的默认行为)并导致实际的http post请求时表单不会提交。 为了注册表单控件,我们使用了ngModel指令。通过与name属性的结合,ngModel在幕后为我们创建了一个表单控件抽象。每个注册了ngModel的表单控件都会自动显示在表单中。值,然后可以很容易地用于进一步的后处理。 在这个模板中,ngSubmit与配方细节组件中的eidtRecipe()方法绑定。“取消”按钮就会回到列表中。 Recipe New Template (add.html) 右键点击“wwwroot/partials”文件夹,添加一个新项目。选择”。Net Core/客户端" HTML页面。命名为“add.html”。 隐藏,复制Code
<divclass="badge badge-info">
<h4>Add Recipe</h4>
</div>
<div*ngIf="item"class="card-text">
<form(ngSubmit)="addRecipe()">
<divclass="row">
<divclass="col-xl-6 form-group">
<labelfor="name">Name</label>
<input[(ngModel)]="item.name"name="name"type="text"class="form-control"/>
</div>
</div>
<divclass="row">
<divclass="col-xl-6 form-group">
<labelfor="comments">Comments</label>
<input[(ngModel)]="item.comments"name="comments"type="text"class="form-control"/>
</div>
</div>
<divclass="row m-2">
<buttontype="submit"class="btn btn-primary">Save</button>
<arouterLink="/recipes"class="btn btn-default">Cancel</a>
</div>
</form>
</div>
在这个模板中,ngSubmit与配方新组件中的addRecipe()方法绑定。“取消”按钮就会回到列表中。 菜谱删除模板(Delete .html) 右键单击“wwwroot/partials”文件夹,并添加一个新项目。选择”。Net Core/客户端" HTML页面。命名为“delete.html”。 隐藏,复制Code
<div*ngIf="item"> <divclass="row">
<divclass="alert alert-warning">
<p>Do you really want to delete this recipe?</p>
<p> {{item.name}} - {{item.comments}}</p>
</div>
</div>
<button(click)="deleteRecipe()"class="btn btn-danger">Yes</button>
<arouterLink="/recipes"class="btn btn-default">No</a> </div>
配方删除模板不是一个提交表单。“是”按钮直接调用菜谱删除组件的deleteRecipe()。“否”按钮会回到食谱列表。 步骤详细模板(edit .html) 右键单击“wwwroot/partials”文件夹,并添加一个新项目。选择”。Net Core/客户端" HTML页面。命名为“editStep.html”。 隐藏,复制Code
<divclass="badge badge-info">
<h4>Edit Recipe Step</h4>
</div>
<div*ngIf="item"class="card-text">
<form(ngSubmit)="editRecipeStep()">
<divclass="row">
<divclass="col-xl-6 form-group">
<labelfor="stepNo">Step No.</label>
<input[(ngModel)]="item.stepNo"name="stepNo"type="text"class="form-control"/>
</div>
</div> <divclass="row">
<divclass="col-xl-6 form-group">
<labelfor="instructions">Instructions</label>
<input[(ngModel)]="item.instructions"name="instructions"type="text"class="form-control"/>
</div>
</div>
<divclass="row m-2">
<buttontype="submit"class="btn btn-primary">Save</button>
<arouterLink="/recipes"class="btn btn-default">Cancel</a>
</div>
</form>
</div>
在这个模板中,ngSubmit与Step Detail组件中的editRecipeStep()方法绑定。“取消”按钮就会回到列表中。 新建模板(addStep.html) 右键单击“wwwroot/partials”文件夹,并添加一个新项目。选择”。Net Core/客户端" HTML页面。命名为“addStep.html”。 隐藏,复制Code
<div class="badge badge-info">
<h4>Add a new recipe Step</h4>
</div>
<div *ngIf="item" class="card-text">
<form (ngSubmit)="addRecipeStep()">
<div class="row">
<div class="col-xl-6 form-group">
<label for="stepNo">Step No.</label>
<input [(ngModel)]="item.stepNo" name="stepNo" type="text" class="form-control" />
</div>
</div> <div class="row">
<div class="col-xl-6 form-group">
<label for="instructions">Instructions</label>
<input [(ngModel)]="item.instructions" name="instructions" type="text" class="form-control" />
</div>
</div>
<div class="row m-2">
<button type="submit" class="btn btn-primary">Save</button>
<a routerLink="/recipes" class="btn btn-default">Cancel</a>
</div>
</form>
</div>
在这个模板中,ngSubmit与步骤新组件中的addRecipeStep()方法绑定。“取消”按钮就会回到列表中。 步骤删除模板(deleteStep.html) 右键单击“wwwroot/partials”文件夹,并添加一个新项目。选择”。Net Core/客户端" HTML页面。命名为“deleteStep.html”。 隐藏,复制Code
<div*ngIf="item"> <divclass="row">
<divclass="alert alert-warning">
<p>Do you really want to delete this recipe step?</p>
<p>Step {{item.stepNo}} - {{item.instructions}}</p>
</div>
</div>
<button(click)="deleteStep()"class="btn btn-danger">Yes</button>
<arouterLink="/recipes"class="btn btn-default">No</a> </div>
步骤删除模板不是一个提交表单。“Yes”按钮直接调用步骤删除组件的deleteStep()。“否”按钮会回到食谱列表。 项目细节模板(editItem.html) 右键单击“wwwroot/partials”文件夹,并添加一个新项目。选择”。Net Core/客户端" HTML页面。命名为“editItem.html”。 隐藏,收缩,复制Code
<divclass="badge badge-info">
<h4>Edit Recipe Item</h4>
</div>
<div*ngIf="item"class="card-text">
<form(ngSubmit)="editRecipeItem()">
<divclass="row">
<divclass="col-xl-6 form-group">
<labelfor="name">Name</label>
<input[(ngModel)]="item.name"name="name"type="text"class="form-control"/>
</div>
</div>
<divclass="row">
<divclass="col-xl-6 form-group">
<labelfor="quantity">Quantity</label>
<input[(ngModel)]="item.quantity"name="quantity"type="text"class="form-control"/>
</div>
</div>
<divclass="row">
<divclass="col-xl-6 form-group">
<labelfor="measurementUnit">Measurement Unit</label>
<input[(ngModel)]="item.measurementUnit"name="measurementUnit"type="text"class="form-control"/>
</div>
</div>
<divclass="row m-2">
<buttontype="submit"class="btn btn-primary">Save</button>
<arouterLink="/recipes"class="btn btn-default">Cancel</a>
</div>
</form>
</div>
在这个模板中,ngSubmit与项目细节组件中的editRecipeItem()方法绑定。“取消”按钮就会回到列表中。 项目新模板(addItem.html) 右键单击“wwwroot/partials”文件夹,并添加一个新项目。选择”。Net Core/客户端" HTML页面。命名为“addItem.html”。 隐藏,收缩,复制Code
<divclass="badge badge-info">
<h4>Add a new recipe Item</h4>
</div>
<div*ngIf="item"class="container-fluid">
<form(ngSubmit)="addRecipeItem()">
<divclass="row">
<divclass="col-xl-6 form-group">
<labelfor="name">Name</label>
<input[(ngModel)]="item.name"name="name"type="text"class="form-control"/>
</div>
</div>
<divclass="row">
<divclass="col-xl-6 form-group">
<labelfor="quantity">Quantity</label>
<input[(ngModel)]="item.quantity"name="quantity"type="text"class="form-control"/>
</div>
</div>
<divclass="row">
<divclass="col-xl-6 form-group">
<labelfor="measurementUnit">Measurement Unit</label>
<input[(ngModel)]="item.measurementUnit"name="measurementUnit"type="text"class="form-control"/>
</div>
</div>
<divclass="row m-2">
<buttontype="submit"class="btn btn-primary">Save</button>
<ahref="/"class="btn btn-default">Cancel</a>
</div>
</form>
</div>
在这个模板中,ngSubmit与Item New组件中的addRecipeItem()方法绑定。“取消”按钮就会回到列表中。 项目删除模板(deleteItem.html) 右键单击“wwwroot/partials”文件夹,并添加一个新项目。选择”。Net Core/客户端" HTML页面。命名为“deleteItem.html”。 隐藏,复制Code
<div*ngIf="item">
<divclass="row">
<divclass="alert alert-warning">
<p>Do you really want to delete this recipe item?</p>
<p> {{item.name}} {{item.quantity}} {{item.measurementUnit}}</p>
</div>
</div>
<button(click)="deleteItem()"class="btn btn-danger">Yes</button>
<arouterLink="/recipes"class="btn btn-default">No</a>
</div>
项目删除模板不是一个提交表单。Yes按钮呼叫de项目直接删除组件的leteItem()。“否”按钮会返回到食谱列表。 添加基本标签 我们需要设置基标签,因为它将告诉路由引擎如何组合所有我们的应用程序最终将拥有的导航url。 我们在索引中添加基标签。html,它在wwwroot文件夹下。 隐藏,收缩,复制Code
<html>
<head>
<basehref="/">
<title>Master Chef2</title>
<metaname="viewport"content="width=device-width, initial-scale=1"> <!-- Step 1. Load libraries -->
<!-- Polyfill(s) for older browsers -->
<scriptsrc="js/shim.min.js"></script>
<scriptsrc="js/zone.js"></script>
<scriptsrc="js/Reflect.js"></script>
<scriptsrc="js/system.src.js"></script> <!-- Angular2 Native Directives -->
<scriptsrc="/js/moment.js"></script> <!-- Step 2. Configure SystemJS -->
<scriptsrc="systemjs.config.js"></script>
<script>
System.import('app').catch(function (err) { console.error(err); });
</script>
<linkhref="lib/bootstrap/dist/css/bootstrap.min.css"rel="stylesheet"media="screen">
</head>
<!-- Step 3. Display the application -->
<body>
<divclass="container-fluid">
<!-- Application PlaceHolder -->
<masterchef2>Please wait...</masterchef2>
</div>
</body>
</html>
Angular 2 Typescript找不到名字 当我构建解决方案时,我得到了许多编译错误。例如,错误TS2304: Build:不能找到名称“Promise”。 有两种方法可以修复它。 将飞越器的目标从ES5切换到ES6。为此,更改您的tsconfig。json文件匹配以下值: 隐藏,复制代码{ “compileOnSave”:假的, " compilerOptions ": { “emitDecoratorMetadata”:没错, “experimentalDecorators”:没错, “模块”:“系统”, “moduleResolution”:“节点”, “noImplicitAny”:假的, “noEmitOnError”:假的, “removeComments”:假的, “sourceMap”:没错, “目标”:“es6” }, “排除”:( “node_modules”, “wwwroot” ] } 然而,这样做可能会带来一些问题:你可能无法使用一些还不支持ES6的工具/包/库,比如UglifyJS。 安装类型和core-js类型定义文件。坦克兵的目标仍然是ES5。 隐藏,复制代码{ “compileOnSave”:假的, " compilerOptions ": { “emitDecoratorMetadata”:没错, “experimentalDecorators”:没错, “模块”:“系统”, “moduleResolution”:“节点”, “noImplicitAny”:假的, “noEmitOnError”:假的, “removeComments”:假的, “sourceMap”:没错, “目标”:“es5” }, “排除”:( “node_modules”, “wwwroot” ] } 打开包。json文件(枚举NPM包的那个),并检查类型包是否已经出现在dependencies或devDependencies节点中,以及在脚本块的后安装阶段运行它所需的脚本。如果它们不在这里,添加它们,使您的文件看起来如下: 隐藏,收缩,复制代码{ “版本”:“1.0.0”, “名称”:“asp.net”, “依赖”:{ “@angular /普通”:“2.0.0”, “@angular /编译器”:“2.0.0”, “@angular /核心”:“2.0.0”, “@angular /形式”:“2.0.0”, “@angular / http”:“2.0.0”, :“@angular / platform-browser 2.0.0”, :“@angular / platform-browser-dynamic 2.0.0”, “@angular /路由器”:“3.0.0”, “@angular /升级”:“2.0.0”, :“core-js ^ 2.4.1”, :“reflect-metadata ^ 0.1.8”, :“rxjs 5.0.0-rc.4”, :“systemjs ^ 0.19.41”, “输入”:“^ 1.3.2”, ”区。js”:“^ 0.7.2”, “时刻”:“^ 2.17.0” }, " devDependencies ": { “吞咽”:“^ 3.9.1”, :“gulp-clean ^ 0.3.2”, :“gulp-concat ^ 2.6.1”, :“gulp-less ^ 3.3.0”, :“gulp-sourcemaps ^ 1.9.1”, :“gulp-typescript ^ 3.1.3”, :“gulp-uglify ^ 2.0.0”, :“打印稿^ 2.0.10” }, "脚本":{ "postinstall": "typings install dt~core-js@^0.9.7 -global" } } 请注意,我们必须指定版本为“0.9.7”,否则会安装最新版本,仍然会造成问题。现在,ES6 TypeScript包应该可以顺利编译了。 运行应用程序 首先,重新构建解决方案。然后转到任务运行器资源管理器窗口运行默认任务。 完成所有任务后,点击“IIS Express”。 加入一个新食谱——麻婆豆腐。 保存后,可以为每个步骤添加步骤和项。 在谷歌Chrome中调试Angular代码 虽然Angular 2是TypeScript,但所有的TypeScript文件都被gulp task转换成JavaScript的minify文件。请看下面的截图,相应的JavaScript文件是在wwwroot/app文件夹下创建的。 因此您不能直接调试TypeScript。幸运的是,我们可以转而调试JavaScript文件。 点击“IIS Express”下拉按钮,选择浏览器的谷歌Chrome。然后单击“IIS Express”启动应用程序。应用程序启动后,在谷歌Chrome的“更多工具”中点击“Developer Tools”。然后单击“来源”。现在您可以使用树视图查看所有JavaScript文件。挑选您想要调试的任何JavaScript文件。这里我们以删除食谱为例。所以我取了“recipe-delete.component.js”。 正如我所说的,所有JavaScript文件都是用minify样式创建的,这很难阅读。不过别担心,Chrome可以帮你把这个小文件还原成普通文件。只要点击中间窗口左下角的“{}”,缩小文件就会变成“漂亮打印”文件。我将break品脱放在deleteRecipe()函数上。 点击食谱旁边的“删除”按钮。应用程序显示菜谱删除模板。 然后单击“Yes”以触发断点,您就可以看到您感兴趣的变量。 将断点放在app.service.js的deleteRecipe函数上。然后点击“Resume script”按钮或者按F8, app.service.js的断点也会被触发。 在App服务中,它调用服务器端web API。如果您将断点放置在服务器端Http Delete方法上,那么在恢复脚本时服务器端断点将被触发。 结论 在这些文章中,我已经向你展示了如何在ASP环境下构建Angular 2 CRUD SPA。净的核心。我们还学习了如何使用Angular 2的Route来导航到不同的组件和模板。由于Angular 4已经在3月份发布,Master Chef将会被转移到Visual Studio 2017,在下一篇文章中会提到Angular 4。 我已经在github中创建了一个公共存储库,即Master Chef存储库。请随时参与开发工作。 本文转载于:http://www.diyabc.com/frontweb/news18958.html
主厨(第4部分)- ASP. netNET Core和Angular 2 CRUD SPA的更多相关文章
- 学习ABP ASP.NET Core with Angular 环境问题
1. 前言 最近学习ABP架构 搭建ASP.NET Core with Angular遇到了些问题,折腾了一个礼拜最终在今天解决了,想想这个过程的痛苦就想利用博客记录下来.其实一直想写博客,但因为 时 ...
- 从零开始一个个人博客 by asp.net core and angular(一)
这是一个个人叙述自己建设博客的帖子,既然是第一篇那肯定是不牵扯代码了,主要讲一下大体的东西,微软最新的web框架应该就数asp.net core 3.1了这是一个长期支持版,而且是跨平台又开源版本,所 ...
- ASP.NET Core Web API Cassandra CRUD 操作
在本文中,我们将创建一个简单的 Web API 来实现对一个 “todo” 列表的 CRUD 操作,使用 Apache Cassandra 来存储数据,在这里不会创建 UI ,Web API 的测试将 ...
- ASP.NET Core和Angular 2双剑合璧
(此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:两个还没有正式发布的东西一起用,是什么效果? 效果当然会很好了(我猜的),那么如何在A ...
- 从零开始一个个人博客 by asp.net core and angular(三)
这是第三篇了,第一篇只是介绍,第二篇介绍了api项目的运行和启动,如果api项目没什么问题了,调试都正常了,那基本上就没什么事了,由于这一篇是讲前端项目的,所以需要运行angular项目了,由于前端项 ...
- 从零开始一个个人博客 by asp.net core and angular(二)
上一篇帖子讲了用了哪些技术,这个帖子就先介绍介绍api项目吧,项目就是一个普通的webapi项目,账户系统用的identity ,什么是identity呢? 其实就是官方封装好的一系列的可以用来操作数 ...
- 52ABP模板 ASP.Net Core 与 Angular的开源实例项目
阅读文本大概需要 5 分钟. 开始之前 自从上一篇文章".NET:持续进化的统一开发平台"发布后,已经有三个月的时间没有写过文章了. 这段时间,做了两场线下活动,一场在上海,一场在 ...
- 在ASP dot Net Core MVC中用Controllers调用你的Asp dotnet Core Web API 实现CRUD到远程数据库中,构建你的分布式应用(附Git地址)
本文所有的东西都是在dot Net Core 1.1环境+VS2017保证测试通过. 本文接着上次文章接着写的,不了解上篇文章的可能看着有点吃力.我尽量让大家都能看懂.这是上篇文章的连接http:// ...
- ASP.NET Core文章汇总
现有Asp.Net Core 文章资料,2016 3-20月汇总如下 ASP.NET Core 1.0 与 .NET Core 1.0 基础概述 http://www.cnblogs.com/Irvi ...
随机推荐
- 20190923-02Linux文件目录类 000 010
pwd 显示当前工作目录的绝对路径 pwd:print working directory 打印工作目录 1.基本语法 pwd (功能描述:显示当前工作目录的绝对路径) 2.案例实操 (1)显示当前工 ...
- Java多线程--两个线程同时对一个人的年龄进行增加和修改
public class Thread_A extends Thread { Human human; public Thread_A(String name, Human human) { supe ...
- git多账号使用
1 背 景 在公司上班的员工会同时拥有两个git账号, 一个是公司内部的, 仅允许工作时使用; 另一个是个人的, 常用于日常的学习记录. 此时, 面临的问题是如何在一台电脑(客户端)上正常使用两个账号 ...
- 关于在异步操作中访问React事件对象的小问题
最近撸React的代码时踩了个关于事件处理的坑,场景如下:在监听某个元素上会频繁触发的事件时,我们往往会对该事件的回调函数进行防抖的处理:防抖的包装函数大致长这样: debounce = (fn, d ...
- (超详细)动手编写 — 栈、队列 ( Java实现 )
目录 前言 栈 概念 栈的设计 编码实现 小结 队列 概念 队列的设计 编码实现 双端队列 概念 设计 编码 循环队列 循环队列 循环双端队列 声明 前言 栈 概念 什么是栈? **栈 **:是一种特 ...
- Superset 0.37 发布——颜值最高的数据可视化平台
Superset 0.37,增加可视化插件,行级权限控制 使用Superset已经有一段时间,其良好的体验与丰富的图表功能节省了大量的时间.但是对于权限,自定义图表,图表下载,报警邮件一直没有很好的支 ...
- 论文:Bottom-Up and Top-Down Attention for Image Captioning and Visual Question Answering-阅读总结
Bottom-Up and Top-Down Attention for Image Captioning and Visual Question Answering-阅读总结 笔记不能简单的抄写文中 ...
- 理解C#中的ExecutionContext vs SynchronizationContext
原文:https://devblogs.microsoft.com/pfxteam/executioncontext-vs-synchronizationcontext/ 作者:Stephen 翻译: ...
- [剑指Offer]66-构建乘积数组
题目 给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]A[1]...A[i-1]A[i+1]...A[n-1].不能使用除法. 题 ...
- HTTP 【值得你看个究竟】
我是一名程序员,我的主要编程语言是 Python,我更是一名 Web 开发人员,所以我必须要了解 HTTP,所以本篇文章就来带你从 HTTP 入门到进阶,看完让你有一种恍然大悟.醍醐灌顶的感觉. 认识 ...