ASP.NET MVC4 新手入门教程之六 ---6.编辑视图与编辑方法

在本节中,您会为电影控制器检查生成的操做方法和视图。而后,您将添加一个自定义的搜索页面。html

运行该应用程序,而后浏览到Movies控制器经过将/Movies追加到您的浏览器的地址栏中的 URL。将鼠标指针悬停在编辑连接,看到它连接到的 URL。jquery

EditLink_sm

编辑连接是由Html.ActionLink方法在Views\Movies\Index.cshtml视图中生成的:git

@Html.ActionLink("Edit", "Edit", new { id=item.ID }) 

Html.ActionLink

Html对象是一个帮助器,暴露使用上System.Web.Mvc.WebViewPage基类的属性。帮助器的 ActionLink 方法,便于动态生成 HTML 超连接连接到控制器上的操做方法。ActionLink方法的第一个参数是要呈现的连接文本 (例如,<a>Edit Me</a>)。第二个参数是要调用的操做方法的名称。最后一个参数是一个匿名对象,生成路由数据 (在本例中,ID 为 4 的)。github

上图中所示的生成的连接是http://localhost:xxxxx/电影/编辑/4默认的路由 (创建在App_Start\RouteConfig.cs) 采用 URL 模式{controller}/{action}/{id}所以,ASP.NET 会http://localhost:xxxxx/电影/编辑/4转化为对Movies 控制器参数ID等于 4 的Edit操做方法的请求。检查App_Start\RouteConfig.cs文件中的如下代码。web

public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( name: "Default", url: "{controller}/{action}/{id}", defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } ); }

你也能够经过使用查询字符串的操做方法参数。例如,URL http://localhost:xxxxx/电影/编辑? ID = 4也向Movies控制器的Edit操做方法传递参数ID为 4。ajax

EditQueryString

打开Movies控制器。两个Edit操做方法以下所示:数据库

// // GET: /Movies/Edit/5 public ActionResult Edit(int id = 0) { Movie movie = db.Movies.Find(id); if (movie == null) { return HttpNotFound(); } return View(movie); } // // POST: /Movies/Edit/5 [HttpPost] public ActionResult Edit(Movie movie) { if (ModelState.IsValid) { db.Entry(movie).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } return View(movie); }

通知的第二个Edit操做方法的前面 HttpPost属性。此属性指定,过载的Edit方法能够调用只为 POST 请求。你能够将 HttpGet属性应用于第一种编辑方法,但这是没必要要的由于它是默认值。(咱们将引用他们做为HttpGet方法隐式地分配HttpGet属性的操做方法)。编程

HttpGetEdit方法获取电影 ID 参数、 查找电影使用实体框架Find方法,并返回到编辑视图的选定的影片。ID 参数指定默认值为零,若是不带参数调用该Edit 的方法。若是找不到一部电影,则会返回HttpNotFound 。当脚手架系统建立编辑视图时,它审查Movie课并建立代码来呈现<label><input>元素的每一个类的属性。下面的示例演示了生成的编辑视图:canvas

@model MvcMovie.Models.Movie

@{
    ViewBag.Title = "Edit";
}

<h2>Edit</h2> @using (Html.BeginForm()) { @Html.ValidationSummary(true) <fieldset> <legend>Movie</legend> @Html.HiddenFor(model => model.ID) <div class="editor-label"> @Html.LabelFor(model => model.Title) </div> <div class="editor-field"> @Html.EditorFor(model => model.Title) @Html.ValidationMessageFor(model => model.Title) </div> <div class="editor-label"> @Html.LabelFor(model => model.ReleaseDate) </div> <div class="editor-field"> @Html.EditorFor(model => model.ReleaseDate) @Html.ValidationMessageFor(model => model.ReleaseDate) </div> <div class="editor-label"> @Html.LabelFor(model => model.Genre) </div> <div class="editor-field"> @Html.EditorFor(model => model.Genre) @Html.ValidationMessageFor(model => model.Genre) </div> <div class="editor-label"> @Html.LabelFor(model => model.Price) </div> <div class="editor-field"> @Html.EditorFor(model => model.Price) @Html.ValidationMessageFor(model => model.Price) </div> <p> <input type="submit" value="Save" /> </p> </fieldset> } <div> @Html.ActionLink("Back to List", "Index") </div> @section Scripts { @Scripts.Render("~/bundles/jqueryval") }

