Dictionary<,> Binding to a Controller in Asp.Net MVC - c#

I have a class like as below.
public class ProductViewGridModel
{
public long Id { get; set; }
public string PackageCode { get; set; }
public string ProductName { get; set; }
public string ProductCategory { get; set; }
public IDictionary<string, string> Localizations { get; set; }
}
I rendered the model in to view like this.(foreach statement running into table, I'm not showing its here.)
#foreach (var localization in Model.Localizations)
{
var p = localization.Value;
<tr>
<td class="adminData">
<input class="k-textbox" type="text" value="#p" name="productView.Localizations[#localization.Key]" />
<input type="hidden" value="#p" name="productView.Localizations[#localization.Key]" />
</td>
</tr>
}
And I have a button, the button sending form values to controller with ajax call.
Other model bindings are okay, but I want to bind all localization inputs to localizations model.
But localizations always null. How can I bind this ?.

The input name is wrong. The default modelbinder will look for the property Localizations:
#foreach (var localization in Model.Localizations)
{
var p = localization.Value;
<tr>
<td class="adminData">
<input class="k-textbox" type="text" value="#p" name="Localizations[#localization.Key]" />
<input type="hidden" value="#p" name="Localizations[#localization.Key]" />
</td>
</tr>
}

Related

Not able to bind value to model which is collection asp.net core mvc

I have list as model in my view. I display my items information and counts. also I have to take assignedworker to that location from the user. When I submit this method, my collectionmodel is getting null. I am losing all information in my model.
I have data in all the properties except assignedworker. I display all the information to the user using foreach and I take assignedworker name from the user. When I submit this form, List is null.
public class Report
{
public string itemname{ get; set; }
public List<itemlocation> locations { get; set; }
}
public class itemlocation
{
public string location { get; set; }
public List<items> items{ get; set; }
public string assignedworker{ get; set; }
}
View:
#model IList<Report>
<form method="post" asp-action="Report" asp-controller="Home">
#foreach (var rep in Model)
{
<tr>
<td colspan="3">
<h3>#rep.itemname</h3>
</td>
</tr>
#foreach (var loc in rep.itemlocation)
{
<tr>
<td>#loc.location </td>
<td>#loc.items.Count()</td>
<td>
<input type="text" class="form-control" id="worker" name="#loc.assignedworker" value="#loc.assignedworker">
</td>
</tr>
}
}
</form>
I have data in all the properties except assignedworker. I display all the information to the user using foreach and I take assignedworker name from the user. When O submit this form, List is null.
Expected result:
In my controller I would like to be able to see my collection(List) with all the values including assignedworker.
In order to generate the right input names for modelbinding, Razor needs the full model expression, which means you must use a regular for loop and index your lists, rather than using foreach:
#for (var i = 0; i < Model.Count; i++)
{
...
#for (var j = 0; j < Model[i].locations.Count; j++)
{
...
<input asp-for="#Model[i].locations[j].assignedworker" />
...
}
}
You could use <input asp-for="" hidden /> to pass the value to the action:
1.Model:
public class Report
{
public string itemname { get; set; }
public List<itemlocation> locations { get; set; }
}
public class itemlocation
{
public string location { get; set; }
public List<items> items { get; set; }
public string assignedworker { get; set; }
}
public class items
{
public int Id { get; set; }
public string name { get; set; }
}
2.View:
#model IList<Report>
<form method="post" asp-action="Report" asp-controller="Home">
#for (var i = 0; i < Model.Count; i++)
{
<tr>
<td colspan="3">
<input asp-for="#Model[i].itemname" hidden/>
<h3>#Model[i].itemname</h3>
</td>
</tr>
#for (var j = 0; j < Model[i].locations.Count; j++)
{
<tr>
<td>
#Model[i].locations[j].location
<input asp-for="#Model[i].locations[j].location" hidden />
</td>
<td>
#Model[i].locations[j].items.Count()
#for (var k = 0; k < Model[i].locations[j].items.Count; k++)
{
<input asp-for="#Model[i].locations[j].items[k].Id" hidden />
<input asp-for="#Model[i].locations[j].items[k].name" hidden />
}
</td>
<td>
<input asp-for="#Model[i].locations[j].assignedworker" />
</td>
</tr>
}
}
<div class="form-group">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</form>

ASP.NET MVC 5 model binding list is empty

I stuck on this issue for a while..
I've created a simple view model:
public class AddTranslationViewModel
{
public List<ProjectTranslation> ProjectTranslations { get; set; }
public AddTranslationViewModel()
{
ProjectTranslations = new List<ProjectTranslation>();
}
}
ProjectTranslation class:
public class ProjectTranslation
{
public int ProjectTranslationId { get; set; }
public string Title { get; set; }
public string Description { get; set; }
public string Address { get; set; }
public int LanguageId { get; set; }
public Language Language { get; set; }
public int ProjectId { get; set; }
public Project Project { get; set; }
}
A simple view which uses the AddTranslationViewModel
<table class="table">
#foreach (var item in Model.ProjectTranslations)
{
#Html.HiddenFor(modelItem => item.ProjectTranslationId)
<tr>
<td>
#Html.DisplayFor(modelItem => item.Language.LanguageCode)
</td>
<td>
#Html.EditorFor(modelItem => item.Title)
</td>
</tr>
}
</table>
<input type="submit" value="Send" />
and finally my POST Method:
public ViewResult AddTranslation(AddTranslationViewModel projectTranslations)
{
if (ModelState.IsValid)
{
//...
}
return View(projectTranslations);
}
The idea is very basic, I want to show a list of items where it should be possible to change/edit the values.
However, the model binding is not working, the projectsTranslations param in the HTTPPost-Method AddTranslation is always empty.
What's the mistake here?
Binding to a list of object requires creating input field structure with names containing indexes, i.e:
<input type="text" name="YourArrayOrList[0].SomeProperty" value="123" />
<input type="text" name="YourArrayOrList[0].SomeOtherProperty" value="321" />
<input type="text" name="YourArrayOrList[1].SomeProperty" value="123" />
<input type="text" name="YourArrayOrList[1].SomeOtherProperty" value="321" />
Moreover, you need to point the form to the proper Action Method in your Controller using Razor's Html.BeginFrom method (see documentation).
In you case it should look like this:
#using(Html.BeginForm("AddTranslation","YourControllerName"))
{
for (int i=0;i<Model.ProjectTranslations.Count; i++)
{
#Html.HiddenFor(model => model.ProjectTranslations[i].ProjectTranslationId)
<tr>
<td>
#Html.DisplayFor(model => model.ProjectTranslations[i].Language.LanguageCode)
</td>
<td>
#Html.EditorFor(model => model.ProjectTranslations[i].Title)
</td>
</tr>
}
}
If your method is not edit, but CREATE method, then obviously your List in model will have 0 elements. In this case, change the stop condition in for loop to desired count.
Keep in mind that this topic was discussed many times before:
ASP.NET MVC bind array in model
ASP.NET MVC - Can't bind array to view model

.net mvc view not return model on post

Before i started i would like to say i searched and found nothing similar
in my solution i have a model that contains a list of some of my objects
public class ModelView
{
public Owner owner = new Owner();
public Tenant tnt = new Tenant();
}
In my view i call that class as a model which is this way
#model WebApp.Models.ModelView
<form name="export_form" action="Export" method="post">
<table cellpadding="2" cellspacing="2" border="0">
#if (Condition_1)
{
<tr>
<td>
<!-- ID -->
</td>
<td>
#Html.HiddenFor(model => model.owner.ID)
</td>
</tr>
<tr>
<td>
Name
</td>
<td>
#Html.EditorFor(model => model.owner.name)
</td>
</tr>
<tr>
<td>
Phone
</td>
<td>
#Html.CheckBoxFor(model => model.owner.is_Checked_Phone)
</td>
</tr>
}
else
{
<tr>
<td>
<!-- ID -->
</td>
<td>
#Html.HiddenFor(model => model.tnt.ID)
</td>
</tr>
<tr>
<td>
Name
</td>
<td>
#Html.EditorFor(model => model.tnt.name)
</td>
</tr>
<tr>
<td>
Adress
</td>
<td>
#Html.CheckBoxFor(model => model.tnt.is_Checked_Adress)
</td>
</tr>
}
</table>
<input type="submit" name="SaveStuff" value="Save" />
<input type="submit" name="ExportStuff" value="Export" />
</form>
In my controller i have a class that handles multiple submit buttons and depending on the button name it would redirect to a method. below is the SaveStuff method
[HttpPost]
[SubmitButtonClass(Name = "SaveStuff")]
public ActionResult Save_Definition(Owner owner, Tenant tnt)
{
/*
Stuff Here
*/
}
the problem here is i keep getting null values even thought the entities are not null. is there a reason why? no values are returned.
Update
Model A
public partial class Owner
{
public long ID { get; set; }
public bool is_Checked_Name { get; set; }
public bool is_Checked_Phone { get; set; }
}
Model B
public partial class Tenant
{
public long ID{ get; set; }
public bool is_Checked_Name { get; set; }
public bool is_Checked_Adress { get; set; }
}
these are auto generated using EF
You have multiple issues with your code. Firstly the model in your view is ModelView and you generating form controls prefixed with that models property names, for example
<input type="checkbox" name="owner.is_Checked_Phone" ... />
which means your POST method needs to match the model in the view
public ActionResult Save_Definition(ModelView model)
The next issue is that you model only has fields, not properties with { get; set; }, so the DefaultModelBinder cannot set any values. Your view model would need properties such as public Owner owner { get; set; } and you set the value in either the controller before you pass the model to the view, or in a parameterless constructor for ModelView.
However a view model should not contain properties which are data models, but rather be a flat structure containing only the properties you need. In your case, it would be
public class ModelView
{
public long ID { get; set; }
[Display(Name = "Name")]
public bool IsNameSelected { get; set; }
[Display(Name = "Phone")]
public bool IsPhoneSelected { get; set; }
[Display(Name = "Address")]
public bool IsAddressSelected { get; set; }
// additional property to define if the form is for an Owner or Tenant
public bool IsOwner { get; set; }
}
and in the GET method
var model = new ModelView()
{
IsOwner = true // or false
};
return View(model);
and in the view
#model ModelView
....
#using (Html.BeginForm("Save_Definition"))
{
#Html.HiddenFor(m => m.ID)
#Html.HiddenFor(m => m.IsOwner)
#Html.CheckBoxFor(m => m.IsNameSelected )
#Html.LabelFor(m => m.IsNameSelected )
if (Model.IsOwner)
{
#Html.CheckBoxFor(m => m.IsPhoneSelected)
#Html.LabelFor(m => m.IsPhoneSelected)
}
else
{
#Html.CheckBoxFor(m => m.IsAddressSelected)
#Html.LabelFor(m => m.IsAddressSelected)
}
.... // submit buttons
}
and the POST method, you can check the value of model.IsOwner to know if you have submitted an Owner or Tenant and take the appropriate action.
Side notes:
Recommend you read What is ViewModel in
MVC?
The <table> element is for tabular data. Do not use it for layout.
Your view has <form action="Export" .. > yet your POST method is
named Save_Definition so unsure which method you intending to
submit the form to.
If your post controller is changing original model data, you will need to issue a ModelState.Remove("propertyname") followed by model.propertyname = "new value" and then a simple return View(model) will update the changed value on your already posted view.

Passing list from view to controller

I want to pass an ArrayList of Object from ARCCreate so the items can be added to the database as multiple entries, these are my codes:
My Model :
public class M_ARC : DbContext
{
[Key]
[Display(Name = "Periode")]
[Required]
[RegularExpression("[2][0]([1][4-9]|[2-9][0-9])(0[1-9]|1[012])", ErrorMessage = "Format tidak sesuai. Contoh format : 201407 (Juli 2014)")]
public int Periode { get; set; }
[Display(Name = "Email SPDT")]
[Required]
[DataType(DataType.DateTime)]
[DisplayFormat(DataFormatString = "dd/MM/yyyy", ApplyFormatInEditMode = true)]
public DateTime EmailSPDT { get; set; }
[Display(Name = "Jatuh Tempo")]
[Required]
public DateTime JatuhTempoDT { get; set; }
}
My View :
<tr>
<th>
#Html.Label("Agustus")
</th>
<td>
<input type="text" name="EmailSPDT" class="pengirimanDT" />
</td>
<td>
<input type="text" name="JatuhTempoDT" class="tanggapanDT" />
</td>
<td>
<input type="text" name="InformasiBankDT" class="informasiBankDT" />
</td>
</tr>
<tr>
<th>
#Html.Label("September")
</th>
<td>
<input type="text" name="EmailSPDT" class="pengirimanDT" />
</td>
<td>
<input type="text" name="JatuhTempoDT" class="tanggapanDT" />
</td>
<td>
<input type="text" name="InformasiBankDT" class="informasiBankDT" />
</td>
</tr>
<tr>
<th>
#Html.Label("Oktober")
</th>
<td>
<input type="text" name="EmailSPDT" class="pengirimanDT" />
</td>
<td>
<input type="text" name="JatuhTempoDT" class="tanggapanDT" />
</td>
<td>
<input type="text" name="InformasiBankDT" class="informasiBankDT" />
</td>
</tr>
And my Controller :
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ARCCreate(List<M_ARC> ARCList)
{
foreach (M_ARC item in ARCList)
{
if (ModelState.IsValid)
{
db.Arcs.Add(item);
db.SaveChanges();
}
else return View();
}
return RedirectToAction("ARCIndex");
}
Is this a proper way for inserting multiple entries ? or Is there better solution for inserting multiple entries into my database?
I think you might have some misunderstandings about MVC and EF. You might want to read up a bit more on the two concepts.
First, your "model" is called M_ARC and inherits from DbContext. The class inheriting from DbContext should not be the model but provide a property to retrieve/update the model from/to the database.
Second, you cannot pass data from the view to the controller. It is the other way around. What happens when the user clicks a button is, the form fields are posted to the server and handled by the controller. I reckon what you try to achieve is rendering the view such that the form fields will have the correct names to be mapped into an array. This is not a new question though, you can find some help here: MVC .NET Model Binding to Array on the fly
I can see that you want to submit multiple entries where each entry contains these properties: Periode, EmailSPDT, JatuhTempoDT, and InformasiBankDT. Here's how you should create your model class
public class ARCCreateModel
{
public ARCCreateModel()
{
this.Details = new List<ARCCreateDetail>();
}
public List<ARCCreateDetail> Details { get; set; }
}
public class ARCCreateDetail
{
[Display(Name = "Periode")]
[Required]
[RegularExpression("[2][0]([1][4-9]|[2-9][0-9])(0[1-9]|1[012])", ErrorMessage = "Format tidak sesuai. Contoh format : 201407 (Juli 2014)")]
public int Periode { get; set; }
[Display(Name = "Email SPDT")]
[Required]
[DataType(DataType.DateTime)]
[DisplayFormat(DataFormatString = "dd/MM/yyyy", ApplyFormatInEditMode = true)]
public DateTime EmailSPDT { get; set; }
[Display(Name = "Jatuh Tempo")]
[Required]
public DateTime JatuhTempoDT { get; set; }
}
Then pass an instance of ARCCreateModel to the view in the controller get method
[HttpGet]
public ActionResult ARCCreate()
{
ARCCreateModel model = new ARCCreateModel();
// generate the multiple entries
ARCCreateDetail detail1 = new ARCCreateDetail();
detail1.Periode = 201408;
model.Details.Add(detail1);
// add more details if necessary
ARCCreateDetail detail2 = new ARCCreateDetail();
detail2.Periode = 201409;
model.Details.Add(detail2);
return View(model);
}
Your view should be like this based on the definition of ARCCreateModel above
#model ARCCreateModel
#using (Html.BeginForm())
{
<table>
#for (int i = 0; i < Model.Details.Count; i++)
{
<tr>
<th>
#Model.Details[i].Periode
</th>
<td>
<input type="text" name="Details[#i].EmailSPDT" class="pengirimanDT" />
</td>
<td>
<input type="text" name="Details[#i].JatuhTempoDT" class="tanggapanDT" />
</td>
<td>
<input type="text" name="Details[#i].InformasiBankDT" class="informasiBankDT" />
</td>
</tr>
}
</table>
<button type="submit">Submit</button>
}
and finally when you submit the form, you can get the multiple entries in model.Details as below
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ARCCreate(ARCCreateModel model)
{
foreach (ARCCreateDetail item in model.Details)
{
// get the entered values and save to database here
// assuming there's Arc table with properties similar to ARCCreateDetail
Arc arc = new Arc();
arc.Periode = item.Periode;
arc.EmailSPDT = item.EmailSPDT;
arc.JatuhTempoDT = item.JatuhTempoDT;
arc.InformasiBankDT = item.InformasiBankDT;
db.Arcs.Add(arc);
}
// submit the changes to EF
db.SaveChanges();
return RedirectToAction("ARCIndex");
}
Please note that the key is how you set the name attribute of the textboxes for each entry, i.e Details[0].EmailSPDT or Details[1].JatuhTempoDT so the entered values will be in the exact same order as in model.Details property.

ASP.NET MVC 4 form post variables are null in receiving controller

I have this model:
public class WorkflowImport {
public bool IsLive { get; set; }
public string DateTimeCreated { get; set; }
public string CreatedByUser { get; set; }
public string VersionComments { get; set; }
public string VersionNumber { get; set; }
public string FilePath { get; set; }
public List<WorkflowProcess> WorkflowProcesses { get; set; }
}
A partial view:
#model <FullyQualifiedPathTo...>.ViewModels.WorkflowImport
<div class="subSectionHeader">Upload New Workflow Profile</div>
<div class="AccountDetailLine">
#using ( Html.BeginForm( "UploadNewMatrix", "Home", FormMethod.Post, new { enctype = "multipart/form-data" } ) ) {
<table>
<tr>
<td>Select Local File:</td>
<td>
<div class="upload-wrapper">
<input type="file" name="file" id="xlsfile" />
</div>
</td>
</tr>
<tr>
<td>Version Comments:</td>
<td>
#Html.TextBox( "comments", Model.VersionComments, new Dictionary<string, object> { { "class", "textboxUploadField" } } )
</td>
</tr>
<tr>
<td>Version Number:</td>
<td>
#Html.TextBox( "version", Model.VersionNumber, new Dictionary<string, object> { { "class", "textboxUploadField" } } )
</td>
</tr>
<tr>
<td colspan="2"></td>
</tr>
<tr>
<td>Select whether this file upload will update<br />
the live workflow matrix or just the "What If" test matrix</td>
<td>LIVE #Html.RadioButtonFor( model => model.IsLive, "True" )
"What If" #Html.RadioButtonFor( model => model.IsLive, "False" )
</td>
</tr>
</table>
<div class="submit-wrapper">
<input type="submit" value="Import Now" id="form_submit" class="ovobutton" />
</div>
}
</div>
and a receiving controller/method:
public class HomeController: Controller {
// POST /Home/UploadNewMatrix
[HttpPost]
public ActionResult UploadNewMatrix( WorkflowImport workflowImport ) {
return View( workflowImport );
}
}
But when I enter some values into the two textboxes and click the submit button, I get null values in the bound object on the controller, when checking in the debugger.
I don't know why this is happening, because I used an almost identical pattern (including file upload with "multipart/form-data") on a previous project and was able to get the values successfully.
Is there something obvious I have not seen here? The difference between this new application and the previous is that it is a partial view in amongst a lot of jquery, but I don't see how that could make a difference. Also, I need to use traditional file upload as the target browser is IE8 and does not support HTML5.
For correct binding, name html element must equal property name
property:
public string VersionComments { get; set; }
View:
<tr>
<td>Version Comments:</td>
<td>
#Html.TextBox("VersionComments", Model.VersionComments, new Dictionary<string, object> { { "class", "textboxUploadField" } } )
</td>
</tr>
Note: i agree with Jacob. Using #Html.EditorFor(m=>m.VersionComments) more flexible approach, but my example demonstrates binding principle.
You're getting back null because the name of the inputs don't match what the model binder expects. To help you create the right names, ASP.NET MVC has some useful helper methods. Instead of:
#Html.TextBox(
"comments",
Model.VersionComments,
new Dictionary<string, object> { { "class", "textboxUploadField" } } )
...do this instead:
#Html.TextBoxFor(
m => m.VersionComments,
new Dictionary<string, object> { { "class", "textboxUploadField" } } )
This will use the value of VersionComments, and it will also give the input the name VersionComments so that it knows to plug this into the model when posting.

Categories