【问题标题】:How can I change the input element name attribute value in a razor view model using a custom attribute in a model?如何使用模型中的自定义属性更改剃刀视图模型中的输入元素名称属性值?
【发布时间】:2015-08-26 00:13:32
【问题描述】:

我有以下几点:

@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 属性的扩展,以便自定义属性以某种方式自动将 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 属性上的自定义属性以类似于更改显示名称的方式执行此操作?

【问题讨论】:

  • 你的意思是文本框中的文本应该默认为'q'吗?
  • 这不行吗? @Html.TextBoxFor(m =&gt; m.SearchPhrase, new { @class = "form-control" , Name = "q"})
  • @AlwaysAProgrammer 不,我不想设置默认值
  • @GeorgeLee,在您的解决方案中,FORM 不会在文本框中发布输入的值? Bonner,你能解释一下你到底想要什么吗?
  • 您必须在 ViewModel 本身中更改属性的名称。您可能会编写自定义 HTML 帮助程序或 DisplayFor 和模板,但您不能使用 TextBoxFor 来完成,假设 GeorgeLee 的建议不起作用

标签: c# asp.net-mvc razor


【解决方案1】:

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

首先我用你提供的名字写了一个简单的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 助手:

@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。

【讨论】:

  • 我认为这是最有前途的解决方案,因为它符合我正在寻找的内容,但是我不确定你在第 2 点上的意思,你能澄清一下吗?
  • 我没有测试过。当您发布表单时,表单值通过键值对从输入值的名称中收集。所以请求正文有一些不同的东西不能与你的实体匹配。
【解决方案2】:

我知道这不是您明确要求的,但我认为 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 或部分。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-18
    • 2021-10-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-07-10
    • 1970-01-01
    相关资源
    最近更新 更多