web-dev-qa-db-fra.com

MVC 4 @ HTML.HiddenFor ne sont pas à jour sur une publication

Avoir des problèmes avec l'état d'affichage sur une série de pages vues - Sur la vue initiale d'une page dans Razor, j'utilise Html.HiddenFor comme suit: 

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

qui semble bien fonctionner. Mes balises d'entrée masquées contiennent les valeurs correctes. Cependant, lorsque je soumets le formulaire [HTTPPost] et que je mets à jour le modèle dans l'action de mon contrôleur avec ..

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

Les champs masqués ne semblent pas se mettre à jour, ils contiennent les valeurs d'origine de la vue initiale. Cependant, lorsque j'utilise ces champs dans un autre contexte dans la même vue de rasoir comme celle-ci ...

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

Ensuite, les champs de saisie sont mis à jour correctement. J'ai même créé un assistant de visualisation pour faciliter le débogage, et dans tous les cas, le modèle semble contenir des données correctes dans HtmlHelper<TModel> - j'ai même renvoyé le modèle sous la forme return Json(model); et les données étaient correctes. 

À ce stade, je cours avec le travail, mais est-ce que quelqu'un sait pourquoi @Html.HiddenFor est sale.

Mise à jour: voici mes actions de contrôleur

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

Voici mon avis:

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

Voici mon modèle:

 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

Le comportement par défaut de HtmlHelpers (@ Html.HiddenFor, etc.) est de se comporter exactement comme vous l'avez décrit. 

c'est-à-dire que toutes les modifications que vous apportez à ViewModel sur une publication sont prises en compte, toutes les modifications que vous renvoyez de la publication sont reçues par la vue, mais lors du rendu avec WITH HTMLHELPERS, les valeurs précédentes de Post ont priorité sur les valeurs ViewModel modifiées.

Voulez-vous "réparer" ce problème de manière rapide et sale, effacez ModelState.Clear () avant de revenir de HttpPost ActionMethod!

32
joedotnot

Comme mentionné par joedotnot, ce comportement est prévu. Une autre solution consiste à coder le code HTML pour le champ masqué et à ne mettre à jour que la valeur du modèle, par exemple:

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

Utilisez les mêmes id et name que votre propriété de modèle et la valeur mise à jour sera rendue après publication.

6
maxscan

J'ai récemment rencontré un problème similaire et j'ai fini par écrire une nouvelle méthode d'assistance simple + 2 surcharges. Je le partage ici au cas où quelqu'un chercherait toujours une solution de contournement, car cette "fonctionnalité" est parfois agaçante.

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

Ensuite, vous l'utilisez comme d'habitude depuis votre vue:

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

Cela vaut la peine de mentionner que cela fonctionne aussi avec les collections.

5

Je pense que vous devriez les utiliser comme ceci à la place:

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

Sans voir votre modèle, je suppose qu'il ressemble à ceci:

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

Sinon, il devrait être semblable aux propriétés publiques ci-dessus.

Mettre à jour

Avez-vous ce qui suit?

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

Je voudrais aussi changer votre formulaire à partir de ceci:

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

Pour ça:

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

Tu peux essayer

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

J'ai eu un problème similaire et résolu comme ça.

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