如何使用模型中的自定义属性更改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属性上的自定义属性来做到这一点,其方式与更改显示名称的方式类似?

如何使用模型中的自定义属性更改razor视图模型中的输入元素名称属性值?

我写了一些简单的东西,但可以作为编写完整解决方案的开始。

首先,我用您提供的名称写了一个简单的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; }
}

这是一个完整的解决方案:

  1. 你必须实现TextBoxFor的所有过载
  2. 如果您尝试将表单数据发送到具有类型SearchBoxViewModel参数的操作,您将获得404,因为ModelBinder不能将请求参数绑定到此ViewModel。所以你必须写一个ModelBinder来解决这个问题。
  3. 您必须相应地编写LabelFor以正确匹配for属性。

EDIT:如果您的问题,您不必处理案例2,因为您发送GET请求,您将获得查询字符串中的表单参数。所以你可以这样写你的动作签名:

public ActionResult Search(string q)
{
  // use q to search
}

当您的操作参数中有非基本类型时,就会出现问题。在这种情况下,ModelBinder尝试匹配查询字符串项目(或请求有效负载)的属性类型的动作参数。例如:

public ActionResult Search(SearchBoxViewModel vm)
{
  // ...
}

在这种情况下,查询字符串(或请求负载)在一个名为q的参数中有您的搜索查询(因为输入的名称是q, html表单以由输入名称和输入值组成的键值形式发送请求)。所以MVC不能绑定qSearchPhraseLoginViewModel,你会得到一个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或部分。