Post javascript generated select option - c#

Here's the code in the view:
<select id="SelectOptions"></select>
And here's the javascript:
$.ajax({
url: '/PriseRendezVous/GetDispos/',
data: { dateText: selected },
success: function (listDispos) {
var myArray = listDispos.split(',');
for (var i = 0; i < myArray.length; i++) {
$('#SelectOptions').append('<option value="' + myArray[i] + '">' + myArray[i] + '</option>');
}
}
});
Say that the Model is of type Car. I want to post the selected value as Car.Color.
How can I associate the value with my Model property?

If you look at how DropDownListFor renders HTML, assume your view looks something like this (assume you have a ColorSelectList property on your Car model that is of type SelectList that contains SelectListItems of all of your colors):
#model Car
#Html.DropDownListFor(m => m.Color, Model.ColorSelectList)
Your HTML would come out looking something like this:
<select id="Color" name="Color">
<option value="Blue" selected="selected">Blue</option>
<option value="Red">Red</option>
</select>
So when you POST the form to your controller, it should be expecting your model:
public JsonResult GetDispos(Car car) { ... }
Then, the DefaultModelBinder will take your values and translate them based on the names of the properties in your form to the object in your action method.
So you simply need to give the names of the elements in your form the corresponding names in your model's property. To find out more about this, search for information on model binders, and in particular, DefaultModelBinder.

The MVC model binder works by matching the name attribute associated with an HTML input to properties of the Model it is expecting to receive.
So you'd need to build your select like:
<select id="SelectOptions" name="Color"></select>
However, HTML helpers will do all the work for you:
#Html.DropDownListFor(x => x.Color, new List<SelectListItem>())
will generate:
<select id="Color" name="Color"></select>

Related

Select Tag Helper binding with an array or list

How can my following controller Action Method bind a list of integers to a Select Tag Helper in my ASP.NET Core project without using ModelView? Or, without ModelView this cannot be done?
Action Method:
public IActionResult ListofDays()
{
IEnumerable<int> DaysList = Enumerable.Range(1, 7).ToList();
return View(DaysList);
}
Select Tag:
<select asp-for="???" asp-items="???"></select>
You can convert the int array to a list of SelectListItem which can be used as your asp-items property value of select tag helper.
public IActionResult Index()
{
var daysList =Enumerable.Range(1, 7)
.Select(g => new SelectListItem {Value = g.ToString(), Text =g.ToString()}).ToList();
return View(daysList);
}
Now in the view which is strongly typed to a list of SelectListItem,
#model List<SelectListItem>
<form asp-action="Index" asp-controller="Home">
<select name="MyDay" asp-items="Model"></select>
<input type="submit"/>
</form>
When you submit the form, the selected day will be in the MyDay form element. You may use a parameter with the same name in your HttpPost action method to receive it.
For your reference :
Select Tag Helper in MVC 6

Binding a complex model to a drop down list in MVC

I'm trying to bind a model that has two properties - one Int, and one Boolean - to a drop-down list in MVC. The boolean is a discriminator and the integer an ID. It is not possible to split the drop down list in two.
Here is my code so far.
<select class="col-md-3 form-control" name="Model.ID" id="model-select">
<option value="0" selected>Select an Option</option>
#foreach (var m in models.OrderBy(x => x.Id))
{
<option value="#m.ID" data-discriminator="#m.Discriminator">
#m.Name
</option>
}
</select>
The model looks something like this
class MyModel
{
int ID { get; set; }
string Name { get; set; }
boolean Discriminator { get; set; }
}
The aim is to provide a set of models to the View, then the user can pick one of these. Unfortunately each model has two properties which are used to identify which model was selected in the database - the Id, which mirrors the Id in the database, and the Discriminator. The two types are otherwise incompatible in the database, hence the discriminator. For the sake of design, I only want to have these two in the same drop-down list, as you can only select one at a time anyway.
My idea of a solution was to create 2 hidden fields which would be bound to the model like so
<input type="hidden" name="Model.ID" />
<input type="hidden" name="Model.Discriminator" />
These would be updated via JavaScript and then bound to the model (as far as I know, using names like that will bind it correctly, providing that the destination property on the model passed to the POST is Model in this example).
Are there any other alternatives I could pursue?
EDIT: Also worth noting that this 'Model' is part of a more complex model and is not the only field being POSTed, so if that makes any difference...
A select box is only going to be able to post one thing, and using JavaScript to populate hidden fields, while perhaps a workable solution, seems very brittle. Your best bet would like be creating an intermediary property that you can bind to and include both sets of information as the option value:
public string SelectedThing
{
get { return string.Format("{0},{1}", ID, Discriminator); }
set
{
if (value != null)
{
var parts = value.Split(',');
if (parts.Length == 2)
{
Int32.TryParse(parts[0], out ID);
Boolean.TryParse(parts[1], out Discriminator);
}
}
}
}
Then you would need to compose your select list in a similar way:
ViewBag.MyModelChoices = myModels.Select(m => new SelectListItem
{
Value = string.Format("{0},{1}", m.ID, m.Discriminator),
Text = m.Name
});
And finally, you would bind to this new property in your view:
#Html.DropDownListFor(m => m.SelectedThing, ViewBag.MyModelChoices)

MVC EditorFor Remove Indexed Name

I have model that is a list of another model such that ModelList : ModelSingle
In my razor view I am using
#model somenamespace.ModelList
#Html.EditorForModel()
This iterates though each ModelSingle and returns an EditorTemplate that is strongly typed to ModelSingle.
#model somenamespace.ModelSingle
#using(Html.BeginForm("Action", "Controller", FormMethod.Post, new { id = "formname" + Model.ID}))
{
#Html.AntiForgeryToken()
#Html.EditorFor(p => p.SomeField)
#Html.EditorFor(p => p.AnotherField)
}
Each of these templates contains a form that can be used to edit the single model. These are posted individually with my controllers method expecting
public ActionResult(ModelSingle model)
The problem I'm having is that the model is not binding correctly. With a Model as such
public class ModelSingle()
{
public string SomeField { get; set; }
public string AnotherField { get; set; }
}
the EditorTemplate is being told that it was part of a list so I get
<Form>
<input name="[0].SomeField"/>
<input name="[0].AnotherField"/>
<input type="submit" value="Update"/>
</Form>
I can't simply bind to the ModelList as it's not naming ModelList[0].SomeField and even if it was I don't think that would work for anything but the first item.
Is there anyway to make the EditorTemplate ignore the fact that it's model was part of a list or force a DropDownListFor, EditorFor etc.... to just use the field name without prepending the [i].
I know I can force a Name="SomeField" change but I'd rather have a solution that will reflect any changes made in the Model class itself.
EDIT - As Requested added a simplified example of the View and EditorTemplate being used.
The problem is related to a mismatch between the input names generated by your page model (which is a list), and the model expected by your action, which is a single item from your list.
When rendering a list, the default behavior is to render the indexed names like you've shown to us (the [#] notation). Since you want to be able to post any arbitrary item from the list, you won't know ahead of time what index is used. When the model binder looks at the request for your single object, it does not attempt to use the index notation.
I don't know what your requirements are from the user perspective - e.g. whether or not a page refresh is desired, but one way to accomplish this is to provide a jQuery post for the specific item being posted:
// pass jquery form object in
var postItem = function($form) {
var postBody = {
SomeField: $form.find('input selector') // get your input value for this form
AnotherField: '' // another input select for this item
}
$.ajax({
url:'<your action url>',
type: 'POST',
contentType:"application/json; charset=utf-8",
data: JSON.stringify(postBody),
dataType: 'json',
success: function(response) {
// do something with returned markup/data
}
});
}
You are manually serializing a single instance of your model with a json object and posting that. What you return from the action is up to you: new markup to refresh that specific item, json data for a simple status, etc.
Alternately, you can consider manually looping over the items in your collection, and using Html.RenderPartial/Html.Partial to render each item using your View template. This will short-circuit the name generation for each item, and will generate the names as if it's a single instance of ModelSingle.
Finally, a quick (but kind of ugly) fix would be to have your action method take a list of ModelSingle objects. I don't suggest this.
Edit: I missed some important aspects of posting json to an mvc action
Edit2: After your comment about hardcoded names, something like this could help:
var inputs = $form.find('all input selector');
var jsonString = '{';
$.each(inputs, function(index, element) {
var parsedName = element.attr('name').chopOffTrailingFieldName();
jsonString += parsedName + ":'" + element.val() + "',";
});
jsonString += '}';

ASP.NET MVC DropDownListFor with model of type List<string>

I have a view with a model of type List<string> and I want to place a drop down list on the page that contains all strings from the list as items in the drop down. I am new to MVC, how would I accomplish this?
I tried this:
#model List<string>
#Html.DropDownListFor(x => x)
but that threw an error.
To make a dropdown list you need two properties:
a property to which you will bind to (usually a scalar property of type integer or string)
a list of items containing two properties (one for the values and one for the text)
In your case you only have a list of string which cannot be exploited to create a usable drop down list.
While for number 2. you could have the value and the text be the same you need a property to bind to. You could use a weakly typed version of the helper:
#model List<string>
#Html.DropDownList(
"Foo",
new SelectList(
Model.Select(x => new { Value = x, Text = x }),
"Value",
"Text"
)
)
where Foo will be the name of the ddl and used by the default model binder. So the generated markup might look something like this:
<select name="Foo" id="Foo">
<option value="item 1">item 1</option>
<option value="item 2">item 2</option>
<option value="item 3">item 3</option>
...
</select>
This being said a far better view model for a drop down list is the following:
public class MyListModel
{
public string SelectedItemId { get; set; }
public IEnumerable<SelectListItem> Items { get; set; }
}
and then:
#model MyListModel
#Html.DropDownListFor(
x => x.SelectedItemId,
new SelectList(Model.Items, "Value", "Text")
)
and if you wanted to preselect some option in this list all you need to do is to set the SelectedItemId property of this view model to the corresponding Value of some element in the Items collection.
If you have a List of type string that you want in a drop down list I do the following:
EDIT: Clarified, making it a fuller example.
public class ShipDirectory
{
public string ShipDirectoryName { get; set; }
public List<string> ShipNames { get; set; }
}
ShipDirectory myShipDirectory = new ShipDirectory()
{
ShipDirectoryName = "Incomming Vessels",
ShipNames = new List<string>(){"A", "A B"},
}
myShipDirectory.ShipNames.Add("Aunt Bessy");
#Html.DropDownListFor(x => x.ShipNames, new SelectList(Model.ShipNames), "Select a Ship...", new { #style = "width:500px" })
Which gives a drop down list like so:
<select id="ShipNames" name="ShipNames" style="width:500px">
<option value="">Select a Ship...</option>
<option>A</option>
<option>A B</option>
<option>Aunt Bessy</option>
</select>
To get the value on a controllers post; if you are using a model (e.g. MyViewModel) that has the List of strings as a property, because you have specified x => x.ShipNames you simply have the method signature as (because it will be serialised/deserialsed within the model):
public ActionResult MyActionName(MyViewModel model)
Access the ShipNames value like so: model.ShipNames
If you just want to access the drop down list on post then the signature becomes:
public ActionResult MyActionName(string ShipNames)
EDIT: In accordance with comments have clarified how to access the ShipNames property in the model collection parameter.
I realize this question was asked a long time ago, but I came here looking for answers and wasn't satisfied with anything I could find. I finally found the answer here:
https://www.tutorialsteacher.com/mvc/htmlhelper-dropdownlist-dropdownlistfor
To get the results from the form, use the FormCollection and then pull each individual value out by it's model name thus:
yourRecord.FieldName = Request.Form["FieldNameInModel"];
As far as I could tell it makes absolutely no difference what argument name you give to the FormCollection - use Request.Form["NameFromModel"] to retrieve it.
No, I did not dig down to see how th4e magic works under the covers. I just know it works...
I hope this helps somebody avoid the hours I spent trying different approaches before I got it working.

Retrieving postback from Dynamically created controls in MVC without using FormCollection

I'm passing a List to an MVC view and generating checkboxes for each object in the list (The checkboxes are named t.Name).
I'd like to be able to tell which checkboxes were checked once the form is posted. However, I'd like to avoid using the FormCollection object. Is there any way to do this?
Set the name of your checkboxes to something like "MyObject[" + index + "].Checked", and for each checkbox also put a hidden input field named something like "MyObject[" + index + "].Name" with the value set to t.Name.
If you name your fields like that, the default model binder can take your form values and map them to a list of objects with a Name property and a Checked property.
I would try something like the following:
<% foreach(var t in Model)
{ %>
<div>
<%= Html.Hidden("MyObject[" + index + "].Name", t.Name, new { id = "MyObject_" + index + "_Name" }) %>
<%= Html.Checkbox("MyObject[" + index + "].Checked", false, new { id = "MyObject_" + index + "_Checked" }) %>
</div><%
} %>
I use the anonymous type with id property so that the MVC framework components don't generate HTML elements with invalid id values, but it isn't really necessary.
Your action handling the post would look something like this:
[HttpPost]
ActionResult MyAction(IList<MyObject> objects)
{
foreach (MyObject obj in objects)
{
if (obj.Checked)
{
// ...
}
else
{
// ...
}
}
return View();
}

Categories