Hidden input for Dictionary in ASP.NET MVC

2019-05-05 20:56发布

问题:

I need to get my model properties in Post action, so need to hidden element for them, but I have a problem with type of Dictionary <string, string>. this is My model:

public class ViewModel{
 ...
 public ViewPart ViewPart { get; set; }
}

public class ViewPart {
 ...
 public Dictionary<string, string> Flags { get; set; }
}

And My Controller:

Dictionary<string, string> flags = new Dictionary<string, string>();
flags.Add("kind", "Edit");
flags.Add("Command", "Save");
ViewModel model = new ViewModel(){ Flags  = flags };
return View(model);

In View:

@foreach(var item in Model.ViewPart.Flags) { 
 <input type="hidden" id="ViewPart_Flags_@(item.Key)" value="@item.Value" name="ViewPart.Flags[@(item.Key)]" />
}

Also I try This one :

@foreach(var item in Model.ViewPart.Flags) { 
  @Html.HiddenFor(modelItem => item)
}

Update Post Action:

[HttpPost]
public ActionResult MyPostAction(ViewModel model){
  //model.ViewPart.Flags is null
}

But in Post action the Flags is Always null, why? where is my fault? Is there any better way to pass variables from View To Action?

回答1:

You need two hidden fields one for the Key and one for the Value if you want to modelbind to a dictionary:

var index = 0;
foreach (var item in Model.ViewPart.Flags)
{

    <input type="hidden" value="@item.Key" 
                         name="ViewPart.Flags[@(index)].Key"/>
    <input type="hidden" value="@item.Value" 
                         name="ViewPart.Flags[@(index)].Value"/>

    index++;
}
    <input type="submit" value="Save"/>

Note, you will be also need a running index to make the model binder happy.

Or if you don't want to have a running you can solve with an addtional hidden Index field:

foreach (var item in Model.ViewPart.Flags)
{

    <input type="hidden" value="@item.Key" 
                         name="ViewPart.Flags.Index"/>
    <input type="hidden" value="@item.Key" 
                         name="ViewPart.Flags[@(item.Key)].Key" />
    <input type="hidden" value="@item.Value" 
                         name="ViewPart.Flags[@(item.Key)].Value" />
}
    <input type="submit" value="Save"/>
}

You can find a lots of info about modelbinding with collections in this two article:

  • ASP.NET Wire Format for Model Binding to Arrays, Lists, Collections, Dictionaries
  • Model Binding To A List


回答2:

Change your post action to this

[HttpPost]
public ActionResult MyPostAction(ViewModel model, ViewPart viewPart)
{
    model.ViewPart.Flags = viewPart.Flags;
}

and in the view use

@foreach(var item in Model.ViewPart.Flags) { 
  @Html.HiddenFor(modelItem => item)
}

If this doesn't work, check the source of the rendered HTML and see if the the Hidden information is rendered. I'm not sure if @Html.HiddenFor will work with a Dictionary you may need to write it out like you had done previously if it doesn't.