Sending multiple objects in Html.BeginForm - c#

I'm trying to send multiple objects via FormMethod.Post, but the problem is that only the value of the first one is saved and the value of second one is the same as first. The problem is probably in the Razor syntax which I'm pretty new at, here is the code I'm using:
#using (Html.BeginForm("chAdress", "Adress", FormMethod.Post))
{
#Html.Label("Number")
#Html.TextBoxFor(model => model.Number)
#Html.ValidationMessageFor(model => model.Number)
#Html.Label("Distance")
#Html.TextBoxFor(model => model.Distance)
#Html.ValidationMessageFor(model => model.Distance)
#Html.Label("New Number")
#Html.TextBoxFor(model1 => model1.Number)
#Html.ValidationMessageFor(model1 => model1.Number)
#Html.Label("New Distance")
#Html.TextBoxFor(model1 => model1.Distance)
#Html.ValidationMessageFor(model1 => model1.Distance)
<button type="submit">Change Adress</button>
}
And here is the controller that should make the change:
public void chAdress(Adress model, Adress model1)
{
prepareConnection();
string distance = model.Distance.ToString();
string newDistance = model1.Distance.ToString();
Dictionary<string, object> queryDict = new Dictionary<string, object>();
queryDict.Add("distance", distance);
queryDict.Add("newDistance", newDistance);
var query = new Neo4jClient.Cypher.CypherQuery("start n=node(*) where (n:Adress) and exists(n.distance) and n.distance =~ {distance} set n.distance = {newDistance} return n",
queryDict, CypherResultMode.Set);
List<Adress> adrese = ((IRawGraphClient)client).ExecuteGetCypherResults<Adress>(query).ToList();
}
After running in debug mode, I see that the value of distance is always the same as the newDistance, what is the best way to fix this issue?

Views can only be typed to one model. You appear to be trying referencing a Model and Model1 in your view. You should create a new ViewModel to contain all properties that you want to return from your form and then, if needed, process that from your controller into the distinct objects you need.
Since you actually have just one model (but are trying to use it like 2) you are overwriting the properties of the previously set values.

POST method will always use the property name to submit the data. Event though you have 2 different models but still it has the same property name which will always get overridden by the latest property value which is new newDistance in this case. Either add a new property name newDistance to model like model.newDistance or create a different model with property name as newDistance like model1.newDistance.

Related

Losing complete Model when posting Ajax values to controller

What i want to do is have 2 listboxes (left, right) where left would have all products entities except the ones that are present in my Contract Entity and in the right the products in my Contract Entity.
I am having this issue that when i pass 1 parameter (string) I can receive them correctly in my controller but whenever i pass my model with it as a second parameter i loose my model completely. This is what i have:
Controller:
public ActionResult EditContract(ContractViewModel model, string selectedProducts)
View (Javascript/JQuery):
function GetSelectedProducts() {
var listbox = document.getElementById("productsForContractListbox");
var txt = "";
var i;
for (i = 0; i < listbox.length; i++) {
txt = txt + "\n" + listbox.options[i].text;
}
$('#SelectedProductForContracts').val(txt);
var selectedProducts = $('#SelectedProductForContracts').val();
var model = $('form').serialize;
$.post('#Url.Action("EditContract", "Contract")', { "model": model, "selectedProducts": selectedProducts});
}
Html helper listboxes:
// listbox for my contract products
#Html.ListBoxFor(c => c.Contract.Products, productsForContract, new { ID = "productsForContractListbox", #class = "form-control" })
// listbox where all products except the ones in my contract are loaded
#Html.ListBox("allProducts", allProducts, new { ID = "allProductsListbox", #class = "form-control" })
HiddenFor for the SelectedProductForContracts from my Model:
#Html.HiddenFor(c => c.SelectedProductForContracts, new { ID = "SelectedProductForContracts", name = "SelectedProductForContracts" })
Model:
[HiddenInput(DisplayValue = false)]
public List<SelectListItem> SelectedProductForContracts { get; set; }
When having the post data containing only the selectedProducts, i get my values but lose my model, when i add my model i get my model but lose my selectedProducts values.
I tried several things like THIS but couldn't get them to work in my case (i am doing something wrong probably but don't know what ...)
Can anyone help me towards the proper way of achieving this cause i see many ways but i hope there must be an elegant way of binding everything to 1 model without having to use javascript/Jquery ?
Kind regards!
List<SelectListItem> is not able to be defined in a single hidden variable

