您的位置:首页 > 编程语言 > ASP

ASP.NET MVC Music Store教程(5):编辑表单和模板

2011-11-17 13:42 281 查看
转自http://firechun.blog.163.com/blog/static/3180452220110303730942/

在上一章,我们加载并显示了数据,这一章中,我们将对数据进行编辑。 我们创建一个新的控制器——StoreManagerController,这个控制器支持Create和Update操作,创建控制器时,把“为Create、Update、Delete和Details方案添加操作方法”勾选上:


这将生成一个包括通用的“增删改查”方法存根的类,在TODO注释中提示我们放入自己的业务逻辑。 using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace MvcMusicStore.Controllers
{
public class StoreManagerController : Controller
{
//
// GET: /StoreManager/
public ActionResult Index()
{
return View();
}
//
// GET: /StoreManager/Create
public ActionResult Create()
{
return View();
}
//
// POST: /StoreManager/Create
[HttpPost]
public ActionResult Create(FormCollection collection)
{
try
{
// TODO: Add insert logic here
return RedirectToAction("Index");
}
catch
{
return View();
}
}

//
// GET: /StoreManager/Edit/5

public ActionResult Edit(int id)
{
return View();
}
//
// POST: /StoreManager/Edit/5
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
try
{
// TODO: Add update logic here

return RedirectToAction("Index");
}
catch
{
return View();
}
}
//
// GET: /StoreManager/Delete/5

public ActionResult Delete(int id)
{
return View();
}
//
// POST: /StoreManager/Delete/5
[HttpPost]
public ActionResult Delete(int id, FormCollection collection)
{
try
{
// TODO: Add delete logic here

return RedirectToAction("Index");
}
catch
{
return View();
}
}
}
} 这里不需要Details操作,因此可以删除它。 添加Artist类 StoreManagerController可以管理唱片的Artist,因此先添加一个Artist模型类,右键单击Models文件夹添加Artist.cs,添加如下代码: namespace MvcMusicStore.Models
{
public class Artist
{
public int ArtistId { get; set; }
public string Name { get; set; }
}
} 下一步,在Album类中添加Artist属性,基于命名约定,不需要任何额外的工作,实体框架就知道如何加载和填充它。更新后的Album类如下: namespace MvcMusicStore.Models
{
public class Album
{
public int AlbumId { get; set; }
public int GenreId { get; set; }
public int ArtistId { get; set; }
public string Title { get; set; }
public decimal Price { get; set; }
public string AlbumArtUrl { get; set; }
public virtual Genre Genre { get; set; }
public virtual Artist Artist { get; set; }
}
} 最后,在MusicStoreEntities类中添加DbSet<Artist>,以便我们在稍后可以获得Artist数据,而不需要通过Album类加载。 using System.Data.Entity;

