MVC3课程中的几个问题整理

这是几个小问题,整理出来给大家参考

 

1. 如何为不同类型的属性设置不同的编辑界面

备注:这个实例的源代码,可以通过这里下载 MvcApplicationEditTemplate.rar

 

我们探讨到了针对不同的属性,MVC3其实可以专门定制不同的编辑界面。这个概念,其实早在MVC出来之前,就曾经有过。之前有一个Dynamic Data的网站模板 ,里面大致也是用到了所谓的编辑器模板(FieldTemplates)的概念。

我们可以大致看一眼之前的做法

image

注意,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,但我个人觉得那不是一个很好的做法。

预览效果

image

我们的需求就是,将Birthday下面这个文本框定制一下,让他可以自动显示出来一个日历。(我们将采用jquery ui来实现)

 

添加一个EditorTemplate

由于Birthday属性是DateTime类型,所以我们可以针对这个类型设计一个编辑器模板。为了共享,我们可以将这个模板放在Shared目录下面

image

请注意,这里的目录名必须叫做: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>

最后预览得到的效果就是下面这样。按照这样的思路,我们还可以给其他类型的属性指定编辑器模板。

 

image

 

这个实例的源代码,可以通过这里下载 MvcApplicationEditTemplate.rar

 

2. 为什么脚本移动位置之后,页面的样式变化了

这是一个小问题,我们先来看一下默认情况下MVC项目的样式

image

这个布局和样式,是在_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>

看起来很好,但是再次打开网站的话,就会发现网页的样式有些问题

image

这是为什么呢?原因是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里面去。这样就和谐了

image

值得一说的是,这个库只有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。

接下来,在视图中,要添加两个脚本引用,如下所示

image

远程验证的效果如下

image

posted @ 2011-12-13 11:27  陈希章  阅读(5363)  评论(13编辑  收藏  举报