42

ページに複数のフォームがあり、それぞれに同じ名前を使用しているが、異なるタイプ (radio/hidden/etc) として使用している場合、ASP.NET MVC に問題があるようです。最初のフォームの投稿(たとえば、「日付」ラジオボタンを選択します)、フォームが再レンダリングされる場合(結果ページの一部として)、他のフォームの SearchType の非表示の値という問題があるようです最後のラジオ ボタンの値 (この場合は SearchType.Name) に変更されます。

以下は、削減目的のフォームの例です。

<% Html.BeginForm("Search", "Search", FormMethod.Post); %>
  <%= Html.RadioButton("SearchType", SearchType.Date, true) %>
  <%= Html.RadioButton("SearchType", SearchType.Name) %>
  <input type="submit" name="submitForm" value="Submit" />
<% Html.EndForm(); %>

<% Html.BeginForm("Search", "Search", FormMethod.Post); %>
  <%= Html.Hidden("SearchType", SearchType.Colour) %>
  <input type="submit" name="submitForm" value="Submit" />
<% Html.EndForm(); %>

<% Html.BeginForm("Search", "Search", FormMethod.Post); %>
  <%= Html.Hidden("SearchType", SearchType.Reference) %>
  <input type="submit" name="submitForm" value="Submit" />
<% Html.EndForm(); %>

結果ページのソース (これは結果ページの一部になります)

<form action="/Search/Search" method="post">
  <input type="radio" name="SearchType" value="Date" />
  <input type="radio" name="SearchType" value="Name" />
  <input type="submit" name="submitForm" value="Submit" />
</form>

<form action="/Search/Search" method="post">
  <input type="hidden" name="SearchType" value="Name" /> <!-- Should be Colour -->
  <input type="submit" name="submitForm" value="Submit" />
</form>

<form action="/Search/Search" method="post">
  <input type="hidden" name="SearchType" value="Name" /> <!-- Should be Reference -->
  <input type="submit" name="submitForm" value="Submit" />
</form>

RC1を持っている他の誰かがこれを確認できますか?

多分それは私が列挙型を使用しているためです。知らない。隠しフィールドに 'manual' input () タグを使用することでこの問題を回避できることを付け加えておきますが、MVC タグ (<%= Html.Hidden(...) %>) を使用すると、.NET MVC がそれらを置き換えます。毎回。

どうもありがとう。

アップデート:

今日もこのバグを見ました。投稿されたページを返し、MVC set hidden form tags を Html ヘルパーで使用すると、これが頭を切り取るようです。私はこれについてPhil Haackに連絡しました。なぜなら、他に頼るべき場所がわからないからです。また、これが David によって指定された予期される動作であるとは思えません。

4

12 に答える 12

36

はい、この動作は現在仕様によるものです。明示的に値を設定している場合でも、同じURLにポストバックすると、モデルの状態を調べてそこで値を使用します。通常、これにより、元の値ではなく、ポストバックで送信した値を表示できます。

2つの可能な解決策があります:

解決策1

各フィールドには一意の名前を使用してください。デフォルトでは、指定した名前をHTML要素のIDとして使用することに注意してください。複数の要素が同じIDを持つことは無効なHTMLです。したがって、一意の名前を使用することをお勧めします。

解決策2

隠しヘルパーは使用しないでください。あなたは本当にそれを必要としないようです。代わりに、これを行うことができます:

<input type="hidden" name="the-name" 
  value="<%= Html.AttributeEncode(Model.Value) %>" />

もちろん、これについてもっと考えると、ポストバックに基づいて値を変更することは、テキストボックスには意味がありますが、非表示の入力にはあまり意味がありません。これをv1.0で変更することはできませんが、v2で検討します。しかし、私たちはそのような変化の意味を注意深く考える必要があります。

于 2009-03-06T17:47:09.033 に答える
6

私はちょうど同じ問題に遭遇しました。渡された値に対するTextBox()の優先順位のようなHTMLヘルパーは、ドキュメントから推測したものとは正反対に動作するように見えます。

テキスト入力要素の値。この値がnull参照(Visual BasicではNothing)の場合、要素の値はViewDataDictionaryオブジェクトから取得されます。そこに値が存在しない場合、値はModelStateDictionaryオブジェクトから取得されます。

