键盘敲烂,月薪过万作业不做,等于没学
当前系列: ASP.NET 修改讲义

说明:form里面@Html引导的控件,都有两种形式:

  1. 后缀为For:用Lambda表达式传递相应的Model属性,控件的name和id由属性名生成,因为能得到智能提示和编译时检查,总是推荐使用
  2. 没有For后缀:用字符串指定生成控件的name和id。除非特殊情况,不推荐使用

接下来我们只讲第1种使用方式。


其他文本域

除了textbox,还有:

  • 密码框 (input type="password")
    @Html.PasswordFor(m => m.Password)
  • 多行文本框(textarea)
    @Html.TextAreaFor(m => m.Name, new {cols=10, rows=50 })
  • 隐藏文本域 (input type="hidden")
    @Html.HiddenFor(m => m.Name)

label

另外,label也由HtmlHelper方法生成:

@Html.LabelFor(m => m.Name)
但作用不大,以为填充label的是属性名(英文Name),要使用自定义的文字,需要在Model属性上添加:
[Display(Name = "用户名")]
public string Name { get; set; }


RadioButton

生成单选框(input type="radio")
    <label>
        @Html.RadioButtonFor(m => m.IsMale, true) 男
    </label>
    <label>
        @Html.RadioButtonFor(m => m.IsMale, false) 女
    </label>

第一个参数lambda表达式确定radio的name和id,第二个参数(必填)确定radio的value,注意要和其绑定的类型匹配(不匹配也报错,差评!)

RadioButton绑定的类型,常常是:

  • bool值(如上所示),或
    public bool IsMale { get; set; }
  • 枚举:
    <label>
        @Html.RadioButtonFor(m => m.DayOfWeek, DayOfWeek.Monday) 星期一
    </label>
    <label>
        @Html.RadioButtonFor(m => m.DayOfWeek, DayOfWeek.Tuesday) 星期二
    </label>
    public DayOfWeek DayOfWeek { get; set; }

默认选中

注意页面加载时radio的“默认选中”由以下规则确定:

  1. 如果属性(比如IsMale)已经被赋值,则根据赋值确定谁被选中。赋值包括:
    • 显式赋值
      return View(new Student { IsMale = true });
    • 隐式赋值
      return View(new Student()); //所以IsMale为false
  2. 否则,
    • View()没有传model
      return View();     //model都为null,IsMale更是为null
    • IsMale是可空类型,且只有默认值null
      public bool? IsMale { get; set; }
    可以由htmlAttributes指定checked
    @Html.RadioButtonFor(m => m.IsMale, false, new { @checked = true }) 女
  3. 所有radio都不会被选中


DropdownList

生成select表单元素。

和radio一样,需要第二个参数IEnumerable<SelectListItem>指明options:

@Html.DropDownListFor(m => m.DayOfWeek, Model.Options)

可以在Model中指定:

public IEnumerable<SelectListItem> Options { get; set; }

然后在return View(new Student{})中为其赋值:

Options = new List<SelectListItem>
{
    new SelectListItem{ Text = "星期一", Value= DayOfWeek.Monday.ToString(), Disabled = true },
    new SelectListItem{ Text = "星期二", Value= DayOfWeek.Tuesday.ToString() , Selected = true},
    new SelectListItem{ Text = "星期三", Value= DayOfWeek.Wednesday.ToString() },
    new SelectListItem{ Text = "星期天", Value= DayOfWeek.Sunday.ToString()},
}

SelectListItem中的属性:Text/Value/Disabled都一目了然,但Selected的设置优先级不如“作为用户选中值”绑定的DayOfWeek。即:只有在DayOfWeek(可空)为null的时候,这里的Selected才有作用。

另外注意

  1. SelectItem的Value值只能是string类型
  2. 但选中的值可以是任意类型(比如int),这样仍然会生成下拉列表并在POST中获得选中的值,但Selected会失效(bug)

SelectList

更多的时候,(因为“类职责划分”的原因)我们Model中不会放SelectListItem的集合,而是这样:

Options = new List<DayOfWeek> { DayOfWeek.Sunday, DayOfWeek.Monday, DayOfWeek.Tuesday }

ASP.NET MVC早就预见了这种场景,所以提供了SelectList类和它的构造函数:

@Html.DropDownListFor(m => m.DayOfWeek,
    new SelectList(Model.Options, 
        DayOfWeek.Tuesday,    //默认选中
        new List<DayOfWeek> { DayOfWeek.Sunday }    //disabled项
    ))
可以将任意集合转化成IEnumerable<SelectListItem>。

text和value

实际开发中,Options中存放的更有可能是一个一个的自定义对象,比如:

public IList<Major> Options { get; set; }
public class Major
{
    public int Id { get; set; }
    public string Name { get; set; }
return View(new Student
{
    Options = new List<Major> { 
        new Major{ Id = 1, Name = "C#"},
        new Major{ Id = 2, Name = "SQL"},
        new Major{ Id = 3, Name = "ASP.NET"},

但这样的话,下拉列表就变得很怪异:

因为我们还没有指定它的text和value。需要在SelectList中另外两个参数dataValueField和dataTextField:

@Html.DropDownListFor(m => m.SelectedMajor.Id,
    new SelectList(
        Model.Options, "Id", "Name"
    ))

注意其中lambda表达式最后确定的属性,要和dataValueField匹配,或者直接:

@Html.DropDownListFor(m => m.SelectedMajorId,
另外,DropDownListFor还可以带一个参数:optionLabel,表示添加一个默认的空选项:
@Html.DropDownListFor(m => m.SelectedMajorId,
    new SelectList(
        Model.Options, "Id", "Name"
    ), "---------")

选中此项,等同于“用户没选”,Model绑定时对应的属性值为null(演示)


Checkbox

生成复选框(input type="checkbox")

@Html.CheckBoxFor(m => m.IsMale)

但是,方法CheckBoxFor()中的参数lamba需要返回值为bool:

public static MvcHtmlString CheckBoxFor<TModel>(this HtmlHelper<TModel> htmlHelper, 
    Expression<Func<TModel, bool>> expression);

当checkbox用于多选时,该限制会给我们后台Model的组织带来麻烦。比如,我们需要用户选择“在一周中的哪几天学习”,咋办呢?

原生HTML

@想一想@:这样的写法是不是很傻?

public bool StudyInMonday { get; set; }
public bool StudyInTuesday { get; set; }
public bool StudyInWednesday { get; set; }
//……

更多的时候,我们希望这样一个简单的Model属性:

public IList<DayOfWeek> StudyDays { get; set; }

把用户所选的时间全部放在这个集合中。

Array days = Enum.GetValues(typeof(DayOfWeek));
for (int i = 0; i < days.Length; i++)
{
    <label>
        <input type="checkbox" name="StudyDays" value="@days.GetValue(i)" /><br />
    </label>
}

我们在razor view中获取到DayOfWeek的Array,然后遍历,生成7个原生的HTML checkbox。

注意:为了在Action中成功Model绑定,name必须是StudyDays(同Model集合名),value必须是DayOfWeek的枚举值。

演示:Action参数student中正确获得用户选择

#体会#:

  • checkbox的传值方式:只有被选中的checkbox的值才会被post,未被选中的不会!复习 
  • Model绑定不依赖于@Html.XXX()

那么,

@Html.CheckboxFor

还有啥作用呢?

实际上,我们在开发过程中,很多时候Model的数据是从数据库来的,可选项是一个一个的(自定义)实体类的集合,类似于之前提到的IList<Major>,但是为了用于checkbox需要多加一个bool值:

public class Major
{
    public int Id { get; set; }
    public bool Selected { get; set; }

这样,在razor view里,就可以这样声明:

for (int i = 0; i < Model.Options.Count; i++)
{
    <label>
        @Html.CheckBoxFor(m => Model.Options[i].Selected)
        @Model.Options[i].Name

演示:同时生成了

隐藏文本域

<input name="Options[0].Selected" type="hidden" value="false">

注意它的name(和checkbox同名)和value(总是为false)

为什么呢?因为只有这样,所有的options才会再次被post到后台。

#理解#:

  • 即使用户没有勾选某个checkbox,但因为有同名的hide input,后台一样会收到一个false的值,所以Model绑定会生成相应的对象……
  • 如果用户勾选了某个checkbox,后台会收到两个值:true,false,Model绑定会用true覆盖false

这样,在Action中,我们就可以遍历Options,既知道哪些option被选中,也知道哪些option被选中!

for (int i = 0; i < student.Options.Count; i++)
{
    if (student.Options[i].Selected) { }
    else { } //未被用户选中

for vs foreach

演示razor view中使用foreach进行遍历,后台Model绑定失败

因为生成的HTML标签,name变成了item.Selected,Model中找不到item属性呀(复习:Model绑定规则

<input id="item_Selected" name="item.Selected" type="checkbox" value="true">

对比for循环的name:Options[0].Selected

<input data-val="true" data-val-required="Selected 字段是必需的。" id="Options_0__Selected" name="Options[0].Selected" type="checkbox" value="true">

PS:

  • 复杂类型集合,Model绑定要求Request键值对中name要有[]带下标
  • 且一定要从0开始,否则一样会绑定失败。

Id去哪儿了?

断点演示:其实不光Id,Name也都没值呢!

@想一想@:为什么?因为POST的时候没有Id相关的数据呀。

#体会#:

  • POST时只传输“特定”的数据,
  • Model绑定完全依赖于前端传回的数据

解决办法:利用hide input……

@Html.HiddenFor(m => Model.Options[i].Id)


作业

利用HtmlHelper(文件上传除外)完成以下页面:

  1. 注册
  2. 登录
  3. 忘记密码
  4. 联系方式
  5. 用户资料
  6. 发布求助
  7. 发布文章
  8. 求督促
  9. 我的消息
并确保能在后台通过model绑定接收到用户输入。  

下拉列表所需数据Controller中直接生成即可。

学习笔记
源栈学历
大多数人,都低估了编程学习的难度,而高估了自己的学习能力和毅力。

作业

觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 ASP.NET 中继续学习:

多快好省!前端后端,线上线下,名师精讲

  • 先学习,后付费;
  • 不满意,不要钱。
  • 编程培训班,我就选源栈

更多了解 加:

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧

公众号:源栈一起帮

二维码