2011-01-17 18 views
119

私のプロジェクトでMVC 3を使用していますが、私は非常に奇妙な動作を見ています。ASP.Net MVC Html.HiddenForが間違っています

私のモデルで特定の値の隠しフィールドを作成しようとしていますが、何らかの理由でフィールドに設定された値がモデルの値に対応していないという問題があります。

私はテストとして、このコードを持っている:

<%:Html.Hidden("Step2", Model.Step) %> 
<%:Html.HiddenFor(m => m.Step) %> 

私は両方の非表示フィールドが同じ値を持っていると思うだろう。 私が行うことは、最初に表示を表示したときに値を1に設定し、投稿後にモデルフィールドの値を1だけ増やします。

したがって、レンダリングされた値がこれらのある値1が、2番目の時間を持っている:あなたが見ることができるように

<input id="Step2" name="Step2" type="hidden" value="2" /> 
<input id="Step" name="Step" type="hidden" value="1" /> 

、最初の値が正しいのですが、2番目の値は、私はビューを表示する最初の時間と同じになるように思われます。

私には何が欠けていますか? * HTMLヘルパーが何らかの方法で値をキャッシングしていますか?もしそうなら、どうすればこのキャッシングを無効にできますか?

ありがとうございました。

+0

私はちょっと試しました。 HiddenFor呼び出しを削除して、隠し呼び出しのみを許可し、 "Step"名を使用すると、最初の値(1)もレンダリングします。 – willvv

+1

も同様に取得します –

答えて

167

これは正常であり、HTMLヘルパーの仕組みです。まず、POSTリクエストの値を使用し、その後にモデルの値を使用します。これは、コントローラのアクションでモデルの値を変更してもPOST要求に同じ変数がある場合でも、変更は無視され、POSTされた値が使用されることを意味します。

一つの可能​​な回避策は値を変更しようとしているコントローラのアクションでモデルの状態からこの値を削除することです:

// remove the Step variable from the model state 
// if you want the changes in the model to be 
// taken into account 
ModelState.Remove("Step"); 
model.Step = 2; 

別の可能性は、常に値を使用するカスタムHTMLヘルパーを書くことですPOST値を無視します。

そして、まだ別の可能性:

<input type="hidden" name="Step" value="<%: Model.Step %>" /> 
+0

素晴らしい説明、ありがとう。私は第3の選択肢をやったが、最初のものは興味深いようだ。 – willvv

+5

これについてSimon Inceのブログ記事を本当に感謝しています。私がそれから取る結論は、ワークフローが正しいことを確認することです。したがって、有効なビューモデルを受け取り、それを使って何かを行った場合、確認アクションにリダイレクトします。これは単に同等のモデルを取得して表示するだけです。これは、新しいModelStateを持っていることを意味します。 http://blogs.msdn.com/b/simonince/archive/2010/05/05/asp-net-mvc-s-html-helpers-render-the-wrong-value.aspx (からリンクされています今日私はこの記事に書いた投稿:http://oceanbites.blogspot.com/2011/02/mvc-renders-wrong-value.html) – Lisa

+0

これは意味がありますが、なぜEditorForModelではリクエストに目を向けるのでしょうかモデルの値に対する値? – ehdv

13

すべての段階で大きなモデルのさまざまな部分を示してウィザードを書くとき、私は同じ問題に遭遇しました。
「Step 1」のデータやエラーは、「Model 2」が「非難」に終わるまで、「Step 2」などと混同することになります。 (!)常にHiddenForはないにModelStateモデル自体から読み取るため...

if (oldPageIndex != newPageIndex) ModelState.Clear(); // <-- solution 
return View(model[newPageIndex]); 
+8

'ModelState.Clear()'は、似たような状況でシーケンシャルPOSTリクエストの問題を解決しました。 –

2

このコードは

// remove the Step variable from the model state 
// if you want the changes in the model to be 
// taken into account 
ModelState.Remove("Step"); 
model.Step = 2; 

を動作しません:。

これは私の簡単な解決策でしたそして、 "ステップ"キーが見つからない場合は、この変数タイプのデフォルトを生成します。この場合、0になります。

ここに解決策があります。私は自分のためにそれを書いたが、多くの人々がこのいたずらなHiddenForヘルパーと闘っているのを見て、それを共有する気にしないでください。

public static class CustomExtensions 
{ 
    public static MvcHtmlString HiddenFor2<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) 
    { 
     ReplacePropertyState(htmlHelper, expression); 
     return htmlHelper.HiddenFor(expression); 
    } 

    public static MvcHtmlString HiddenFor2<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, object htmlAttributes) 
    { 
     ReplacePropertyState(htmlHelper, expression); 
     return htmlHelper.HiddenFor(expression, htmlAttributes); 
    } 

    public static MvcHtmlString HiddenFor2<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression, IDictionary<string, object> htmlAttributes) 
    { 
     ReplacePropertyState(htmlHelper, expression); 
     return htmlHelper.HiddenFor(expression, htmlAttributes); 
    } 

    private static void ReplacePropertyState<TModel, TProperty>(HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> expression) 
    { 
     string text = ExpressionHelper.GetExpressionText(expression); 
     string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(text); 
     ModelStateDictionary modelState = htmlHelper.ViewContext.ViewData.ModelState; 
     ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData); 

     if (modelState.ContainsKey(fullName)) 
     {     
      ValueProviderResult currentValue = modelState[fullName].Value; 
      modelState[fullName].Value = new ValueProviderResult(metadata.Model, Convert.ToString(metadata.Model), currentValue.Culture); 
     } 
     else 
     { 
      modelState[fullName] = new ModelState 
      { 
       Value = new ValueProviderResult(metadata.Model, Convert.ToString(metadata.Model), CultureInfo.CurrentUICulture) 
      }; 
     } 
    } 
} 

次に、あなたはちょうどあなたの中からいつものようにそれを使用するには、表示:

@Html.HiddenFor2(m => m.Id) 

それはあまりにもコレクションで動作します言及する価値があります。

+0

この解決策は完全には機能しません。次のポストバックプロパティがアクション内でnullの場合 – user576510

+0

これは、正常に動作するプロダクションのコードです。なぜそれがあなたのために機能しないのかわかりませんが、ページ上にレンダリングされた正しい値の隠しフィールドが表示された場合、それがモデルのプロパティに復元されない理由はわかりません。しかし、別の話ですが、私の作品で同じことが起こる前に、どのような状況下で起こったのか非常にわかります。ありがとうございます。 –

関連する問題