私には、渡された場合の値が使用されることを読みました。しかし、TextBox()ソースを読む:

string attemptedValue = (string)htmlHelper.GetModelStateValue(name, typeof(string));
tagBuilder.MergeAttribute("value", attemptedValue ?? ((useViewData) ? htmlHelper.EvalString(name) : valueParameter), isExplicitValue);

実際の順序が文書化されているものとは正反対であることを示しているようです。実際の注文は次のようです。

  1. ModelState
  2. ViewData
  3. 値(呼び出し元によってTextBox()に渡されます)
于 2009-10-03T03:05:30.227 に答える
6

注意 - このバグは MVC 3 にもまだ存在します。私は Razor マークアップ構文を使用していますが (これは非常に重要です)、オブジェクト プロパティに対して毎回同じ値を生成する foreach ループで同じバグに遭遇しました。

于 2011-10-13T13:56:22.683 に答える
5

これは予想される動作です。MVC はビューステートやその他の背後にあるトリックを使用してフォームに追加情報を渡すことはありません。名前と値のペアのリスト)。

MVC がフォームをレンダリングするとき、同じ名前の送信された値が存在するかどうかを確認するだけです。繰り返しますが、名前付きの値がどのフォームから来たのか、またはそれがどのタイプのコントロールであったか (ラジオ、テキスト、または非表示を使用します。HTTP 経由で送信された場合はすべて name=value です)。

于 2009-02-27T13:25:45.133 に答える
4
foreach (var s in ModelState.Keys.ToList())
                if (s.StartsWith("detalleProductos"))
                    ModelState.Remove(s);

ModelState.Remove("TimeStamp");
ModelState.Remove("OtherOfendingHiddenFieldNamePostedToSamePage1");
ModelState.Remove("OtherOfendingHiddenFieldNamePostedToSamePage2");

return View(model);
于 2011-02-15T01:00:18.413 に答える
4

この問題は MVC 5 にまだ存在しており、バグとは見なされていないことは明らかです。

設計上ではありますが、これは予期された動作ではないことがわかりました。むしろ、非表示フィールドの値が他のタイプのフィールドと同様に動作し、特別に扱われたり、あいまいなコレクションからその値を取得したりしないようにする必要があります (ViewState を思い起こさせます)。

いくつかの結果 (私たちにとって正しい値はモデル値であり、間違っているのは ModelState 値です):

  • Html.DisplayFor()正しい値を表示します(モデルから取得します)
  • Html.ValueForしません (ModelState からプルします)。
  • ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model正しい値を引き出します

私たちの解決策は、独自の拡張機能を実装することです。

        /// <summary>
        /// Custom HiddenFor that addresses the issues noted here:
        /// http://stackoverflow.com/questions/594600/possible-bug-in-asp-net-mvc-with-form-values-being-replaced
        /// We will only ever want values pulled from the model passed to the page instead of 
        /// pulling from modelstate.  
        /// Note, do not use 'ValueFor' in this method for these reasons.
        /// </summary>
        public static IHtmlString HiddenTheWayWeWantItFor<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper,
                                                    Expression<Func<TModel, TProperty>> expression,
                                                    object value = null,
                                                    bool withValidation = false)
        {
            if (value == null)
            {
                value = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData).Model;
            }

            return new HtmlString(String.Format("<input type='hidden' id='{0}' name='{1}' value='{2}' />",
                                    htmlHelper.IdFor(expression),
                                    htmlHelper.NameFor(expression),
                                    value));
        }
于 2014-02-18T15:39:40.153 に答える
3

「設計上の問題」を再現する例と考えられる回避策。ただし、「バグ」を見つけようとして失われた 3 時間の回避策はありません。この「設計」はまだ ASP.NET MVC 2.0 RTM にあることに注意してください。

    [HttpPost]
    public ActionResult ProductEditSave(ProductModel product)
    {
        //Change product name from what was submitted by the form
        product.Name += " (user set)";

        //MVC Helpers are using, to find the value to render, these dictionnaries in this order: 
        //1) ModelState 2) ViewData 3) Value
        //This means MVC won't render values modified by this code, but the original values posted to this controller.
        //Here we simply don't want to render ModelState values.
        ModelState.Clear(); //Possible workaround which works. You loose binding errors information though...  => Instead you could replace HtmlHelpers by HTML input for the specific inputs you are modifying in this method.
        return View("ProductEditForm", product);
    }

