如何使用模型中的自定义属性更改razor视图模型中的输入元素名称属性值?
本文关键字:模型 元素 属性 输入 razor 自定义属性 何使用 视图 | 更新日期: 2023-09-27 17:54:53
我有以下内容:
@model Pharma.ViewModels.SearchBoxViewModel
<div class="smart-search">
@using (Html.BeginForm("Index", "Search", FormMethod.Get, new { @class = "form-horizontal", role = "form" }))
{
<div class="form-group">
<div class="hidden-xs- col-sm-1 col-md-1 col-lg-1 text-right">
@Html.LabelFor(m => m.SearchPhrase, new { @class = "control-label" })
</div>
<div class="col-xs-8 col-sm-8 col-md-9 col-lg-10">
@Html.TextBoxFor(m => m.SearchPhrase, new { @class = "form-control" })
</div>
<div class="col-xs-4 col-sm-3 col-md-2 col-lg-1">
<input type="submit" value="Search" class="btn btn-default" />
</div>
</div>
}
</div>
可以看到,这是在创建一个输入元素。
传递给视图的视图模型包含以下内容:
public class SearchBoxViewModel
{
[Required]
[Display(Name = "Search")]
public string SearchPhrase { get; set; }
}
目前,输入元素包含一个name属性,其值为"SearchPhrase",但我希望该值仅为"q",而不重命名该属性。
我更喜欢一个扩展,允许我调用TextBoxFor,但不需要提供Name属性,以便自定义属性以某种方式将Name属性的值自动设置为自定义属性中指定的值。
下面是我的意思的一个例子:
public class SearchBoxViewModel
{
[Required]
[Display(Name = "Search")]
[Input(Name = "q")]
public string SearchPhrase { get; set; }
}
结合:@model Pharma.ViewModels.SearchBoxViewModel
<div class="smart-search">
@using (Html.BeginForm("Index", "Search", FormMethod.Get, new { @class = "form-horizontal", role = "form" }))
{
<div class="form-group">
<div class="hidden-xs- col-sm-1 col-md-1 col-lg-1 text-right">
@Html.LabelFor(m => m.SearchPhrase, new { @class = "control-label" })
</div>
<div class="col-xs-8 col-sm-8 col-md-9 col-lg-10">
@Html.TextBoxFor(m => m.SearchPhrase, new { @class = "form-control" })
</div>
<div class="col-xs-4 col-sm-3 col-md-2 col-lg-1">
<input type="submit" value="Search" class="btn btn-default" />
</div>
</div>
}
</div>
这将产生类似下面的内容:
<div class="smart-search">
<form action="/Search/Index" method="get" class="form-horizontal" role="form">
<div class="form-group">
<div class="hidden-xs- col-sm-1 col-md-1 col-lg-1 text-right">
<label for="Search" class="control-label">Search</label>
</div>
<div class="col-xs-8 col-sm-8 col-md-9 col-lg-10">
<input type="text" name="q" id="Search" value="" class="form-control" />
</div>
<div class="col-xs-4 col-sm-3 col-md-2 col-lg-1">
<input type="submit" value="Search" class="btn btn-default" />
</div>
</div>
</form>
</div>
我希望这个自定义属性在使用SearchBoxViewModel时生效,而不管使用什么模板来防止错误,目的是让程序员清楚,同时为用户创建一个用户友好的查询字符串。
是否可以使用SearchPhrase属性上的自定义属性来做到这一点,其方式与更改显示名称的方式类似?
我写了一些简单的东西,但可以作为编写完整解决方案的开始。
首先,我用您提供的名称写了一个简单的Attribute
:
public class InputAttribute : Attribute
{
public string Name { get; set; }
}
然后我写了一个Html帮助包装默认的TextBoxFor
和搜索Input
属性,如果有,它将替换从TextBoxFor
生成的HtmlString
的名称属性:
public static MvcHtmlString MyTextBoxFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, System.Linq.Expressions.Expression<Func<TModel, TProperty>> expression, object htmlAttributes)
{
var memberExpression = expression.Body as MemberExpression;
var attr = memberExpression.Member.GetCustomAttribute(typeof (InputAttribute)) as InputAttribute;
var result = htmlHelper.TextBoxFor(expression, htmlAttributes);
if (attr != null)
{
var resultStr = result.ToString();
var match = Regex.Match(resultStr, "name='''"''w+'''"");
return new MvcHtmlString(resultStr.Replace(match.Value, "name='"" + attr.Name + "'""));
}
return result;
}
然后在razor视图中使用这个html helper:
@Html.MyTextBoxFor(m => m.SearchPhrase, new { @class = "form-control" })
你们的型号如下:
public class SearchBoxViewModel
{
[Required]
[Display(Name = "Search")]
[Input(Name = "q")]
public string SearchPhrase { get; set; }
}
这是一个完整的解决方案:
- 你必须实现
TextBoxFor
的所有过载 - 如果您尝试将表单数据发送到具有类型
SearchBoxViewModel
参数的操作,您将获得404,因为ModelBinder
不能将请求参数绑定到此ViewModel
。所以你必须写一个ModelBinder
来解决这个问题。 - 您必须相应地编写
LabelFor
以正确匹配for
属性。
EDIT:如果您的问题,您不必处理案例2,因为您发送GET
请求,您将获得查询字符串中的表单参数。所以你可以这样写你的动作签名:
public ActionResult Search(string q)
{
// use q to search
}
当您的操作参数中有非基本类型时,就会出现问题。在这种情况下,ModelBinder
尝试匹配查询字符串项目(或请求有效负载)的属性类型的动作参数。例如:
public ActionResult Search(SearchBoxViewModel vm)
{
// ...
}
在这种情况下,查询字符串(或请求负载)在一个名为q
的参数中有您的搜索查询(因为输入的名称是q
, html表单以由输入名称和输入值组成的键值形式发送请求)。所以MVC不能绑定q
到SearchPhrase
在LoginViewModel
,你会得到一个404。
我知道这不是你明确要求的,但我觉得与实际表单名称不同的ViewModel名称破坏了MVC中的核心约定之一,可能会产生误导。
作为一种替代方法,您可以向虚拟机添加一个新属性,该属性可以镜像SearchPhrase并具有适当的名称:
public class SearchBoxViewModel
{
[Required]
[Display(Name = "Search")]
public string SearchPhrase { get; set; }
[Display(Name = "Search")]
public string q
{
get { return SearchPhrase; }
set { SearchPhrase = value; }
}
}
改变你的视图使用这些:
@model Pharma.ViewModels.SearchBoxViewModel
<div class="smart-search">
@using (Html.BeginForm("Index", "Search", FormMethod.Get, new { @class = "form-horizontal", role = "form" }))
{
<div class="form-group">
<div class="hidden-xs- col-sm-1 col-md-1 col-lg-1 text-right">
@Html.LabelFor(m => m.q, new { @class = "control-label" })
</div>
<div class="col-xs-8 col-sm-8 col-md-9 col-lg-10">
@Html.TextBoxFor(m => m.q, new { @class = "form-control" })
</div>
<div class="col-xs-4 col-sm-3 col-md-2 col-lg-1">
<input type="submit" value="Search" class="btn btn-default" />
</div>
</div>
}
</div>
这将允许您在后端保持代码引用SearchPhrase
而不是q
,以使程序员更容易。希望这个视图不是到处都是,你只有一个EditorTemplate或部分。