这是几个小问题,整理出来给大家参考
1. 如何为不同类型的属性设置不同的编辑界面
备注:这个实例的源代码,可以通过这里下载 MvcApplicationEditTemplate.rar
我们探讨到了针对不同的属性,MVC3其实可以专门定制不同的编辑界面。这个概念,其实早在MVC出来之前,就曾经有过。之前有一个Dynamic Data的网站模板 ,里面大致也是用到了所谓的编辑器模板(FieldTemplates)的概念。
我们可以大致看一眼之前的做法
注意,Dynamic Data这个模板,是基于Web Forms实现的一个应用架构。这里不作进一步展开,如果有兴趣的朋友,可以参考:http://msdn.microsoft.com/en-us/library/ee845452.aspx
提一下Dynamic Data,是希望给大家一个初步印象,确实可以针对不同类型的字段,设置不同的界面来编辑或者显示。MVC中也提供了这样的机制。
下面我们来做一个常见的例子,我们希望那些类型为DateTime的属性,在页面上编辑的时候,自动调用jquery的一个日历效果让用户可以选择,而不是简单的一个文本框。
准备一个业务实体类型(Employee)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace MvcApplicationEditTemplate.Models
{
public class Employee
{
public int ID { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Birthday { get; set; }
}
}
准备一个HomeController
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcApplicationEditTemplate.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
var emp = new Models.Employee();
return View(emp);
}
}
}
为这个Action生成一个Edit视图
@model MvcApplicationEditTemplate.Models.Employee
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Employee</legend>
@Html.HiddenFor(model => model.ID)
<div class="editor-label">
@Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.FirstName)
@Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.LastName)
@Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Birthday)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Birthday)
@Html.ValidationMessageFor(model => model.Birthday)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
注意看这里的语法:EditorFor。这是MVC比较推荐的做法,我发现不少朋友可能还是习惯自己去些input,但我个人觉得那不是一个很好的做法。
预览效果
我们的需求就是,将Birthday下面这个文本框定制一下,让他可以自动显示出来一个日历。(我们将采用jquery ui来实现)
添加一个EditorTemplate
由于Birthday属性是DateTime类型,所以我们可以针对这个类型设计一个编辑器模板。为了共享,我们可以将这个模板放在Shared目录下面
请注意,这里的目录名必须叫做:EditorTemplates。那个模板的名称也必须叫DateTime.cshtml。 再一次领教了MVC中约定胜于配置的特性吧。
@model System.DateTime
@Html.TextBox("",ViewData.TemplateInfo.FormattedModelValue,new {date_picker=true})
在这个文件中,我们只需要指定两行代码。(这是Razor表达式语法),从上面的语法可以看出,我们其实还是放了一个文本框(TextBox),但这里的关键在于,我们会通过jquery来给这个文本框添加特效。
添加一个jscript文件(EditorBehavior.js)
/// <reference path="jquery-1.5.1-vsdoc.js" />
$(function () {
$(":input[date-picker]").datepicker();
});
在视图中引入这个脚本文件
@model MvcApplicationEditTemplate.Models.Employee
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
<script src="@Url.Content("~/Scripts/jquery-ui-1.8.11.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/EditorBehavior.js")" type="text/javascript"></script>
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Employee</legend>
@Html.HiddenFor(model => model.ID)
<div class="editor-label">
@Html.LabelFor(model => model.FirstName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.FirstName)
@Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.LastName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.LastName)
@Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="editor-label">
@Html.LabelFor(model => model.Birthday)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Birthday)
@Html.ValidationMessageFor(model => model.Birthday)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
最后预览得到的效果就是下面这样。按照这样的思路,我们还可以给其他类型的属性指定编辑器模板。
这个实例的源代码,可以通过这里下载 MvcApplicationEditTemplate.rar
2. 为什么脚本移动位置之后,页面的样式变化了
这是一个小问题,我们先来看一下默认情况下MVC项目的样式
这个布局和样式,是在_layout.cshtml中定义好的
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
</head>
<body>
<div class="page">
<header>
<div id="title">
<h1>
My MVC Application</h1>
</div>
<div id="logindisplay">
@Html.Partial("_LogOnPartial")
</div>
<nav>
<ul id="menu">
<li>@Html.ActionLink("Home", "Index", "Home")</li>
<li>@Html.ActionLink("About", "About", "Home")</li>
</ul>
</nav>
</header>
<section id="main">
@RenderBody()
</section>
<footer>
</footer>
</div>
</body>
</html>
出于网站设计优化的考虑,我们知道应该尽可能地将javascript的引用放在页面底部。Yahoo有一个专门的文章讲这方面的内容:Best Practices for Speeding Up Your Web Site
所以,你可能会尝试将脚本移动到footer里面,例如下面这样
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />
</head>
<body>
<div class="page">
<header>
<div id="title">
<h1>
My MVC Application</h1>
</div>
<div id="logindisplay">
@Html.Partial("_LogOnPartial")
</div>
<nav>
<ul id="menu">
<li>@Html.ActionLink("Home", "Index", "Home")</li>
<li>@Html.ActionLink("About", "About", "Home")</li>
</ul>
</nav>
</header>
<section id="main">
@RenderBody()
</section>
<footer>
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")" type="text/javascript"></script>
</footer>
</div>
</body>
</html>
看起来很好,但是再次打开网站的话,就会发现网页的样式有些问题
这是为什么呢?原因是MVC3默认使用了HTML 5的语法来构建页面,目前HTML 5因为还没有成为事实上的标准,所以使用了一个专门的javascript库来提供支持。这个javascript库就是:modernizr-1.7.min.js, 这是一个开源的作品,请参考这里 http://www.modernizr.com/
在页面中用到的header,section,footer这类的tag其实是html 5专用的,所以如果上面这个脚本没有预先加载,可能显示的时候,就会有些问题。
所以,我们应该将modernizr-1.7.min.js放在header里面去。这样就和谐了
值得一说的是,这个库只有10KB左右,所以将它放在header里面对性能影响很小。
我们还可以使用微软提供的CDN功能,尽可能地减少用户需要下载的javascript的体积。关于CDN,请参考这里:http://www.asp.net/ajaxlibrary/cdn.ashx
3.Remote验证
MVC3提供了一个新增的数据验证功能,叫做Remote验证,意思是可以利用客户端javascript,发起异步的请求,调用服务器代码进行验证。这个功能很不错,例如有一个页面提供用户输入用户名进行注册,我们经常需要检测用户名是否已经被别人占用。这种验证显然是无法在客户端直接提供,而是需要利用服务器代码来实现。
下面来看一个例子
首先,我们要为类型定义Annotation,需要注意的是,Remote这个Annotation是MVC专用的,所以要using System.Web.Mvc
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace MvcApplicationEditTemplate.Models
{
public class Employee
{
public int ID { get; set; }
[Required]
[Remote("IsNameValid","Home")]
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Birthday { get; set; }
}
}
这里指定了一个远程验证用的Controller(Home)和Action(IsNameValid)
所以,接下来我们准备这样一个Action
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MvcApplicationEditTemplate.Controllers
{
public class HomeController : Controller
{
//
// GET: /Home/
public ActionResult Index()
{
var emp = new Models.Employee();
return View(emp);
}
public ActionResult IsNameValid(string FirstName)
{
if(FirstName == "ares")
{
return Json(true, JsonRequestBehavior.AllowGet);
}
return Json("Your name is invalid", JsonRequestBehavior.AllowGet);
}
}
}
注意,这里只是做了一个例子,直接比较FirstName是不是等于ares。实际情况下,这里可以执行数据库查询,得到结果。
还需要注意的是,这里必须返回json的数据,并且必须设置为AllowGet。
接下来,在视图中,要添加两个脚本引用,如下所示
远程验证的效果如下