フォームに最初にこれが含まれている場合:<%= Html.HiddenFor( m => m.ProductId ) %>

"名前" の元の値 (フォームがレンダリングされたとき) が "ダミー" である場合、フォームが送信された後、レンダリングされた "ダミー (ユーザー セット)" が表示されます。なくModelState.Clear()ても「ダミー」が表示されます!!!!!!

正しい回避策:

<input type="hidden" name="Name" value="<%= Html.AttributeEncode(Model.Name) %>" />

すべての mvc フォーム開発者はそれを念頭に置く必要があるため、これはまったく良い設計ではないと思います。

于 2010-03-23T07:08:27.873 に答える
1

これは「設計による」可能性がありますが、文書化されているものではありません。

Public Shared Function Hidden(  

  ByVal htmlHelper As System.Web.Mvc.HtmlHelper,  
  ByVal name As String, ByVal value As Object)  
As String  

System.Web.Mvc.Html.InputExtensions のメンバー

概要: 非表示の入力タグを返します。

パラメータ:
htmlHelper: HTML ヘルパー。
name: 値の検索に使用されるフォーム フィールド名と System.Web.Mvc.ViewDataDictionary キー。
value: 非表示の入力の値。null の場合は、System.Web.Mvc.ViewDataDictionary を調べ、次に System.Web.Mvc.ModelStateDictionary を調べて値を探します。

これは、値パラメーターが null (または指定されていない) の場合にのみ、HtmlHelper が他の場所で値を探すことを示唆しているようです。

私のアプリでは、次のフォームがあります: html.Hidden("remote", True) は次のようにレンダリングされます <input id="remote" name="remote" type="hidden" value="False" />

ViewData.ModelState ディクショナリの内容によって値が上書きされていることに注意してください。

または、何か不足していますか?

于 2009-09-02T14:52:51.987 に答える
1

したがって、MVC 4 には「設計上の問題」がまだ残っています。コレクションに正しい非表示の値を設定するために使用しなければならなかったコードは次のとおりです。コントローラーで何をしても、ビューには常に間違った値が表示されるためです。

古いコード

for (int i = 0; i < Model.MyCollection.Count; i++)
{
    @Html.HiddenFor(m => Model.MyCollection[i].Name) //It doesn't work. Ignores what I changed in the controller
}

更新されたコード

for (int i = 0; i < Model.MyCollection.Count; i++)
{
    <input type="hidden" name="MyCollection[@(i)].Name" value="@Html.AttributeEncode(Model.MyCollection[i].Name)" /> // Takes the recent value changed in the controller!
}

彼らはMVC 5でこれを修正しましたか?

于 2013-12-11T21:50:57.627 に答える
0

他の人が示唆しているように、HtmlHelpers (TextBoxFor、CheckBoxFor、HiddenFor など) を使用する代わりに、直接の html コードを使用しました。

ただし、このアプローチの問題は、name 属性と id 属性を文字列として配置する必要があることです。モデル プロパティを厳密に型指定したままにしたかったので、NameFor および IdFor HtmlHelpers を使用しました。

<input type="hidden" name="@Html.NameFor(m => m.Name)" id="@Html.IdFor(m=>m.Name)" value="@Html.AttributeEncode(Model.Name)">

更新: これは便利な HtmlHelper 拡張機能です

    public static MvcHtmlString MyHiddenFor<TModel, TValue>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression, object htmlAttributes = null)
    {
        return new MvcHtmlString(
            string.Format(
                @"<input id=""{0}"" type=""hidden"" value=""{1}"" name=""{2}"">",
                helper.IdFor(expression),
                helper.NameFor(expression),
                GetValueFor(helper, expression)
            ));
    }

    /// <summary>
    /// Retrieves value from expression
    /// </summary>
    private static string GetValueFor<TModel, TValue>(HtmlHelper<TModel> helper, Expression<Func<TModel, TValue>> expression)
    {
        object obj = expression.Compile().Invoke(helper.ViewData.Model);
        string val = string.Empty;
        if (obj != null)
            val = obj.ToString();
        return val;
    }

その後、次のように使用できます

@Html.MyHiddenFor(m => m.Name)
于 2015-06-03T13:39:20.617 に答える