namespace MvcMusicStore.Models
{
public class MusicStoreEntities : DbContext
{
public DbSet<Album> Albums { get; set; }
public DbSet<Genre> Genres { get; set; }
public DbSet<Artist> Artists {
get; set; }
}
} 定制Store Manager Index 在StoreManagerController中,添加一个字段来保存MusicStoreEntities的实例,首先引入命名空间: using MvcMusicStore.Models; 接下来添加字段: public class StoreManagerController : Controller
{
MusicStoreEntities storeDB = new
MusicStoreEntities(); 现在要实现Store Manager Index操作了,这个操作要显示唱片列表,和之前写的StoreController中的Index操作类似,使用LINQ检索所有唱片及相关的Genre和Artist。 //
// GET: /StoreManager/
public ActionResult Index()
{
var albums = storeDB.Albums
.Include("Genre").Include("Artist")
.ToList();
return View(albums);
} 视图模板“脚手架” 添加视图的方式和前面的StoreController.Index相同,记住视图内容选择“List”,这里不重复了。 下面是MVC 2产生的代码,和原文略有不同,实质一样。 <table>
<tr>
<th></th>
<th>
AlbumId
</th>
<th>
GenreId
</th>
<th>
ArtistId
</th>
<th>
Title
</th>
<th>
Price
</th>
<th>
AlbumArtUrl
</th>
</tr> <% foreach (var item in Model) { %>

<tr>
<td>
<%: Html.ActionLink("Edit", "Edit", new { /* id=item.PrimaryKey */ }) %> |
<%: Html.ActionLink("Details", "Details", new { /* id=item.PrimaryKey */ })%> |
<%: Html.ActionLink("Delete", "Delete", new { /* id=item.PrimaryKey */ })%>
</td>
<td>
<%: item.AlbumId %>
</td>
<td>
<%: item.GenreId %>
</td>
<td>
<%: item.ArtistId %>
</td>
<td>
<%: item.Title %>
</td>
<td>
<%: String.Format("{0:F}", item.Price) %>
</td>
<td>
<%: item.AlbumArtUrl %>
</td>
</tr>

<% } %> </table> <p>
<%: Html.ActionLink("Create New", "Create") %>
</p> 我们只显示唱片的Title、Artist和Genre属性,修改后的代码如下: <table>
<tr>
<th></th>
<th>Title</th>
<th>Artist</th>
<th>Genre</th>
</tr> <% foreach (var item in Model) { %>

<tr>
<td>
<%: Html.ActionLink("Edit", "Edit", new { id=item.AlbumId }) %> |
<%: Html.ActionLink("Delete", "Delete", new { id=item.AlbumId })%>
</td>
<td><%: item.Title %></td>
<td><%: item.Artist.Name %></td>
<td><%: item.Genre.Name %></td>
</tr>

<% } %> </table> <p>
<%: Html.ActionLink("Create New", "Create") %>
</p> 运行程序浏览/StoreManager:

使用自定义HTML helper截断文本 在Store Manager Index页面中有一个潜在的问题,唱片的Title和Artist的Name属性可能会因为过长而破坏表格布局。我们创建一个自定义的HTML Helper以在视图中方便地截断这些文本。

添加一个新目录Helpers,在该目录下添加一个新类HtmlHelpers.cs

我们将为视图中的Html对象增加一个新的方法“Truncate”,要想实现这个目的,需要为内置的System.Web.Mvc.HtmlHelper 类添加一个扩展方法。除了HtmlHelpers和方法都必须是静态的以外,其它都很简单。 using System.Web.Mvc;

namespace MvcMusicStore.Helpers
{
public static class HtmlHelpers
{
public static string Truncate(this HtmlHelper helper, string input, int
length)
{ if (input.Length <= length)
{
return input;
}
else
{
return input.Substring(0, length) + "...";
}
}
}
} 这个方法接收文本和允许的最大长度,如果文本长度小于指定长度,返回文本本身,否则返回用指定长度截断的文本并加上“...”。 要使用自定义的Html Helper方法,需要在视图中引入命名空间,在页面指令下面加上下面的代码: <%@ Import Namespace="MvcMusicStore.Helpers" %>
现在可以使用Html.Truncate方法以确保Album的Title和Artist的Name属性少于25个字符。使用了Truncate方法的代码如下: <td><%: Html.Truncate(item.Title,25) %></td>
<td><%: Html.Truncate(item.Artist.Name,25) %></td>
浏览/StoreManager/ URL,唱片的标题保持我们指定的最大长度:

创建Edit视图 接下来创建表单以允许网站管理者编辑唱片,包括为唱片的Title、Price和AlbumArtUrl准备的文本字段,稍后还会增加下拉框,以便用户可以从列表中选择Genre和Artist。

实现Edit操作方法 仓库管理人员可以经由访问/StoreManager/Edit/[id]来编辑唱片,URL中的[Id]值表示要编辑的唱片的ID。 管理者访问上述网址时,我们的代码会从数据库中检索唱片,创建一个包含Artist和Genre列表的Album对象,然后把这个对象传递到视图模板中用HTML呈现给用户,这HTML页面会包含一个<form>元素并用文本框显示唱片细节。 用户可以改变表单中的值,点击“Save”把这些改变提交到应用程序并保存到数据库中。当用户点击“Save”按纽时,会执行一个HTTP-POST回传至/StoreManager/Edit/[id],而表单中的值也会以HTTP-POST的方式提交。 ASP.NET MVC让我们很容易区分两种对网址的调用,在StoreManagerController内执行两个不同的Edit操作方法,一个是初始的以HTTP- GET方式浏览/StoreManager/Edit/[id],另一个则是HTTP-POST方式提交的更改。 我们可以象如下所示那样定义两个Edit操作方法: //
// GET: /StoreManager/Edit/5
public ActionResult Edit(int id)
{
//Display Edit form
}
//
// POST: /StoreManager/Edit/5
[HttpPost]
public ActionResult Edit(int id, FormCollection formValues)
{
//Save Album
} ASP.NET MVC会自动调用方法,无论对/StoreManager/Edit/[id]的请求是来自HTTP-POST还是HTTP-GET。如果是HTTP-POST,调用第二个方法,其它的(包括HTTP-GET)调用第一个方法。 编写HTTP-GET的Edit操作 我们用下面的代码实现HTTP-GET版的Edit操作,它从数据库中检索Album对象,并把它传递到视图中。 //
// GET: /StoreManager/Edit/5
public ActionResult Edit(int id)
{
Album album = storeDB.Albums.Find(id);

return View(album);
} 创建Edit视图 在Edit操作方法上单击右键选择“添加视图”创建Edit视图模板,这是一个Album的强类型视图模板,在“视图内容”中选择“Edit”:

这是自动生成的代码: <% using (Html.BeginForm()) {%>
<%: Html.ValidationSummary(true) %>

<fieldset>
<legend>Fields</legend>

<div class="editor-label">
<%: Html.LabelFor(model => model.AlbumId) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.AlbumId) %>
<%: Html.ValidationMessageFor(model => model.AlbumId) %>
</div>

<div class="editor-label">
<%: Html.LabelFor(model => model.GenreId) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.GenreId) %>
<%: Html.ValidationMessageFor(model => model.GenreId) %>
</div>

