zoukankan      html  css  js  c++  java
  • 厨师长(第二部分)NET Core MVC与Fluent NHibernate和AngularJS

    在Maser Chef第1部分中,我介绍了如何集成ASP。NET Core MVC与Fluent NHibernate和Angular JS。在这篇文章中,我将讨论如何使用ASP。NET Core MVC, Fluent NHibernate和Angular JS来实现一个CRUD SPA(单页应用)。 在存储库中使用泛型 创建、读取、更新和删除(CRUD的首字母缩写)是持久性存储的四个基本功能。 我们需要首先在我们的repository类的数据库级别上实现CRUD。我希望在查询、添加、更新、删除方法中使用泛型,以避免冗余编码。为什么使用泛型?简短的回答是,类型安全、编译时检查、更快并且适用于具有相同底层行为的许多类型。 在以前的数据模型类中,所有成员都具有与数据库字段相同的名称。实际上,数据模型类成员不必与数据库字段相同。例如,Recipe类的Id不必是RecipeId,它可以是任何名称,比如Id。我们需要做的是在映射过程中告诉Fluent NHibernate,如下所示。 隐藏,复制Code

    Id(x => x.Id, "RecipeId");

    通过这种方式,Fluent NHibernate知道它在映射“Id”到“RecipeId”。 因为我们不必使用相同的名称作为数据库字段,现在我们有机会改变不同的数据模型类,以拥有一些共同的成员。 我们创建了一个基类实体。 隐藏,复制Code

        public class Entity
        {
            public virtual Guid Id { get; set; }
            public virtual Guid? ParentId { get; set; }
            public virtual Type ParentType => null;
    }

    然后将Recipe, RecipeStep和RecipeItem派生Entity,将Recipe的RecipeId替换为Id,将RecipeStep的RecipeStepId替换为Id,将RecipeItem的ItemId替换为Id,将RecipeStep的RecipeId替换为ParentId,将RecipeItem的RecipeStepId替换为ParentId。 隐藏,复制Code

     public class Recipe : Entity
        {
            public virtual string Name { get; set; }
            public virtual string Comments { get; set; }
            public virtual DateTime ModifyDate { get; set; }
            public virtual IList<RecipeStep> Steps { get; set; }
    }
    
    public class RecipeStep : Entity
        {
            public virtual int StepNo { get; set; }
            public virtual string Instructions { get; set; }
            public virtual IList<RecipeItem> RecipeItems { get; set; }
            public override Type ParentType => typeof(Recipe);
        }
    public class RecipeItem : Entity
        {
            public virtual string Name { get; set; }
            public virtual decimal Quantity { get; set; }
            public virtual string MeasurementUnit { get; set; }
            public override Type ParentType => typeof(RecipeStep);
        }

    现在我们还需要更改映射类。请注意不同名称的映射。 隐藏,收缩,复制Code

    public class RecipeMap : ClassMap<Recipe>
        {
            public RecipeMap()
            {
                Id(x => x.Id, "RecipeId");
                Map(x => x.Name);
                Map(x => x.Comments);
                Map(x => x.ModifyDate);
                HasMany(x => x.Steps).KeyColumn("RecipeId").Inverse().Cascade.DeleteOrphan().OrderBy("StepNo Asc");
                Table("Recipes");
            }
    }
    public class RecipeStepMap : ClassMap<RecipeStep>
        {
            public RecipeStepMap()
            {
                Id(x => x.Id, "RecipeStepId");
                Map(x => x.ParentId, "RecipeId");
                Map(x => x.StepNo);
                Map(x => x.Instructions);
                HasMany(x => x.RecipeItems).KeyColumn("RecipeStepId").Inverse().Cascade.DeleteOrphan();
                Table("RecipeSteps");
            }
        }
    public class RecipeItemMap : ClassMap<RecipeItem>
        {
            public RecipeItemMap()
            {
                Id(x => x.Id, "ItemId");
                Map(x => x.Name);
                Map(x => x.Quantity);
                Map(x => x.MeasurementUnit);
                Map(x => x.ParentId, "RecipeStepId");
                Table("RecipeItems");
            }
        }

    “Cascade.DeleteOrphan”是什么?此选项在删除父对象时删除子对象。对于我们的示例,删除一个配方将删除该配方的所有配方步骤和配方项,删除一个步骤将删除该步骤的所有项。 然后将Repository的方法改为泛型方法,并放入泛型约束,即T必须是Entity的子类。 隐藏,收缩,复制Code

    public T GetEntity<T>(Guid id) where T : Entity
            {
                try
                {
                    return _session.Get<T>(id);
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
    
            public T AddEntity<T>(T entity) where T : Entity
            {
                T newOne = null;
                using (var transaction = _session.BeginTransaction())
                {
                    try
                    {
                        _session.SaveOrUpdate(entity);
                        Commit(transaction, entity);
                        RefreshParentObject(entity);
                        newOne = _session.Get<T>(entity.Id) as T;
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
    
                    return newOne;
                }
            }
    
            public void UpdateEntity<T>(T entity) where T : Entity
            {
                using (var transaction = _session.BeginTransaction())
                {
                    try
                    {
                        _session.Update(entity);
                        Commit(transaction, entity);
                        RefreshParentObject(entity);
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }
    
                }
            }
    
            public void DeleteEntity<T>(Guid id) where T : Entity
            {
                using (var transaction = _session.BeginTransaction())
                {
                    var entity = _session.Get<T>(id);
                    if (entity != null)
                    {
                        try
                        {
                            _session.Delete(entity);
                            Commit(transaction, entity);
                            RefreshParentObject(entity);
                        }
                        catch (Exception ex)
                        {
                            throw ex;
                        }
                    }
                }
            }

    对于添加、更新和删除方法,所有调用RefreshParentObject()。这是什么意思?当我们改变RecipeStep或RecipeItem时,它的父对象缓存并不知道这个改变。我们需要刷新父对象缓存。 隐藏,复制Code

    void RefreshParentObject(Entity entity)
        {
            if (!entity.ParentId.HasValue)
                return;
            var parentObj = _session.Get(entity.ParentType, entity.ParentId.Value);
            if (parentObj != null)
                _session.Refresh(parentObj);
        }
    

    现在我们更新web API控制器。 隐藏,收缩,复制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.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>(recipe);
        return new ObjectResult(existingOne);
    }
    
    [HttpDelete("{id}")]
    public IActionResult Delete(Guid id)
    {
        _repository.DeleteEntity<Recipe>(id);
        return new StatusCodeResult(200);
    }
    

    角端路由 现在,我们需要在Master Chef应用程序中设置客户机路由,以便根据客户机提供的URL替换动态视图。我们可以从角度路由模块中获取角度路由特征。 使用ngRoute模块,您可以在单个页面应用程序中导航到不同的页面,而无需重新加载页面。$route用于将url深链接到控制器和视图(HTML部分)。它监视$location.url()并尝试将该路径映射到现有的路由定义。 $route中有两个依赖项,即$location和$routeParams。 1)注入ngRoute 打开app.js,在masterChefApp模块中注入ngroute。 隐藏,复制Code

    (function () {
        'use strict';
    
        angular.module('masterChefApp', [
            // Angular modules 
            'ngRoute',
    
            // Custom modules 
            'recipesService'
            // 3rd Party Modules
            
        ]);
    })();

    2)配置Angular路由 为我们的Angular app模块定义一个配置函数——masterChefApp。并且,在该配置函数中,使用来自ngRoute模块的路由提供程序服务来定义客户端路由 隐藏,复制Code

    angular.module('masterChefApp').config(['$routeProvider', '$locationProvider', function ($routeProvider, $locationProvider) {
            $routeProvider
            .when('/', {
                templateUrl: 'partials/recipes.html',
                controller: 'recipesController'
            })
            .when('/recipes/add', {
                templateUrl: 'partials/add.html',
                controller: 'recipesAddController'
            })
            .when('/recipes/edit/:id', {
                templateUrl: 'partials/edit.html',
                controller: 'recipesEditController'
            })
            .when('/recipes/delete/:id', {
                templateUrl: 'partials/delete.html',
                controller: 'recipesDeleteController'
            });
    
            $locationProvider.html5Mode(true);
    
    }]);

    第一个只是一个默认的路由-正斜杠。第二个是/recipes/add。第三个是/recipes/edit/,并传递:id作为路由参数,它允许我们采用一个动态id,我可以匹配其中一个菜谱。最后一个route /recipes/delete/:id也需要采用动态id参数。这个默认路由只会列出所有的菜谱。“添加”路由处理添加,“编辑”路由处理编辑或更新,“删除”路由处理删除或删除。CRUD函数由这四个客户端路由表示。对于每个路由,我们需要定义一个模板URL(它表示一些应该为此路由呈现的HTML)和一个单独的控制器(它将处理此路由)。 在最底部,使用$locationProvider,它的html5Mode函数,设置为true,以确保我可以使用友好和自然的url,避免使用hash bangs进行客户端路由。 Angular JS客户端控制器 我们已经配置了默认路由、添加路由、编辑路由和删除路由。然后需要相应的控制器,recipesController、recipesAddController、recipesEditController和recipesDeleteController。我们在recipesController.js中定义了所有这些控制器。 1)注入“添加”、“编辑”和“删除”控制器 隐藏,复制Code

    angular
            .module('masterChefApp')
            .controller('recipesController', recipesController)
            .controller('recipesAddController', recipesAddController)
            .controller('recipesEditController', recipesEditController)
            .controller('recipesDeleteController', recipesDeleteController);

    2)实现食谱添加控制器 隐藏,复制Code

    recipesAddController.$inject = ['$scope', 'Recipe', '$location'];
        function recipesAddController($scope, Recipe, $location) {
            $scope.recipe = new Recipe();
            $scope.addRecipe = function () {
                $scope.recipe.$save(function () {
                    $location.path('/');
                });
            }
        }

    因此,recipesAddController需要一个$作用域和食谱服务,它还需要$location服务。recipesAddController创建或提供了允许用户向应用程序添加菜谱的功能。为此,使用菜谱服务创建一个新的$scope变量recipe。它还在这里创建了一个$scope函数——addRecipe,该函数将使用recipe services保存方法向服务器提交菜谱。在提交食谱后的回调中,我们将把应用程序重定向到它的主页。 3)实现菜谱编辑控制器 隐藏,复制Code

    recipesEditController.$inject = ['$scope', 'Recipe', '$location', '$routeParams'];
        function recipesEditController($scope, Recipe, $location, $routeParams) {
            $scope.recipe = Recipe.get({ id: $routeParams.id });
            $scope.editRecipe = function () {
                $scope.recipe.$save(function () {
                    $location.path('/');
               });
            }
    }

    recipesEditController需要一个$作用域和菜谱服务$location服务。它还需要$routeParameter来传递id. recipesEditController创建或提供允许某人向应用程序更新菜谱的功能。我们将使用routeParams服务来更新菜谱。通过从route参数获取菜谱的ID。然后,我们将进入服务器,通过调用菜谱服务get函数获取适当的菜谱——这次是提供ID的get方法。该ID将被提供给前端。用户可以做出任何。 最后,我们将更新后的食谱记录提交给服务器。 4)实现菜谱删除控制器 隐藏,复制Code

    recipesDeleteController.$inject = ['$scope', 'Recipe', '$location', '$routeParams'];
        function recipesDeleteController($scope, Recipe, $location, $routeParams) {
            $scope.recipe = Recipe.get({ id: $routeParams.id });
            $scope.deleteRecipe = function () {
                $scope.recipe.$remove({ id: $scope.recipe.id }, function () {
                    $location.path('/');
                });
            };
    }

    recipesDeleteController使用$routeParams获取ID并检索特定的菜谱。然后提供这个函数deleteRecipe,在这个函数中我们可以使用菜谱服务的$remove方法告诉服务器我们想要删除一个特定的菜谱。 局部视图模板 1)修改Index.html使用ng-view 修改index.html以使用部分视图。首先添加一个“base”标签和它的href属性到/。这对于$locationProvider能够正常工作是必要的,因为它需要一个基础。现在转到正文内容。摆脱所有这些,只需使用ng-view指令。 隐藏,复制Code

    <!DOCTYPEhtml>
    <htmlng-app="masterChefApp">
    <head>
        <basehref="/">
        <metacharset="utf-8"/>
        <title>Master Chef Recipes</title>
        <scriptsrc="lib/angular/angular.min.js"></script>
        <scriptsrc="lib/angular-resource/angular-resource.min.js"></script>
        <scriptsrc="lib/angular-route/angular-route.min.js"></script>
        <scriptsrc="app.js"></script>
        </head>
    <bodyng-cloak>
        <div>
            <ng-view></ng-view>
        </div>
    </body>
    </html>

    基于这个ng-view指令和我们已经设置的路由的使用,ng-view将能够交付正确的部分视图和正确的控制器,以在客户端路由上使用$routeProvider来为视图提供电源。 我们在app.js文件中指定了四个控制器。这些控制器给我们CRUD操作。route URL /将从服务器检索所有菜谱。/recipes/add将创建一个新菜谱。使用变量id的recipes/edit将更新现有的菜谱,而/recipes/delete也使用变量id将从服务器删除或删除特定的菜谱。 现在我们在wwwroot文件夹下创建“partials”文件夹。然后可以逐个添加模板。 2)检索模板—Recipes.html 右键点击wwwroot下的“partials”文件夹。添加一个新项目。在客户端模板部分,选择HTML Page。我们给它起名叫“recipes.html”。 html,它将检索并显示菜谱列表。 隐藏,收缩,复制Code

    <div>
        <h2>Master Chief Recipes</h2>
        <ul>
            <ling-repeat="recipe in recipes">
                <div>
                   <h5>{{recipe.name}} - {{recipe.comments}}</h5>
                </div>
                <div>
                    <ahref="recipes/edit/{{recipe.id}}">edit</a>
                </div>
                <div>
                    <ahref="recipes/delete/{{recipe.id}}">delete</a>
                </div>
                <ul>
                    <ling-repeat="step in recipe.steps">
                        <p> step {{step.stepNo}} : {{step.instructions}}</p>
                        <ul>
                            <ling-repeat="item in step.recipeItems">
                                <p> {{item.name}}  {{item.quantity}} {{item.measurementUnit}}</p>
                            </li>
                        </ul>
                    </li>
                </ul>
            </li>
        </ul>
        <p><ahref="recipes/add"> Add a new recipe </a></p>
    </div>

    请注意,这不是完整的html。我们只是定义了一个部分视图它将在AngularJS应用中被替换。 现在如果我们运行它,我们应该看到所有的食谱。 3)引导风格 虽然它工作,但它是一个完全普通的html。所以我们需要应用一些CSS样式。 Bootstrap是一个非常流行的前端框架,它包括基于HTML和CSS的排版设计模板,表单,按钮,表格,导航,模板,图像旋转木马和许多其他的,以及可选的JavaScript插件。应用bootstrap样式可以使我们的master chef web应用程序更漂亮。 我们已经在bower配置中添加了引导包。 隐藏,复制Code

     {
    	"name": "asp.net",
    	"private": true,
      "dependencies": {
        "jquery": "3.1.0",
        "bootstrap": "3.1.0",
        "angular": "1.5.8",
        "angular-route": "1.5.8",
        "angular-resource": "1.5.8"
      }
    }

    所以bootstrap已经安装在wwwrootlib文件夹中。现在我们将它包含在index.html中。 隐藏,复制Code

    <link href="lib/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" media="screen">

    我们将应用下面的引导样式。 我们在index.html中应用主div与.container-fluid(全宽度)来进行适当的对齐和填充。 我们为ui应用所有的.list-group,为recipes.html中的li应用.list-group-item。我们还为添加链接应用“btn-primary”,为编辑链接应用“btn-default”,为删除链接应用“btn-delete”。我还想把recipe显示为一个徽章,所以也要应用。badge样式。 再跑一次大厨,看看现在是什么样子。 Bootstrap包括一个强大的移动优先网格系统,用于构建各种形状和大小的布局。它基于12列布局,有多个层次,每个媒体查询范围一个。有三个主要组件——容器、行和列。容器-。固定宽度的容器或。容器-流体的全宽度-中心你的网站内容,并帮助对齐你的网格内容。行是列的水平分组,确保列被正确地排列。列类表示您希望在每行可能的12列中使用的列数。如果你想要三个等宽的列,你可以用。cole -xs-4。 我们使用bootstrap网格系统中的主厨模板。 4)使用Angular JS实现展开/折叠 我知道有很多方法可以用jQuery展开/折叠来改变DOM。记住,我们使用的是MVVM模式。所以我热衷于通过改变控制器(视图模型)中的模型来实现展开/折叠。 在recipesController中添加expand()函数。在expand()函数中,我们设置了recipe对象的show属性。 隐藏,复制Code

    recipesController.$inject = ['$scope', 'Recipe'];
    
        function recipesController($scope, Recipe) {
            $scope.recipes = Recipe.query();
            $scope.expand = function (recipe) {
                recipe.show = !recipe.show;
            }
    }

    我们在recipesController中添加了一个ng-click来调用expand()函数。 隐藏,复制Code

     <divclass="btn-group">
                   <buttonclass="btn badge pull-left"ng-click="expand(recipe)"><h5>{{recipe.name}} - {{recipe.comments}}</h5></button>
    </div>
    

    然后我们使用ng-show来控制是否显示菜谱的详细信息。 隐藏,复制Code

    <ul class="list-group" ng-show="recipe.show">
                    <li ng-repeat="step in recipe.steps" class="list-group-item">

    只需单击recipe badge扩展您想要查看的内容。 5)创建模板- add.html 右键单击wwwroot下的“partials”文件夹。添加一个新项目。在客户端模板部分,选择HTML Page。我们将其命名为“add.html”。 在add.html中,使用ng-submit将数据发送到服务器。我们将通过ng-model指令将用户输入到输入字段中的信息绑定到一个范围变量菜谱。当用户按下Save按钮使用表单提交时,我们会调用作用域函数addRecipe它会在控制器中后台将recipe对象提交给服务器。 隐藏,复制Code

    <h1>Add a new recipe</h1>
    <divclass="container-fluid">
        <formng-submit="addRecipe()">
            <divclass="row">
                <divclass="form-group col-xs-4">
                    <labelfor="name">Name</label>
                    <inputng-model="recipe.name"name="name"type="text"class="form-control"/>
                </div>
            </div>
            <divclass="row">
                <divclass="form-group col-md-4 col-xs-8">
                    <labelfor="comments">Comments</label>
                    <inputng-model="recipe.comments"name="comments"type="text"class="form-control"/>
                </div>
            </div>
            <divclass="row">
                <buttontype="submit"class="btn btn-primary">Save</button>
                <ahref="/"class="btn btn-default">Cancel</a>
            </div>
        </form>
    </div>

    编辑模板- Edit .html 右键单击wwwroot下的“partials”文件夹。添加一个新项目。在客户端模板部分,选择HTML Page。我们提供一个名称“edit.html”。 现在我们要更新一个食谱。我们将在edit.html部分模板中处理这个问题。edit.html看起来像add.html,因为我们需要为最终用户提供所有必要的字段,以实际更新现有菜谱。我们有recipe.name和recipe.comments的输入。它们通过ng-model指令被绑定到一个范围变量——一个对象配方。此外,在编辑控制器上有一个作用域函数——editRecipe。当用户在编辑中按下Save按钮时。html中,该函数将被调用,而将更新的菜谱信息提交到服务器进行持久存储是该函数的工作。 隐藏,复制Code

    <h1>Edit recipe</h1>
    <divclass="container-fluid">
        <formng-submit="editRecipe()">
            <divclass="row">
                <divclass="form-group col-xs-4">
                    <labelfor="name">Name</label>
                    <inputng-model="recipe.name"name="name"type="text"class="form-control"/>
                </div>
            </div>
            <divclass="row">
                <divclass="form-group col-md-4 col-xs-8">
                    <labelfor="comments">Comments</label>
                    <inputng-model="recipe.comments"name="comments"type="text"class="form-control"/>
                </div>
            </div>
            <divclass="row">
                <buttontype="submit"class="btn btn-primary">Save</button>
                <ahref="/"class="btn btn-default">Cancel</a>
            </div>
        </form>
    </div>

    7)删除模板 右键点击wwwroot下的“partials”文件夹。添加一个新项目。在客户端模板部分,选择HTML Page。我们将其命名为“delete.html”。 在删除。html,我们会提供一个段落来进行确认。那么你真的想删除这个食谱吗?我们将绑定到有问题的食谱信息也就是要删除的食谱。我们将提供一个按钮,它调用一个作用域函数- deleteRecipe。它将向服务器提交一个请求,以删除特定的菜谱。 隐藏,复制Code

    <div class="alert alert-warning">
        <p>Do you really want to delete this recipe?</p>
        <p> {{recipe.name}} - {{recipe.comments}}</p>
    </div>
    <button ng-click="deleteRecipe()" class="btn btn-danger">Yes</button>
    <a href="/" class="btn btn-default">No</a>

    多个URL映射到同一个Web API控制器 那食谱步骤和食谱项呢?一般来说,我们可以创建单独的API控制器来处理菜谱步骤和菜谱项。但是它太重了。我想把所有与菜谱相关的restful服务打包到RecipesController中。但是对于配方步骤操作和配方项操作,它肯定需要不同的url。幸运的是,ASP。NET Core Web API支持不同的路由。路由是Web API将URI匹配到操作的方式。Web API支持一种新的路由类型,称为属性路由。顾名思义,属性路由使用属性来定义路由。属性路由使您可以对web API中的uri进行更多的控制。例如,您可以轻松地创建描述资源层次结构的uri。 web控制器类的route属性是基URI。 隐藏,复制Code

    [Route("api/[controller]")]
        public class RecipesController : Controller
    {
    ….
    }

    对于RecipesController,基本URL是/api/recipes。 隐藏,复制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();
    
            }

    上面的方法没有route属性,这意味着这个方法被映射到/api/recipes/:id 但是对于get step方法和get item方法,我们需要不同的URL。我想获得步骤URL是/api/recipes/step/:id和项目URL是/api/recipes/item/:id。因此,我们为get step方法添加[Route("step/{id}")],为get item方法添加[Route("item/{id}")]。 隐藏,复制Code

    [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();
    
            }
    [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();
    
            }

    让我们看看API路由是否可以工作。单击IIS Express启动我们的web应用程序。首先我们检查URL, api/recipes/step/AEE9602B-03EF-4A5F-A380-2962134ADB7E。 它像预期的那样工作。 然后我们检查api/recipes/item/862B91D5-FB60-4004-8179-0415AB900795 它也起作用了。 我们还需要为post和delete添加路由属性。 隐藏,收缩,复制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)
                {
                    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)
                {
                    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);
            }

    单个Angular资源服务的多个路由url Angular资源服务也支持多个url。到目前为止,我们只使用默认动作。 隐藏,复制Code

    {
      get: {method: 'GET'},
      save: {method: 'POST'},
      query: {method: 'GET', isArray: true},
      remove: {method: 'DELETE'},
      delete: {method: 'DELETE'}
    }

    以上操作都是在ng resource中构建的,所以我们可以直接使用它。 隐藏,复制Code

    recipesService.factory('Recipe', ['$resource', function ($resource) {
          return $resource('/api/recipes/:id');
      }]);
    

    但是我们现在需要定义自己的自定义操作,并使用默认URL为操作提供不同的URL。 隐藏,复制Code

    recipesService.factory('Recipe', ['$resource', function ($resource) {
            return $resource('/api/recipes/:id', {}, {
                getRecipeStep: { method: 'GET', url: '/api/recipes/step/:id' },
                saveRecipeStep: { method: 'POST', url: '/api/recipes/step' },
                removeRecipeStep: { method: 'DELETE', url: '/api/recipes/step/:id' },
                getRecipeItem: { method: 'GET', url: '/api/recipes/item/:id' },
                saveRecipeItem: { method: 'POST', url: '/api/recipes/item' },
                removeRecipeItem: { method: 'DELETE', url: '/api/recipes/item/:id' }
            });
    }]);

    我们仍然使用recipe的默认操作,并添加新的自定义操作getRecipeStep、saveRecipeStep、removeRecipeStep、getRecipeItem、saveRecipeItem和removeRecipeItem。 所有url都匹配配方步骤和配方项的web API url。 为配方步骤和配方项添加新的角度路径 现在我们需要为app.js中的菜谱步骤创建、更新、删除和菜谱项创建、更新、删除模板和控制器添加新的客户端路由。 隐藏,收缩,复制Code

    $routeProvider
      .when('/', {
          templateUrl: 'partials/recipes.html',
          controller: 'recipesController'
      })
      .when('/recipes/add', {
          templateUrl: 'partials/add.html',
          controller: 'recipesAddController'
      })
      .when('/recipes/edit/:id', {
          templateUrl: 'partials/edit.html',
          controller: 'recipesEditController'
      })
      .when('/recipes/delete/:id', {
          templateUrl: 'partials/delete.html',
          controller: 'recipesDeleteController'
      })
      .when('/recipes/addStep/:id', {
          templateUrl: 'partials/addStep.html',
          controller: 'recipesAddStepController'
      })
      .when('/recipes/editStep/:id', {
          templateUrl: 'partials/editStep.html',
          controller: 'recipesEditStepController'
      })
      .when('/recipes/deleteStep/:id', {
          templateUrl: 'partials/deleteStep.html',
          controller: 'recipesDeleteStepController'
      })
      .when('/recipes/addItem/:id', {
          templateUrl: 'partials/addItem.html',
          controller: 'recipesAddItemController'
      })
      .when('/recipes/editItem/:id', {
          templateUrl: 'partials/editItem.html',
          controller: 'recipesEditItemController'
      })
      .when('/recipes/deleteItem/:id', {
          templateUrl: 'partials/deleteItem.html',
          controller: 'recipesDeleteItemController'
      });
    

    为配方步骤和配方项添加新的Angular控制器 在recipesController.js中注入step和item控制器。 隐藏,复制Code

    angular
       .module('masterChefApp')
       .controller('recipesController', recipesController)
       .controller('recipesAddController', recipesAddController)
       .controller('recipesEditController', recipesEditController)
       .controller('recipesDeleteController', recipesDeleteController)
       .controller('recipesAddStepController', recipesAddStepController)
       .controller('recipesEditStepController', recipesEditStepController)
       .controller('recipesDeleteStepController', recipesDeleteStepController)
       .controller('recipesAddItemController', recipesAddItemController)
       .controller('recipesEditItemController', recipesEditItemController)
       .controller('recipesDeleteItemController', recipesDeleteItemController);
    

    recipesAddStepController创建或提供了允许某人向应用程序添加菜谱步骤的功能。当我们添加配方步骤时,我们需要父配方Id。我们将通过使用routeParams服务获得要创建的配方步骤。通过从route参数获取菜谱的ID。 隐藏,复制Code

    recipesAddStepController.$inject = ['$scope', 'Recipe', '$location', '$routeParams'];
        function recipesAddStepController($scope, Recipe, $location, $routeParams) {
            $scope.recipeStep = new Recipe();
            $scope.recipeStep.parentId = $routeParams.id;
            $scope.addRecipeStep = function () {
                $scope.recipeStep.$saveRecipeStep(function () {
                    $location.path('/');
                });
            };
        }

    recipesEditStepController创建或提供了允许某人将配方步骤更新到应用程序的功能。我们将使用routeParams服务来更新菜谱步骤。通过从route参数获取菜谱步骤的ID。 隐藏,复制Code

    recipesEditStepController.$inject = ['$scope', 'Recipe', '$location', '$routeParams'];
    function recipesEditStepController($scope, Recipe, $location, $routeParams) {
        $scope.recipeStep = Recipe.getRecipeStep({ id: $routeParams.id });
        $scope.editRecipeStep = function () {
            $scope.recipeStep.$saveRecipeStep(function () {
                $location.path('/');
            });
        };
    }
    

    recipesDeleteStepController使用$routeParams获取ID并检索特定的菜谱步骤。然后将此函数的删除步骤提供给应用程序。 隐藏,复制Code

        recipesDeleteStepController.$inject = ['$scope', 'Recipe', '$location', '$routeParams'];
        function recipesDeleteStepController($scope, Recipe, $location, $routeParams) {
            $scope.recipeStep = Recipe.getRecipeStep({ id: $routeParams.id });
            $scope.deleteRecipeStep = function () {
                $scope.recipeStep.$removeRecipeStep({ id: $scope.recipeStep.id }, function () {
                    $location.path('/');
                });
            };
    }

    recipesAddItemController创建或提供了允许用户向应用程序添加菜谱项的功能。当我们添加菜谱项时,我们需要父菜谱步骤Id。我们将通过使用routeParams服务获得要创建的菜谱项。通过从route参数获取菜谱步骤的ID。 隐藏,复制Code

    recipesAddItemController.$inject = ['$scope', 'Recipe', '$location', '$routeParams'];
        function recipesAddItemController($scope, Recipe, $location, $routeParams) {
            $scope.recipeItem = new Recipe();
            $scope.recipeItem.parentId = $routeParams.id;
            $scope.addRecipeItem = function () {
                $scope.recipeItem.$saveRecipeItem(function () {
                    $location.path('/');
                });
            };
    }

    recipesEditItemController创建或提供了允许用户将菜谱项更新到应用程序的功能。我们将使用routeParams服务来更新菜谱项。通过从route参数获取菜谱项的ID。 隐藏,复制Code

    recipesEditItemController.$inject = ['$scope', 'Recipe', '$location', '$routeParams'];
    function recipesEditItemController($scope, Recipe, $location, $routeParams) {
        $scope.recipeItem = Recipe.getRecipeItem({ id: $routeParams.id });
        $scope.editRecipeItem = function () {
            $scope.recipeItem.$saveRecipeItem(function () {
                $location.path('/');
            });
        };
    }
    

    recipesDeleteItemController使用$routeParams获取ID并检索特定的菜谱项。然后提供此函数,将菜谱项删除到应用程序。 隐藏,复制Code

        recipesDeleteItemController.$inject = ['$scope', 'Recipe', '$location', '$routeParams'];
        function recipesDeleteItemController($scope, Recipe, $location, $routeParams) {
            $scope.recipeItem = Recipe.getRecipeItem({ id: $routeParams.id });
            $scope.deleteRecipeItem = function () {
                $scope.recipeItem.$removeRecipeItem({ id: $scope.recipeItem.id }, function () {
                    $location.path('/');
                });
            };
    }

    添加配方步骤和配方项的所有模板 现在我们需要为配方步骤和配方项创建所有模板。创建“addStep。html”、“editStep。html”、“deleteStep.html”、“addItem。html”、“editItem。html”和“deleteItem。在partials文件夹中。 1)配方步骤模板 在addStep。html,使用ng-submit发送数据到服务器。当用户按下Save按钮时,调用一个作用域函数addRecipeStep,该函数在控制器的后台将向服务器提交这个配方步骤对象。 隐藏,复制Code

    <h1>Add a new recipe step</h1>
    <divclass="container-fluid">
        <formng-submit="addRecipeStep()">
            <divclass="row">
                <divclass="form-group col-xs-1">
                    <labelfor="stepNo">Step No.</label>
                    <inputng-model="recipeStep.stepNo"name="stepNo"type="text"class="form-control"/>
                </div>
            </div>
    
            <divclass="row">
                <divclass="form-group col-md-4 col-xs-8">
                    <labelfor="instructions">Instructions</label>
                    <inputng-model="recipeStep.instructions"name="instructions"type="text"class="form-control"/>
                </div>
            </div>
            <divclass="row">
                <buttontype="submit"class="btn btn-primary">Save</button>
                <ahref="/"class="btn btn-default">Cancel</a>
            </div>
        </form>
    </div>

    html更新现有的配方步骤。使用ng-model指令将输入字段绑定到一个范围变量——一个对象recipeStep。此外,在step编辑控制器上,有一个作用域函数- editRecipeStep。 隐藏,复制Code

    <h1>Edit Recipe Step</h1>
    <divclass="container-fluid">
        <formng-submit="editRecipeStep()">
            <divclass="row">
                <divclass="form-group col-xs-1">
                    <labelfor="stepNo">Step No.</label>
                    <inputng-model="recipeStep.stepNo"name="stepNo"type="text"class="form-control"/>
                </div>
            </div>
    
            <divclass="row">
                <divclass="form-group col-md-4 col-xs-8">
                    <labelfor="instructions">Instructions</label>
                    <inputng-model="recipeStep.instructions"name="instructions"type="text"class="form-control"/>
                </div>
            </div>
            <divclass="row">
                <buttontype="submit"class="btn btn-primary">Save</button>
                <ahref="/"class="btn btn-default">Cancel</a>
            </div>
        </form>
    </div>

    在deleteStep。html,我们会提供一个段落来进行确认。我们将提供一个按钮,它调用一个作用域函数- deleteRecipeStep。它将向服务器提交一个请求,以删除特定的配方步骤。 隐藏,复制Code

    <div class="alert alert-warning">
        <p>Do you really want to delete this recipe step?</p>
        <p> {{recipeStep.stepNo}} - {{recipeStep.instructions}}</p>
    </div>
    <button ng-click="deleteRecipeStep()" class="btn btn-danger">Yes</button>
    <a href="/" class="btn btn-default">No</a>

    2)配方物品模板 在addItem。html,使用ng-submit发送数据到服务器。当用户按下Save按钮时,调用一个作用域函数addRecipeItem,该函数在控制器的后台将向服务器提交这个recipe item对象。 隐藏,收缩,复制Code

    <h1>Add a new recipe item</h1>
    <divclass="container-fluid">
        <formng-submit="addRecipeItem()">
            <divclass="row">
                <divclass="form-group col-xs-4">
                    <labelfor="name">Name</label>
                    <inputng-model="recipeItem.name"name="name"type="text"class="form-control"/>
                </div>
            </div>
            <divclass="row">
                <divclass="form-group col-md-4 col-xs-4">
                    <labelfor="quantity">Quantity</label>
                    <inputng-model="recipeItem.quantity"name="quantity"type="text"class="form-control"/>
                </div>
            </div>
            <divclass="row">
                <divclass="form-group col-md-4 col-xs-4">
                    <labelfor="measurementUnit">Measurement Unit</label>
                    <inputng-model="recipeItem.measurementUnit"name="measurementUnit"type="text"class="form-control"/>
                </div>
            </div>
            <divclass="row">
                <buttontype="submit"class="btn btn-primary">Save</button>
                <ahref="/"class="btn btn-default">Cancel</a>
            </div>
        </form>
    </div>

    html更新现有的菜谱项。使用ng-model指令将输入字段绑定到一个范围变量——一个对象recipeItem。此外,在项目编辑控制器上,有一个作用域函数- editRecipeItem。 隐藏,收缩,复制Code

    <h1>Edit Recipe Item</h1>
    <divclass="container-fluid">
        <formng-submit="editRecipeItem()">
            <divclass="row">
                <divclass="form-group col-xs-4">
                    <labelfor="name">Name</label>
                    <inputng-model="recipeItem.name"name="name"type="text"class="form-control"/>
                </div>
            </div>
            <divclass="row">
                <divclass="form-group col-md-4 col-xs-4">
                    <labelfor="quantity"></label>
                    <inputng-model="recipeItem.quantity"name="quantity"type="text"class="form-control"/>
                </div>
            </div>
            <divclass="row">
                <divclass="form-group col-md-4 col-xs-4">
                    <labelfor="measurementUnit"></label>
                    <inputng-model="recipeItem.measurementUnit"name="measurementUnit"type="text"class="form-control"/>
                </div>
            </div>
            <divclass="row">
                <buttontype="submit"class="btn btn-primary">Save</button>
                <ahref="/"class="btn btn-default">Cancel</a>
            </div>
        </form>
    </div>

    在deleteItem。html,我们会提供一个段落来进行确认。我们将提供一个按钮,它调用作用域函数deleteRecipeItem。它将向服务器提交一个请求,以删除特定的菜谱项。 隐藏,复制Code

    <div class="alert alert-warning">
        <p>Do you really want to delete this recipe item?</p>
        <p> {{recipeItem.name}}  {{recipeItem.quantity}} {{recipeItem.measurementUnit}}</p>
    </div>
    <button ng-click="deleteRecipeItem()" class="btn btn-danger">Yes</button>
    <a href="/" class="btn btn-default">No</a>

    一切都完成了。现在您可以创建、更新或删除菜谱了。你会成为一个真正的大厨。不仅仅是一个只遵循别人食谱的厨师。 IE缓存问题 最后,我想谈谈发生在IE上的一个缓存问题。如果我们把IIS Express改成IE,在我添加了一个新菜谱“roast Duck”之后,你不能马上看到我刚刚添加的新菜谱。它没有正确地插入吗?去数据库查一下,新食谱就在那里。看起来当返回到list时,AngularJS根本没有发送httpget请求到服务器,只是从缓存中获取结果。这就是为什么新的更新不会弹出。我们可以资源通过httpProvider解决这个问题。在AngularJS应用程序配置函数中注入httpProvider。然后将http默认缓存设置为false,并将http get请求头中的If-Modified-Since设置为0。 隐藏,收缩,复制Code

    angular.module('masterChefApp').config(['$routeProvider', '$httpProvider', '$locationProvider', function ($routeProvider, $httpProvider, $locationProvider) {
            //disable http cache
            $httpProvider.defaults.cache = false;
            if (!$httpProvider.defaults.headers.get) {
                $httpProvider.defaults.headers.get = {};
            }
    
            $httpProvider.defaults.headers.get['If-Modified-Since'] = '0';
            //////////////////////////////////////////////////////////////////
    
            $routeProvider
            .when('/', {
                templateUrl: 'partials/recipes.html',
                controller: 'recipesController'
            })
            .when('/recipes/add', {
                templateUrl: 'partials/add.html',
                controller: 'recipesAddController'
            })
            .when('/recipes/edit/:id', {
                templateUrl: 'partials/edit.html',
                controller: 'recipesEditController'
            })
            .when('/recipes/delete/:id', {
                templateUrl: 'partials/delete.html',
                controller: 'recipesDeleteController'
            })
            .when('/recipes/addStep/:id', {
                templateUrl: 'partials/addStep.html',
                controller: 'recipesAddStepController'
            })
            .when('/recipes/editStep/:id', {
                templateUrl: 'partials/editStep.html',
                controller: 'recipesEditStepController'
            })
            .when('/recipes/deleteStep/:id', {
                templateUrl: 'partials/deleteStep.html',
                controller: 'recipesDeleteStepController'
            })
            .when('/recipes/addItem/:id', {
                templateUrl: 'partials/addItem.html',
                controller: 'recipesAddItemController'
            })
            .when('/recipes/editItem/:id', {
                templateUrl: 'partials/editItem.html',
                controller: 'recipesEditItemController'
            })
            .when('/recipes/deleteItem/:id', {
                templateUrl: 'partials/deleteItem.html',
                controller: 'recipesDeleteItemController'
            });
    
            $locationProvider.html5Mode(true);
    
        }]);

    然后我们再试一次。它像一个魅力。虽然我没有这个缓存问题在谷歌Chrome,我们仍然需要修复这个问题在IE,因为web应用程序应该工作在所有的浏览器。 结论 在本文中,我介绍了如何使用angular route创建SPA CRUD应用程序。我们还讨论了如何在单个服务器端Web API控制器中映射多个url。以及相应的,如何在单个客户端angular资源服务中映射不同的路由。从Maser Chef part 3开始,我们将在Angular2和EntityFramework Core上开始一个新的冒险。 本文转载于:http://www.diyabc.com/frontweb/news17322.html

  • 相关阅读:
    day25:接口类和抽象类
    vue1
    How the weather influences your mood?
    机器学习实验方法与原理
    How human activities damage the environment
    Slow food
    Brief Introduction to Esports
    Massive open online course (MOOC)
    Online learning in higher education
    Tensorflow Dataset API
  • 原文地址:https://www.cnblogs.com/Dincat/p/13494085.html
Copyright © 2011-2022 走看看