Hidden field not storing value from partial view into an array of complex object

I am unable to get an array of a complex type in the model to store its value IsAssigned between two actions in a controller.
Once the model has been composed, which including the array of complex type (which include IsAssigned), the view engine begins to render the HTML/Razor script. In the view, there is a section of the script that handles the displaying of the controls which manipulates the complex type. It is a "For i" loop that cycles through the array of complex type.
During this loop, there is a razor command HiddenFor for property IsAssigned which is contained inside of the complex type array.
A second step is carried out in the loop that renders a partial view. It is in this view where there are two radio buttons linked to an array position's IsAssigned boolean? property. If the user select yes the property turns to true, and no false.
After the view has been completely rendered and the user has made their selections, the next action method in the controller is activated and the model is passed to it. It is here where I expect the user choices against the IsAssigned property to persist, but no. It does not.
Below is the code for looping and the code for the partial view.
Loop
#for (int i = 0; i < Model.KitTypeAssignment.Length; i++)
{
#Html.HiddenFor(m => m.KitTypeAssignment[i].IsKitTypeActiveForSubject)
#Html.HiddenFor(m => m.KitTypeAssignment[i].KitType.Code)
#Html.HiddenFor(m => m.KitTypeAssignment[i].KitType.Description)
#Html.HiddenFor(m => m.KitTypeAssignment[i].KitType.KitTypeId)
#Html.HiddenFor(m => m.KitTypeAssignment[i].IsAssigned, new { #Name = "IsAssigned" + Model.KitTypeAssignment[i].KitType.Code })
#Html.Partial("_KitTypeDispensing", Model.KitTypeAssignment[i])
}
PartialView
<div class="field inline spacer20">
#{
var kitTypeAssign = String.Format("{0}{1}", Model.KitType.Code, "Assign");
var identityOfAssigned = new { id = #kitTypeAssign, #Name = "IsAssigned" + Model.KitType.Code };
var kitTypeNoAssign = String.Format("{0}{1}", Model.KitType.Code, "NoAssign");
var identityOfNoAssigned = new { id = #kitTypeNoAssign, #Name = "IsAssigned" + Model.KitType.Code };
var sectionTitle = string.Format("{0} {1}", PromptText.Assign_185B7133DB22230701A857C059360CC2.ToLocalizableString(), Model.KitType.Description);
#Html.Label(sectionTitle, new { #class = "colon" })
#Html.RadioButtonFor(m => m.IsAssigned, true, #identityOfAssigned)
<label for=#kitTypeAssign>#Html.PromptFor(PromptText.Yes_93CBA07454F06A4A960172BBD6E2A435)</label>
#Html.RadioButtonFor(m => m.IsAssigned, false, #identityOfNoAssigned)
<label for=#kitTypeNoAssign>#Html.PromptFor(PromptText.No_BAFD7322C6E97D25B6299B5D6FE8920B)</label>
}
</div>
I think the problem is that the partial view does not prefix the names of its HTML input elements correctly, therefore the ModelBinder does not know how to bind them back to your array. Try to pass the correct prefix explicitly to the partial:
#Html.Partial(
"_KitTypeDispensing",
Model.KitTypeAssignment[i],
new ViewDataDictionary { TemplateInfo = new TemplateInfo {
HtmlFieldPrefix = Html.NameFor(m => m.KitTypeAssignment[i]).ToString() }
}
)
Further reading: ASP.NET MVC Partial Views with Partial Models

razor dropdown list add the data but show after refresh the page

#{
HotelManagementEntities db = new HotelManagementEntities();
var list = db.tblCategories.Select(m => new { m.intseqid, m.varCategory}).ToList();
ViewBag.Category = new SelectList(db.tblCategories, "intseqid", "varCategory");
}
#Html.DropDownList("tblCategories",
ViewBag.Category as SelectList,
new
{
#class = "drplist",
id = "drpItemCat",
ng_show = "selectfieldforcat"
})
There are problems in your code, you are populating list from database here:
var list = db.tblCategories.Select(m => new { m.intseqid, m.varCategory}).ToList();
but down in the code when creating SelectList, you are again getting records form the database using the dbContext:
ViewBag.Category = new SelectList(db.tblCategories, "intseqid", "varCategory");
// note the db.tblCategories as first parameter, this is wrong
and if you have actually written this code in view, then you don't even need ViewBag, but your approach is not right, you should be writing the poupulation code in the controller action and then use it in the view:
public ActionResult SomeAction()
{
HotelManagementEntities db = new HotelManagementEntities();
ViewBag.Category = new SelectList(db.tblCategories, "intseqid", "varCategory");
}
In Controller:
public ActionResult Add()
{
ViewBag.countryList = GetCountries();
return View();
}
In razor View .csHtml
#Html.DropDownListFor(x => x.countryId, new SelectList(ViewBag.countryList, "id", "Name"), 'Select Country',
new { #id = "ddlCountry", #rows = 1 })
Despite the fact, it's not a good practice to have data retrieval in views, based on you code, you could try the following:
#{
var db = new HotelManagementEntities();
var list = db.tblCategories.Select(m => new
{
m.intseqid,
m.varCategory
}).ToList();
}
#Html.DropDownList("tblCategories", new SelectList(#list, "intseqid", "varCategory"));
A more appropriate approach it would be to create a model, which would contain as a property a SelectList object. Then at the corresponding action at the controller to create a new model and set the categories to mentioned above property. Last pass at your view (so now you have to define that your view expects a model of a specific type, that you declared above) the model and at the dropdownlist html helper do the following:
#Html.DropDownList("tblCategories", Model.Categories)
This way you are more adhered to separation of concerns that is built in the MVC pattern and your code would be more readable and maintainable in the long run. Your example is pretty simple. So that I mentioned before cannot be seen at this point. However, while you model would be more complex and you have to do more things in the views, you will see that I mean by saying more readable and maintainable.
Last but not least, try to avoid using the ViewBag. Apparently you can accomplish what you have to do with the use of ViewBag, but it's far better you pass a strongly typed object as a model to your view and have there all you need to show on the view rather than adding things in the ViewBag at the controller and then use casts in the view to do whatever you want.

Passing back a single object when using EditorFor(model[])

This has been a thorn in my side for a while. If I use EditorFor on an array of objects and the editor Template has a form in it ex.
public class FooController:Controller {
public ActionResult Action(Foo foo) {
// ...
}
}
Index.cshtml
#model IEnumerable<Foo>
#Html.EditorFor(m=> m)
EditorTemplate
#model Foo
#using (Html.BeginForm("action", "controller"))
{
#Html.TextBoxFor(f=> f.A)
#Html.CheckBoxFor(f=> f.B)
#Html.LabelFor(f=> f.B)
}
So I'll hit a few problems.
The checkbox label's for doesn't bind correctly to the checkbox (This has to do with the label not receiving the proper name of the property ([0].A as opposed to A).
I'm aware I can get rid of the pre- text by doing a foreach on the model in Index but that screws up ids and naming as the framework doesnt realize there are multiples of the same item and give them the same names.
For the checkboxes I've just been doing it manually as such.
#Html.CheckBoxFor(m => m.A, new {id= Html.NameFor(m => m.A)})
<label for="#Html.NameFor(m => m.A)">A</label>
However I cant solve the inability of the controller to accept the item as a single model. I've even tried allowing an array of Foo's in the Action parameters but that only work when its the first item being edited ([0]...) if its any other item in the array (ex. [1].A) the controller doesn't know how to parse it. Any help would be appreciated.
Make your model a class with the properties you need.
create a class in your Models subfolder
public class MyModel {
public IEnumerable<Foo> Foolist { get ; set;}
public string Something { get;set;}
}
your EditorFor will have to have a foreach loop for Foolist...
MVC will attempt to put your model together from the form and return it to your POST action in the controller.
Edit:
You could create an EditorTemplate for foo. In Views/Shared/EditorTemplates folder, create FooTemplate.cs
#model Foo
<div class="span6 float-left" style="margin-bottom: 6px">
#Html.TextBoxFor(m => m.A, new { style = "width:190px" })
#Html.CheckBoxFor(m => m.B, new { style = "width:40px" })
#Html.ValidationMessage("foo", null, new { #class = "help-inline" })
</div>
then in your view
#foreach (var myFoo in Model)
{
#EditorFor(myFoo)
}
This still suffers from the "model gets passed back as a whole" requiredment of yours. Not sure about why there is a need to process these individually.
Hah finally solved this - Here's how I did it. As a bit of background HTML forms use the name attribute when submitting forms, but the label for element uses Id . so I only adapt the id tag to have the prefix and not the name tag.
--In the cshtml file
#{
var prefix = ViewData.TemplateInfo.HtmlFieldPrefix;
ViewData.TemplateInfo.HtmlFieldPrefix = "";
}
then I can specify the id for the properties by their prefix while letting the name remain the same like so
#Html.CheckBoxFor(m => m.A,
new {id = prefix+"."+ Html.NameFor(m => m.A)})
<label for="#prefix.#Html.NameFor(m => m.A)">A!</label></div>

Using .NET MVC HTMLHelper .For methods doesn't generate correct ID/Name when casting viewmodel field

I got a ViewModel graph with some sub-objects.
This code:
#Html.DropDownListFor(model => model.JobTypeParams.ContactCatalogId, new SelectList(paramz.ContactCatalogues, "Id", "Description"), new { #class = "select2 input-default " })
Generates a Request.Form key/value item with the key of: JobTypeParams.ContactCatalogId.
That's great because that means the MVC Model Binder can correctly map the key/value item to the corresponding field in the view model.
However! This code (using casts)
#Html.DropDownListFor(model => ((AdSyncJobSpecificParameters)model.JobTypeParams).ContactCatalogId, new SelectList(paramz.ContactCatalogues, "Id", "Description"), new { #class = "select2 input-default " })
Generates a Request.Form key/value item with key ContactCatalogId. It loses the JobTypeParams prefix somewhere.
This leads to the modelbinder being unable to bind correct key to correct viewmodel field, and hence the problem.
I do not want to hard-code the ID because of many reasons. So how do you get the HTML Helper to correctly generate they name of the Name/Id attribute ?
It doesn't get them wrong, it's not supposed to do that. You have an incorrect expectation of how lambda's work. You are effectively replacing the old expression, model.JobTypeParams with a single expression of type AdSyncJobSpecificParameters.
What you should be doing is using an editor template.
#Html.EditorFor(model => model.JobTypeParams)
Then, create a AdSyncJobSpecificParameters.cshtml in your EditorTemplates folder and do this:
#model AdSyncJobSpecificParameters
#Html.DropDownListFor(model => model.ContactCatalogId,
new SelectList(paramz.ContactCatalogues, "Id", "Description"),
new { #class = "select2 input-default " })
You'll need to figure out a solution for the SelectList, since it doesn't seem to be part of your model. Maybe as a ViewBag item.
You can also create one for your superclass as well in YourSuperclass.cshtml
#model YourSuperclass
// etc...

Categories

Resources