<div class="editor-label">
<%: Html.LabelFor(model => model.ArtistId) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.ArtistId) %>
<%: Html.ValidationMessageFor(model => model.ArtistId) %>
</div>

<div class="editor-label">
<%: Html.LabelFor(model => model.Title) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Title) %>
<%: Html.ValidationMessageFor(model => model.Title) %>
</div>

<div class="editor-label">
<%: Html.LabelFor(model => model.Price) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.Price, String.Format("{0:F}", Model.Price)) %>
<%: Html.ValidationMessageFor(model => model.Price) %>
</div>

<div class="editor-label">
<%: Html.LabelFor(model => model.AlbumArtUrl) %>
</div>
<div class="editor-field">
<%: Html.TextBoxFor(model => model.AlbumArtUrl) %>
<%: Html.ValidationMessageFor(model => model.AlbumArtUrl) %>
</div>

<p>
<input type="submit" value="Save" />
</p>
</fieldset> <% } %> <div>
<%: Html.ActionLink("Back to List", "Index") %>
</div>
运行应用程序并访问/StoreManager/Edit/5,我们可以看到Edit视图显示的唱片编辑界面:

使用编辑器模板我们看到了编辑器模板产生的视图代码,也看到了视图显示的表单。现在我们使用Html.EditForModel()方法来创建Edit表单,这有某些优点,包括可以重复使用应用程序中其它视图的表单。 把视图代码改成如下所示: <% using (Html.BeginForm()) {%>
<%: Html.ValidationSummary(true) %>

<fieldset>
<legend>Fields</legend>
<%: Html.EditorForModel() %>

<p>
<input type="submit" value="Save" />
</p>
</fieldset> <% } %> <div>
<%: Html.ActionLink("Back to List", "Index") %>
</div>
上面的代码表示我们想为模型创建编辑视图,再次运行应用程序并访问/StoreManager/Edit/5,显示了一个几乎一样的表单。唯一不同的是它显示了AlbumId字段,而之前这个字段使用了隐藏域。

