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

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

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

EditLink_sm

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

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

Html.ActionLink

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

上图中所示的生成的链接是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文件中的以下代码。

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。

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>元素的每个类的属性。下面的示例演示了生成的编辑视图:

@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.

搭建的代码使用的帮助器方法的几种简化的 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模型以及如何添加一个初始值设定项,将自动创建一个测试数据库。

posted @ 2015-01-22 11:15  178mz  阅读(351)  评论(0编辑  收藏  举报