通知视图模板如何将@model MvcMovie.Models.Movie语句在文件的顶部 — — 这将指定视图预计的模型视图模板类型Movie.api

搭建的代码使用的帮助器方法的几种简化的 HTML 标记。 Html.LabelFor帮助器将显示字段 ("标题"、"ReleaseDate"、"流派"或"价格") 的名称。 Html.EditorFor帮手会呈现 HTML <input>元素。Html.ValidationMessageFor帮助器将显示与该属性相关联的任何验证消息。

运行该应用程序,而后定位到/Movies URL。单击编辑连接。在浏览器中查看网页的源代码。以下所示的 HTML 表单元素。

<form action="/Movies/Edit/4" method="post"> <fieldset> <legend>Movie</legend> <input data-val="true" data-val-number="The field ID must be a number." data-val-required="The ID field is required." id="ID" name="ID" type="hidden" value="4" /> <div class="editor-label"> <label for="Title">Title</label> </div> <div class="editor-field"> <input class="text-box single-line" id="Title" name="Title" type="text" value="Rio Bravo" /> <span class="field-validation-valid" data-valmsg-for="Title" data-valmsg-replace="true"></span> </div> <div class="editor-label"> <label for="ReleaseDate">ReleaseDate</label> </div> <div class="editor-field"> <input class="text-box single-line" data-val="true" data-val-date="The field ReleaseDate must be a date." data-val-required="The ReleaseDate field is required." id="ReleaseDate" name="ReleaseDate" type="text" value="4/15/1959 12:00:00 AM" /> <span class="field-validation-valid" data-valmsg-for="ReleaseDate" data-valmsg-replace="true"></span> </div> <div class="editor-label"> <label for="Genre">Genre</label> </div> <div class="editor-field"> <input class="text-box single-line" id="Genre" name="Genre" type="text" value="Western" /> <span class="field-validation-valid" data-valmsg-for="Genre" data-valmsg-replace="true"></span> </div> <div class="editor-label"> <label for="Price">Price</label> </div> <div class="editor-field"> <input class="text-box single-line" data-val="true" data-val-number="The field Price must be a number." data-val-required="The Price field is required." id="Price" name="Price" type="text" value="2.99" /> <span class="field-validation-valid" data-valmsg-for="Price" data-valmsg-replace="true"></span> </div> <p> <input type="submit" value="Save" /> </p> </fieldset> </form>

<input>元素是其action的属性设置为发布到/Movies/Edit URL HTML <form>元素。单击编辑按钮时,表单数据将张贴到服务器。

处理 POST 请求

下面的清单展现HttpPost版本的 Edit操做方法。

[HttpPost] public ActionResult Edit(Movie movie) { if (ModelState.IsValid) { db.Entry(movie).State = EntityState.Modified; db.SaveChanges(); return RedirectToAction("Index"); } return View(movie); }

ASP.NET MVC 中的模型联编程序取出已过账窗体值,并建立一个Movie做为movie参数传递。ModelState.IsValid方法验证提交表单中的数据能够用于修改 (编辑或更新)Movie对象。若是数据是有效的电影数据保存到Movies收藏的db(MovieDBContext instance)。新的电影数据将保存到数据库中,经过调用MovieDBContext SaveChanges方法。保存数据以后, 的代码将用户重定向到Index操做方法的MoviesController类,显示的电影收藏,包括刚才所作的更改。

若是发送的值不是有效的系统会将他们从新显示在窗体中。Edit.cshtml视图模板Html.ValidationMessageFor佣工照顾显示相应的错误消息。

abcNotValid

注意jQuery 验证支持为非英语区域设置,使用逗号 (",") 一个小数点,您必须包括globalize.js和你具体cultures/globalize.cultures.js文件 (从https://github.com/jquery/globalize ) 和 JavaScript 来使用 Globalize.parseFloat下面的代码演示对要与"FR-FR"文化工做的 Views\Movies\Edit.cshtml 文件的修改:
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
    <script src="~/Scripts/globalize.js"></script> <script src="~/Scripts/globalize.culture.fr-FR.js"></script> <script> $.validator.methods.number = function (value, element) { return this.optional(element) || !isNaN(Globalize.parseFloat(value)); } $(document).ready(function () { Globalize.culture('fr-FR'); }); </script> <script> jQuery.extend(jQuery.validator.methods, { range: function (value, element, param) { //Use the Globalization plugin to parse the value  var val = $.global.parseFloat(value); return this.optional(element) || ( val >= param[0] && val <= param[1]); } }); </script> }

十进制字段可能须要一个逗号,不是小数点。做为临时性的措施,能够将全球化元素添加到项目根 web.config 文件。下面的代码显示设置为美国英语的文化全球化元素。

  <system.web> <globalization culture ="en-US" /> <!--elements removed for clarity--> </system.web>

全部HttpGet方法都遵循相似的模式。他们获得电影对象 (或对象的列表,在Index的状况下),并将模型传递到视图。Create方法将空影片对象传递给建立视图。建立、 编辑、 删除或以其余方式修改数据的方法这样作在HttpPost重载的方法。修改 HTTP GET 方法中的数据是安全风险的博客帖子条目中所述ASP.NET MVC 提示 #46 — — 不要使用删除连接,由于它们建立安全漏洞在 GET 方法中修改数据也违反了 HTTP 的最佳作法和建筑的其余模式,指定 GET 请求不该更改您应用程序的状态。换句话说,执行 GET 操做,应该是没有任何反作用,不会修改您的持久化的数据的安全操做。

添加一个搜索方法和搜索视图

在本节中,您将添加容许您搜索电影的体裁或名称SearchIndex操做方法。这将可以使用/Movies/SearchIndex的 URL。该请求将显示 HTML 窗体,其中包含用户能够输入要搜索的一部电影的输入的元素。当用户提交窗体时,该操做方法将获取用户张贴的搜索值和使用的值在数据库中搜索。

显示 SearchIndex 窗体

经过将SearchIndex操做方法添加到现有的MoviesController类开始。该方法将返回包含 HTML 窗体的视图。下面是代码:

public ActionResult SearchIndex(string searchString) { var movies = from m in db.Movies select m; if (!String.IsNullOrEmpty(searchString)) { movies = movies.Where(s => s.Title.Contains(searchString)); } return View(movies); }

SearchIndex方法的第一行建立如下的LINQ查询,以选择看电影:

var movies = from m in db.Movies select m;

查询定义了在这一点上,但还没有被开办针对数据存储区。

若是searchString参数包含一个字符串,修改电影查询要做为筛选依据的值的搜索字符串,使用下面的代码:

    if (!String.IsNullOrEmpty(searchString)) { movies = movies.Where(s => s.Title.Contains(searchString)); }

s => s.Title 上面的代码是一个Lambda 表达式Lambda 方法基于LINQ查询中被用做在哪里法在上面的代码中使用标准查询运算符方法的参数。当它们被定义或经过调用方法,如Where OrderBy被修改时,不会执行 LINQ 查询。相反,这意味着表达式的计算延迟,直到其实现的价值实际上遍历或ToList方法称为,推迟执行查询。SearchIndex示例中,在 SearchIndex 视图中执行查询。有关延迟的查询执行的详细信息,请参阅查询执行.

如今,您能够实现SearchIndex认为,将向用户显示窗体。SearchIndex方法内部右键单击,而后单击添加视图添加视图对话框中,指定要将Movie对象传递给视图模板做为其示范课。脚手架模板列表中,选择列表,而后单击添加.

AddSearchView

当您单击添加按钮时,建立Views\Movies\SearchIndex.cshtml视图模板。由于你选择 (就绪) 在脚手架模板列表中,Visual Studio 将自动生成的列表视图中的某些默认标记。脚手架建立 HTML 表单。它审查Movie课,并建立代码来呈现<label>元素的每一个类的属性。下面的列表显示了生成建立视图:

@model IEnumerable<MvcMovie.Models.Movie> @{ ViewBag.Title = "SearchIndex"; } <h2>SearchIndex</h2> <p> @Html.ActionLink("Create New", "Create") </p> <table> <tr> <th> Title </th> <th> ReleaseDate </th> <th> Genre </th> <th> Price </th> <th></th> </tr> @foreach (var item in Model) { <tr> <td> @Html.DisplayFor(modelItem => item.Title) </td> <td> @Html.DisplayFor(modelItem => item.ReleaseDate) </td> <td> @Html.DisplayFor(modelItem => item.Genre) </td> <td> @Html.DisplayFor(modelItem => item.Price) </td> <td> @Html.ActionLink("Edit", "Edit", new { id=item.ID }) | @Html.ActionLink("Details", "Details", new { id=item.ID }) | @Html.ActionLink("Delete", "Delete", new { id=item.ID }) </td> </tr> } </table>

运行该应用程序,而后定位到/Movies/SearchIndex如追加查询字符串?searchString=ghost到的 URL。将显示筛选后的电影。

SearchQryStr

若是您更改的SearchIndex方法,有一个名为id的参数的签名, id参数将匹配在Global.asax文件中设置的默认路由的{id}占位符。

{controller}/{action}/{id}

原来的SearchIndex方法看起来像这样:

public ActionResult SearchIndex(string searchString) { var movies = from m in db.Movies select m; if (!String.IsNullOrEmpty(searchString)) { movies = movies.Where(s => s.Title.Contains(searchString)); } return View(movies); }

改进的SearchIndex方法将以下所示:

public ActionResult SearchIndex(string id) { string searchString = id; var movies = from m in db.Movies select m; if (!String.IsNullOrEmpty(searchString)) { movies = movies.Where(s => s.Title.Contains(searchString)); } return View(movies); }

如今,能够将搜索标题做为路由数据 (URL 段) 而不是做为查询字符串值传递。

SearchRouteData

然而,你不能期望用户能够修改 URL,每次他们想要寻找一部电影。因此,如今您将添加 UI,以帮助他们筛选的电影。若是您更改了SearchIndex方法来测试如何传递路线绑定 ID 参数的签名,改变它回去,这样你的SearchIndex方法采用一个名为searchString的字符串参数:

public ActionResult SearchIndex(string searchString) { var movies = from m in db.Movies select m; if (!String.IsNullOrEmpty(searchString)) { movies = movies.Where(s => s.Title.Contains(searchString)); } return View(movies); }

打开Views\Movies\SearchIndex.cshtml文件,并只是后 @Html.ActionLink("Create New", "Create"),请添加如下内容:

@using (Html.BeginForm()){    
         <p> Title: @Html.TextBox("SearchString")<br /> <input type="submit" value="Filter" /></p> }

下面的示例显示Views\Movies\SearchIndex.cshtml文件添加筛选标记的一部分。

@model IEnumerable<MvcMovie.Models.Movie> @{ ViewBag.Title = "SearchIndex"; } <h2>SearchIndex</h2> <p> @Html.ActionLink("Create New", "Create") @using (Html.BeginForm()){ <p> Title: @Html.TextBox("SearchString") <br /> <input type="submit" value="Filter" /></p> } </p>

Html.BeginForm帮助建立开放<form>标记。Html.BeginForm助手致使表单时用户经过单击筛选按钮提交表单发送到自己。

运行应用程序并试着寻找一部电影。

还有SearchIndex方法没有HttpPost过载。你不须要它,由于该方法不会改变应用程序的状态只筛选数据。

您能够添加如下HttpPost SearchIndex 方法。在这种状况下,动做调用器将匹配HttpPost SearchIndex方法,,而且HttpPost SearchIndex方法将运行下面的图像所示。

[HttpPost] public string SearchIndex(FormCollection fc, string searchString) { return "<h3> From [HttpPost]SearchIndex: " + searchString + "</h3>"; }

SearchPostGhost

然而,即便您添加此HttpPost 版本的 SearchIndex 方法,还有这一切如何执行的限制。想象一下你想要添加书签特定的搜索,或你想要的连接发送给朋友他们能够单击以查看相同的电影已筛选的列表中。注意 HTTP POST 请求的 URL 是 GET 请求 (localhost:xxxxx/电影/SearchIndex) 的 URL 相同 — — 在 URL 自己没有搜索信息。权利如今,搜索字符串信息发送到服务器做为窗体字段值。这意味着你永远不能捕捉那搜索信息,以书签或发送给朋友在 URL 中。

解决方法是使用BeginForm ,它指定 POST 请求应添加到 URL 的搜索信息,应该把它传递给SearchIndex 方法的公共版本重载。现有的无参数BeginForm 方法替换为如下内容:

@using (Html.BeginForm("SearchIndex","Movies",FormMethod.Get))

BeginFormPost_SM

如今当你提交一个搜索,该 URL 包含一个搜索查询字符串。搜索还会去 HttpGet SearchIndex操做方法,即便你有一个 HttpPost SearchIndex方法。

SearchIndexWithGetURL

添加搜索按流派

若是您添加HttpPost版本的SearchIndex方法,当即删除它。

接下来,您将添加一个功能,让用户搜索电影的体裁。SearchIndex方法替换为如下代码:

public ActionResult SearchIndex(string movieGenre, string searchString) { var GenreLst = new List<string>(); var GenreQry = from d in db.Movies orderby d.Genre select d.Genre; GenreLst.AddRange(GenreQry.Distinct()); ViewBag.movieGenre = new SelectList(GenreLst); var movies = from m in db.Movies select m; if (!String.IsNullOrEmpty(searchString)) { movies = movies.Where(s => s.Title.Contains(searchString)); } if (string.IsNullOrEmpty(movieGenre)) return View(movies); else { return View(movies.Where(x => x.Genre == movieGenre)); } }

此版本的SearchIndex方法须要一个额外的参数,即movieGenre第一次的几行代码建立一个List对象来保存电影流派从数据库。

下面的代码是从数据库中检索全部流派的 LINQ 查询。

var GenreQry = from d in db.Movies orderby d.Genre select d.Genre;

代码使用泛型 List集合的 AddRange方法向列表中添加全部不一样的流派。(不带 Distinct修饰符,将添加剧复的流派 — — 例如,喜剧会添加两次在咱们的示例)。该代码而后在ViewBag对象中存储流派的列表。

下面的代码演示如何检查movieGenre参数。若是它不是空的该守则还限制电影查询,以限制到指定类型的所选的电影。

 if (string.IsNullOrEmpty(movieGenre)) return View(movies); else { return View(movies.Where(x => x.Genre == movieGenre)); }

将标记添加到 SearchIndex 视图支持按类型排列的搜索

Html.DropDownList助手添加到Views\Movies\SearchIndex.cshtml文件中,只是以前的TextBox帮手。已完成的标记以下所示:

<p> @Html.ActionLink("Create New", "Create") @using (Html.BeginForm("SearchIndex","Movies",FormMethod.Get)){ <p>Genre: @Html.DropDownList("movieGenre", "All") Title: @Html.TextBox("SearchString") <input type="submit" value="Filter" /></p> } </p>

运行该应用程序,而后浏览到/Movies/SearchIndex按流派、 电影的名称,和这两个条件,请尝试搜索。

在本节中,您研究了 CRUD 操做方法和框架所生成的视图。您建立了一个搜索操做方法和视图让用户搜索到的电影标题和流派。在接下来的部分中,你会看看如何将一个属性添加到Movie模型以及如何添加一个初始值设定项,将自动建立一个测试数据库。

相关文章
相关标签/搜索