我们看到有两种方法可以产生Edit表单: 1.使用编辑器视图模板在设计时为所有属性生成编辑字段,这意味着在Edit.aspx视图中以代码形式显示各个表单字段。
2.使用ASP.NET MVC的模板特性在表单显示时生成字段。
这两种方式在不同的情况下都非常有用,在这个例子中,我们仍然使用Html.EditForModel()以便网站的其它视图能重复使用表单。 创建共享的Album编辑器模板 Album的Edit视图和Create视图有相同的表单字段,我们可以利用一个共享的Album编辑器模板。重复使用编辑器模板可以给我们的网站用户一个风格一致的用户界面,它也使得我们的代码更易于管理和维护,并消除视图之间重复的代码。 ASP.NET MVC的模板也遵循命名约定,因此文件和文件夹的名称非常重要。当我们在视图中使用Html.EditForModel()时,MVC运行库检查在 /Views/Shared/EditorTemplates 目录中检查与模型名称相同的模板,如果找到了,则使用它而不是默认模板,这可以让我们为每种不同的模型自定义显示风格。 首先在 /Views/Shared 目录中创建一个EditorTemplates文件夹,然后在该文件夹中创建一个视图:

修改自动生成的代码,把下面的代码: <div class="editor-label">
<%: Html.LabelFor(model => model.AlbumId) %>
</div>
改成: <div class="editor-label">
<%: Html.Label(“唱片Id") %>
</div>
运行应用程序并访问/StoreManager/Edit/5,可以看到使用了我们刚添加的模板:

后面用MVC 3了,实际上控制器部分的代码并没有什么改变,主要是视图使用了Razor语法。 使用ViewBag给视图传递附加信息 接下来为修改Edit表单,使用下拉框显示Genre和Artist而不是用文本框来显示GenreId和ArtistId。要这样做,视图还需要一些数据,确切地说,它需要: 1.当前编辑的Album对象
2.所有类别列表,我们用它来填充Genre下拉框
3.所有Artist列表,我们用它来填充Artist下拉框
控制器可以使用ViewBag传递附加信息给视图,例如我们需要的Genre和Artist列表。 修改StoreManagerController的Edit操作: public ActionResult Edit(int id)
{
ViewBag.Genres = storeDB.Genres.OrderBy(g
=> g.Name).ToList();
ViewBag.Artists = storeDB.Artists.OrderBy(a => a.Name).ToList();
var album = storeDB.Albums.Single(a => a.AlbumId == id);

return View(album);
} 这里,控制器把一个Album模型传递给Edit视图,Edit视图显示Album编辑器模板,Album模板通过访问ViewBag可以得到 要显示Genre和Artist下拉框需要的所有信息,我们现在可以为Album编辑器模板设置Genre和Artist下拉框了。 在Album编辑器模板中实现下拉框 我们使用另一个Html Helper的方法Html.DropDownList创建下拉框,让我们看看需要为Artist下拉框传递哪些信息: 1.表单字段的名称(ArtistId)
2.下拉框的列表值,我们给它传递一个SelectList
3.表单回发时提交的数据值
4.下拉框中显示的数据文本
5.表单显示时,下拉框中选定的当前值
我们的调用看起来如下: @Html.DropDownList("ArtistId",
new SelectList(ViewBag.Artists as System.Collections.IEnumerable,
"ArtistId", "Name", Model.ArtistId))
下面是Album.cshtml编辑器模板中的完整代码: @model MvcMusicStore.Models.Album
<p>
@Html.LabelFor(model =>
model.Genre)
@Html.DropDownList("GenreId",
new SelectList(ViewBag.Genres as System.Collections.IEnumerable,
"GenreId", "Name", Model.GenreId))
</p>
<p>
@Html.LabelFor(model => model.Artist)
@Html.DropDownList("ArtistId",
new SelectList(ViewBag.Artists as System.Collections.IEnumerable,
"ArtistId", "Name", Model.ArtistId))
</p>
<p>
@Html.LabelFor(model =>
model.Title)
@Html.TextBoxFor(model =>
model.Title)
@Html.ValidationMessageFor(model
=> model.Title)
</p>
<p>
@Html.LabelFor(model =>
model.Price)
@Html.TextBoxFor(model =>
model.Price)
@Html.ValidationMessageFor(model
=> model.Price)
</p>
<p>
@Html.LabelFor(model =>
model.AlbumArtUrl)
@Html.TextBoxFor(model =>
model.AlbumArtUrl)
@Html.ValidationMessageFor(model
=> model.AlbumArtUrl)
</p> 现在从StoreManager中编辑一个Album时,我们看到下拉框替代了原来显示ArtistId和GenreId的文本框。

实现HTTP-POST版的Edit操作方法 下面处理仓库管理员按下“Save”按纽后,表单以HTTP-POST方法将数据提交到服务器更新并保存它们的情况。我们使用具有两个参 数:ID(从路由参数中获取),FormCollection(从HTML表单中获取)的Edit操作方法实现上述业务逻辑。这个方法被[HTTP- POST]特性修饰,表示以HTTP-POST方式访问/StoreManager/Edit/[id]时,它将被调用。 这个控制器操作方法将执行三个步骤: 1.通过从网址中获得的Id从数据库中读取Album对象
2.用客户端回传的数据,调用控制器内建的UpdateModel方法尝试更新Album
3.把结果显示给用户:如果发生错误,重新显示表单,更新成功则返回到Album列表
下面的代码将用来实现上述步骤: //
// POST: /StoreManager/Edit/5
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
var album = storeDB.Albums.Find(id);

if(TryUpdateModel(album))
{
storeDB.SaveChanges();
return RedirectToAction("Index");
}
else
{
ViewBag.Genres = storeDB.Genres.OrderBy(g => g.Name).ToList();
ViewBag.Artists = storeDB.Artists.OrderBy(a => a.Name).ToList();
return View(album);
}
} 我们使用了[HTTP-POST]特性修饰这个方法,因此只有产生HTTP-POST回发时才会被调用。 我们准备修改一个唱片,运行应用程序并访问/Edit/5,如下所示修改唱片的类别和标题:

