繁体   English   中英

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

[英]How can I change the input element name attribute value in a razor view model using a custom attribute in a model?

我有以下内容:

@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; }
}

目前,输入元素包含名称属性,其值为“ SearchPhrase”,但我希望该值仅为“ q”而不重命名该属性。

我希望有一个扩展名,该扩展名使我可以调用TextBoxFor而不需要提供Name属性,以便使custom属性以某种方式将Name属性的值自动设置为custom属性中指定的值。

以下是我的意思的示例:

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 name属性:

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;
}

然后在剃刀视图中使用此html帮助器:

@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正确的属性。

编辑:如果您遇到问题,则不必处理情况2,因为您发送了GET请求,并且将在查询字符串中获取表单参数。 因此,您可以将动作签名写为:

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

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

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

在这种情况下,查询字符串(或请求有效负载)在名为q的参数中进行搜索查询(因为输入的名称为q并且html表单以键值形式由包含输入名称和输入值的键发送请求)。 因此,MVC无法将q绑定到LoginViewModel SearchPhrase ,您将获得404。

我知道这不是您明确要求的,但我认为拥有与实际表单名称不同的ViewModel名称会破坏MVC的核心约定之一,并且可能会引起误解。

或者,您可以将新属性添加到镜像SearchPhrase并具有正确名称的VM:

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>

这将使您可以将代码保留在后端中,而不是使用q而不是q来引用SearchPhrase ,从而使程序员更轻松。 希望该视图不会分布到所有地方,并且您只有一个EditorTemplate或一部分。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM