wake-up-neo.com

MVC 4 @ HTML.HiddenFor wird bei einem Postback nicht aktualisiert

Probleme mit dem Ansichtsstatus bei einer Reihe von Seitenaufrufen - In der ersten Ansicht einer Seite in Razor verwende ich Html.HiddenFor wie folgt: 

    @Html.HiddenFor(x => Model.err)
    @Html.HiddenFor(x => Model.errField)
    @Html.HiddenFor(x => Model.errMessage)
    @Html.HiddenFor(x => Model.IsMove)

das scheint gut zu funktionieren. Meine versteckten Eingabe-Tags enthalten die korrekten Werte. Wenn ich jedoch das Formular [HTTPPost] absende und das Modell in meiner Controller-Aktion mit aktualisiere, ..

       model.err = transHelper.err;
       model.errField = transHelper.errField;
       model.errMessage = transHelper.errMessage;
       return View(model);

Die verborgenen Felder scheinen sich nicht zu aktualisieren, sie enthalten die ursprünglichen Werte aus der ursprünglichen Ansicht. Wenn ich diese Felder jedoch in einem anderen Kontext in derselben Rasiermesser-Ansicht wie dieser verwende ...

     @*      
        this seems to not update correctly...

    @Html.HiddenFor(x => Model.err)
    @Html.HiddenFor(x => Model.errField)
    @Html.HiddenFor(x => Model.errMessage)
    @Html.HiddenFor(x => Model.IsMove)

        *@
        <input type="hidden" id="err" value="@Model.err" />
        <input type="hidden" id="errField" value="@Model.errField" />
        <input type="hidden" id="errMessage" value="@Model.errMessage" />
        <input type="hidden" id="IsMove" value="@Model.IsMove" />

    </div>

Dann werden die Eingabefelder korrekt aktualisiert. Ich habe sogar einen View Helper erstellt, um das Debuggen zu erleichtern, und in allen Fällen scheint das Modell korrekte Daten in HtmlHelper<TModel> zu haben. Ich habe sogar das Modell als return Json(model); zurückgegeben und die Daten waren in Ordnung. 

An dieser Stelle renne ich mit der Arbeit herum, weiß aber jeder, warum @Html.HiddenFor schmutzig ist.

Update: Hier sind meine Controller-Aktionen

  [HttpPost]
   public ActionResult Index(HomePageModel model)
   {


       // process transaction
       Transactionr transr = new Transactionr();
       transr.Process(model);

       model.err = transr.err;
       model.errField = transr.errField;
       model.errMessage = transr.errMessage;

       return View(model);
   }

Hier ist meine Ansicht:

        @model App.Models.HomePageModel
    @{
        ViewBag.Title = "Product Categorizer";
    }
    <form id="formData" method="post" action="/Home/Index">
        @Html.AntiForgeryToken()
        <fieldset>
            <div>

            @Html.HiddenFor(model => model.err)
            @Html.HiddenFor(model => model.errField)
            @Html.HiddenFor(model => model.errMessage)
            @Html.HiddenFor(model => model.IsMove)

            <input type="hidden" id="myerr" value="@Model.err" />
            <input type="hidden" id="myerrField" value="@Model.errField" />

            </div>

           <div class="section group">
                <div class="col span_2_of_2">
                     <div class="message" id ="message">
                         @if (Model.err < 0)
                         {
                             <span style="color: purple;">@Model.errMessage (@Model.err) - (@Model.errField)</span>
                         }
                         else if (Model.err > 0)
                         {
                             <span style="color:red;">@Model.errMessage (@Model.err) (@Model.errField)</span>
                         } else {
                            <span>@Model.errMessage (@Model.err) (@Model.errField)</span>
                         }
                         </div>
                     </div>
            </div>

            <div class="section group" id="workspace">
                  @Html.Partial("_WorkspacePartial", Model)
            </div>
              <div class="section group" id="details">
                  @Html.Partial("_DetailPartial", Model)
              </div>


        </fieldset>
        </form>

Hier ist mein Modell:

 public class HomePageModel
 {
    public int FromStore { get; set; }

    //  the "To" part of the copy/move transaction
    public int ToStore { get; set; }

    // a list of the copy/move transaction
    public List<int> Details { get; set; }


    // true is move false is copy
    public bool IsMove { get; set; }

    // current message
    public int err { get; set; }
    public int errField { get; set; }
    public string errMessage { get; set; }
21
user799301

Das Standardverhalten von HtmlHelpers (@ Html.HiddenFor usw.) verhält sich genau so, wie Sie es beschrieben haben. 

das heißt, alle Änderungen, die Sie an einem ViewModel an einem Beitrag vornehmen, werden ausgeführt, alle Änderungen, die Sie vom Beitrag zurückgeben, werden von der Ansicht empfangen. Beim erneuten Rendern WITH HTMLHELPERS haben die vorherigen Post-Werte Vorrang vor den geänderten ViewModel-Werten.

Wollen Sie dieses Verhalten auf eine schnelle und schmutzige Weise "beheben", löschen Sie ModelState.Clear (), bevor Sie von HttpPost ActionMethod zurückkehren!

32
joedotnot

Wie von joedotnot erwähnt, handelt es sich hierbei um beabsichtigtes Verhalten. Eine weitere "schnelle Lösung" dafür ist, das HTML für das ausgeblendete Feld zu codieren und nur den Wert aus dem Modell zu aktualisieren, z. B .:

<input type="hidden" id="ErrMessage" name="ErrMessage" value="@Model.ErrMessage">

Verwenden Sie dieselbe id und name als Ihre Modelleigenschaft, und der aktualisierte Wert wird nach dem Postback wiedergegeben.

6
maxscan

Ich habe kürzlich vor einem ähnlichen Problem gestanden und am Ende eine neue einfache Hilfsmethode + 2 Überladungen geschrieben. Ich teile es hier, falls noch jemand nach einer Problemumgehung sucht, weil diese "Funktion" manchmal nervig ist.

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;

        if (modelState.ContainsKey(fullName))
        {
            ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
            ValueProviderResult currentValue = modelState[fullName].Value;
            modelState[fullName].Value = new ValueProviderResult(metadata.Model, Convert.ToString(metadata.Model), currentValue.Culture);
        }
    }
}

Dann verwenden Sie es wie gewohnt aus Ihrer Sicht:

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

Erwähnenswert ist, dass es auch mit Sammlungen funktioniert.

5

Ich denke, Sie sollten sie stattdessen so verwenden:

@Html.HiddenFor(x => x.Err)
@Html.HiddenFor(x => x.ErrField)
@Html.HiddenFor(x => x.ErrMessage)
@Html.HiddenFor(x => x.IsMove)

Ohne Ihr Modell zu sehen, gehe ich davon aus, dass es ungefähr so ​​aussieht:

public class ErroViewModel
{
  public string Err { get; set; }
  public string ErrField { get; set; }
  public string ErrMessage { get; set; }
  public bool IsMove { get; set; }
}

Wenn nicht, sollte es mit öffentlichen Eigenschaften wie oben ähnlich sein.

Update

Hast du die folgende?

public ActionResult Index(HomePageModel model)
{
   var model = new HomePageModel();
   return View(model);
}

Ich würde auch Ihre Form davon ändern:

 <form id="formData" method="post" action="/Home/Index">

Zu diesem:

@using (Html.BeginForm("Index", "Home", FormMethod.Post))
{
  // rest of form
}
3
hutchonoid

Du kannst es versuchen

<input type="hidden" id="SomeFieldID" name="SomeFieldID" value="@Model.SomeFieldID" />
1
Sumit Kapadia

Ich hatte ein ähnliches Problem und löste es so.

@Html.TextBoxFor(m => m.Email, new { onclick = "this.select()", Value = Model.Email, Placeholder = "E-Mail" })
0
Rune Antonsen