点击“Save”按纽,我们的更新被保存下来,并且返回到Store Manager Index,我们可以看到更新后的数据。

实现Create操作 现在在StoreManagerController中可以“Edit”唱片,下面我们让它同样具备“Create”的能力。 象之前的“Edit”一样,我们在控制器内实现两种不同情况下的Create操作,当管理人员首次访问/StoreManager /Create 时,一个方法显示空表单,另一个方法则处理管理员按下“Save”按纽,把表单数据以HTTP-POST方式提交到/StoreManager /Create 的情况。 实现HTTP-GET方式的Create操作方法 首先定义一个“Create”操作方法为用户显示一个Album的空表单: //
// GET: /StoreManager/Create
public ActionResult Create()
{
ViewBag.Genres = storeDB.Genres.OrderBy(g => g.Name).ToList();
ViewBag.Artists = storeDB.Artists.OrderBy(a => a.Name).ToList();
var album = new Album();
return View(album);
} 注意:你可能注意到了在Edit和Create操作中出现了重复的加载Genre和Artist的代码,这些代码可以用好几种方法重构以消除重复,不过在这个简单的例子中,我们在两个方法中都包含了这些代码。 在方法上单击右键创建Create视图:

修改生成的Create.cshtml视图模板,让它使用Album编辑器模板: @model MvcMusicStore.Models.Album
@{
ViewBag.Title = "Create";
}
<h2>Create</h2>
<script src="@Url.Content("/Scripts/jquery.validate.min.js")"
type="text/javascript"></script>
<script src="@Url.Content("/Scripts/jquery.validate.unobtrusive.min.js")"
type="text/javascript"></script>
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Create Album</legend>
@Html.EditorForModel()
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to
List", "Index")
</div> 运行应用程序,访问/StoreManager/Create ,看到一个空的“新建”表单:

最后,我们实现HTTP-POST方式的Create操作方法,以便管理人员把新的Album对象回传到服务器时调用,代码如下: //
// POST: /StoreManager/Create
[HttpPost]
public ActionResult Create(Album album)
{
if (ModelState.IsValid)
{
//Save Album
storeDB.Albums.Add(album);
storeDB.SaveChanges();
return RedirectToAction("Index");
}
// Invalid – redisplay with errors
ViewBag.Genres = storeDB.Genres.OrderBy(g => g.Name).ToList();
ViewBag.Artists = storeDB.Artists.OrderBy(a => a.Name).ToList();
return View(album);
} 和之前Edit方法中加载已存在的Album对象并使用UpdateModel方法不同,我们接收一个Album对象做为操作方法的参 数,ASP.NET MVC对自动从表单数据中创建Album对象,代码检查提交的数据是否有效,如果是则把新的Album对象保存到数据库中。 浏览/StoreManager/Create,我们可以看到空的编辑界面让我们输入新唱片的数据。

点击“Save”按纽,新唱片被追加到列表中:

处理删除 删除操作和之前的“Edit”和“Create”采用相同的方式,一个控制器操作显示“确认”表单,另一个控制器操作处理表单提交。 HTTP-GET方式的Delete操作方法和之前的Details操作方法完全一样: //
// GET: /StoreManager/Delete/5
public ActionResult Delete(int id)
{
var album = storeDB.Albums.Find(id);
return View(album);
} 添加一个Album的强类型视图,支架模板选择Delete:

Delete视图模板显示了模型的所有字段,我们把它简化一下: @model MvcMusicStore.Models.Album
@{
ViewBag.Title = "Delete";
}
<h2>Delete Confirmation</h2>
<p>Are you sure you want to delete the album titled
<strong>@Model.Title</strong>?
</p>
@using (Html.BeginForm()) {
<p>
<input type="submit" value="Delete" />
</p>
<p>
@Html.ActionLink("Back to
List", "Index")
</p>
} HTTP-POST方式的Delete操作要完成以下几个动作: 1.根据Id加载Album对象
2.删除对象并且保存
3.重定向到一个简单的“Deleted”视图模板,表明删除成功
//
// POST: /StoreManager/Delete/5
[HttpPost]
public ActionResult Delete(int id, FormCollection collection)
{
var album = storeDB.Albums.Find(id);

storeDB.Albums.Remove(album);
storeDB.SaveChanges();
return View("Deleted");
} 我们需要添加一个视图以显示Album已经被删除,添加一个名叫“Deleted”的非强类型的视图:

Deleted视图显示一条Album被删除的信息以及一个回到Store Manager Index的链接: @{
ViewBag.Title = "Deleted";
}
<h2>Deleted</h2>
<p>Your album was successfully deleted.</p>
<p>
@Html.ActionLink("Click
here", "Index")
to return to the album list.
</p> 要测试它,运行应用程序并访问/StoreManager,从列表中选择一个唱片点击“Delete”:

显示确认删除界面:

点击“Delete”按纽删除唱片,并显示删除完成界面:

返回Store Manager Index页面,刚才选择的唱片已经被删除:
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: