Im playing around with a booking-system in MVC.
I have a view where you select 3 diffrent values (treatment, hairdresser and date).
#using (Html.BeginForm("testing", "Home", FormMethod.Post)) {
<p id="frisor"> Frisör: #Html.DropDownList("Fris", "All")<a class="butt" onclick="showdiv()">Nästa steg</a></p>
<p id="behandling">Behandling: #Html.DropDownList("Cat", "All")<a class="butt" onclick="showdiv2()">Nästa steg</a></p>
<p>
Datum:
<input type="text" id="MyDate" /> <-------This is a jquery datetimepicker
</p
I would like to save the customers three choices in three properties i have created.
My post method looks like this:
[HttpPost]
public ActionResult Testing(string Fris, string Cat, DateTime MyDate)
{
kv.treatment = Cat
kv.Hairdresser = Fris;
kv.Datum = MyDate;
return View();
}
I get the two first (hairdresser,treatment) fine,
the problem is that i dont know how to get the value from the jquery datetimpicker.
Any help appreciated!
The input needs a name in order to be included in the form post:
<input type="text" id="MyDate" name="MyDate" />
Otherwise the browser won't include it in the posted data, so it will never reach the server for model binding. (And, of course, the name has to match the method argument name for the model binder to match them.)
Related
I have a form with a DropDownListFor. When I select the ID from my drop down list, select a date and click submit, I get error:
The parameters dictionary contains a null entry for parameter 'CasinoID' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Index(Int32, System.DateTime, NameSpace.ViewModels.TerminalReceiptPostData)' in 'Namesppace.Controllers.TerminalReceiptsController'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
It worked fine with just a regular Input tag and typing it in manually... however when I added a DropDownListFor this issue arises. Am I setting up the DDL wrong? Any other issues as to why this would happen? Below is some code.
Controller Action:
[HttpPost]
public ActionResult Index(int CasinoID, DateTime Date)
{
var model = TRBL.GetTransactionTestsData(CasinoID, Date);
return View(model);
}
View:
#using (Html.BeginForm("Index", "TerminalReceipts", new { id = "submitForm" }))
{
<div>
#*<input type="text" name="CasinoID" placeholder="Enter Casino ID" id="cIdSearch" />*#
#Html.DropDownListFor(o => o.TerminalReceiptPostData.CasinoIdDDL, Model.TerminalReceiptPostData.CasinoIdDDL, new { id = "CasinoID"})
<input id="datepicker" class="datepicker-base" name="Date" placeholder="MM/DD/YYY" type="text" />
<button type="submit" class="btn btn-sm btn-primary" id="search" onclick="checkField()"> Search Transactions</button>
</div>
}
Edit update
So I was able to change how the structure a bit to now be able to get the CasinoID to be passed properly to the controller action. Below are the changes... however after the action goes to return the model, I get an obj reference not set to instance of the obj err.
Action:
[HttpPost]
public ActionResult Index(int CasinoID, DateTime Date)
{
var id = Int32.Parse(Request.Form["CasinoID"].ToString());
var model = TRBL.GetTransactionTestsData(id, Date);
return View(model);
}
Change to DDL:
#Html.DropDownList("CasinoID", Model.TerminalReceiptPostData.CasinoIdDDL, "Select Casino")
The int CasinoID will be bound by a form field with the name CasinoID. I think the #Html.DropDownListFor is not generating the 'name' you want.
You can add name explicitly like
#Html.DropDownListFor(o => o.TerminalReceiptPostData.CasinoIdDDL, Model.TerminalReceiptPostData.CasinoIdDDL, new { id = "CasinoID", name="CasinoID"})
Or better to create a ViewModel with the fields CasinoID, Date & CId and use BindProperty on that ViewModel instance
Okay so I figured out what was going on..
I actually have two seperate controller actions named Index. One for post and one for get. Since I am now sending back a model on the POST one, the drop down list was not getting "re-filled" with the drop downs... So i simply took a call from my GET action and added it to the post..
[HttpPost]
public ActionResult Index(int CasinoID, DateTime Date)
{
var id = Int32.Parse(Request.Form["CasinoID"].ToString());
var model = TRBL.GetTransactionTestsData(id, Date);
model.TerminalReceiptPostData = TRBL.GetCasinosDDL();
return View(model);
}
Probably not the best way to do it, but works fine.
I've searched around and can't find an answer to my problem.
I've got a View that takes accepts Foo in like so:
#model IEnumerable<MyApplication.Models.Foo>
Inside of the view I've got a table. I populate the the table just doing a simple foreach loop through my Foo model. I have added a column where a user can add details to each row in a text area and save.
So the view looks like this:
#foreach (var item in Model)
{
<tr>
<th>
#using (Html.BeginForm("AddDetails", "MyController", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(u => item.Id)
#Html.TextAreaFor(u => item.Details, new { #class = "form-control" })
<input type="submit" value="Save" class="btn btn-sm btn-default" />
}
</th>
</tr>
}
My controller is setup like this:
[HttpPost]
public ActionResult AddDetails(Foo foo)
{
}
When I set a breakpoint on that controller foo always has null values for the Details property and the Id is always 0. When it's passed into the view it's got all of the properties set to right, so I'm not sure why it is not posting back properly?
I've tried this:
#using (Html.BeginForm("AddDetails", "MyController", FormMethod.Post, new { foo = Model }))
I have also tried removing the foreach loop and changing it to pass in just a single Model to the View and for the View to accept just the single model instead of an IEnumerable. When I do this it posts back just fine.
And that does not work either. I'm wondering if it has something to do with the fact that my form is within a foreach loop and perhaps I need to do a little more work like add a data-id attribute to the element and just capture the click event on the button and do an AJAX call to the controller and pass the right values along that way?
EDIT Sorry I completely forgot the view model. Here is what that looks like:
public class Foo
{
public int Id { get; set; }
public string Details { get; set; }
}
Generally speaking it's bad form to post to a different model than what your form view is composed from. It makes a lot of things difficult, not the least of which is recovering from validation errors.
What you're doing here is looping through a list of Foo and creating multiple forms that will each submit only a single Foo to an action that takes only a single Foo. While the post itself is fine, using the *For helpers on a model that is not the same as what you're posting to will likely not generate the proper input names necessary for the modelbinder to bind the posted values onto that other model. At the very least, if you need to return to this view because of a validation error, you will not be able to keep the user's posted data, forcing them to repeat their efforts entirely.
Ideally, you should either post the entire list of Foo and have just one form that wraps the iteration (in which case, you would need to use for rather than foreach). Or you should simply list the Foos with a link to edit a particular Foo, where you would have a form for just one Foo.
try to do this.possible problems
form should outside the table
single form for whole table
wrap your column editor inside tbody not thead
#using (Html.BeginForm("AddDetails", "MyController", FormMethod.Post))
{
//thead and tbody
}
Since no answer was reported for testing the for loop instead of foreach.
I was stuck all day yesterday using foreach loop and always got 0 items in the post method. I changed to for loop and was able to receive the updated items in the post method.
For reference I am using asp.net core 3.2
This is the foreach loop:
#{
foreach(var role in #Model.RolesList)
{
<div class="btn-check m-1">
<input type="hidden" asp-for="#role.RoleId" />
<input type="hidden" asp-for="#role.RoleName" />
<input asp-for="#role.IsSelected" class="form-check-input" />
<label class="form-check-label" asp-for="#role.IsSelected">#role.RoleName</label>
</div>
}
}
This is the for loop:
#for(int i = 0;i< Model.`enter code here`RolesList.Count();i++)
{
<div class="btn-check m-1">
<input type="hidden" asp-for="RolesList[i].RoleId" />
<input type="hidden" asp-for="RolesList[i].RoleName" />
<input asp-for="RolesList[i].IsSelected" class="form-check-input" />
<label class="form-check-label" asp-for="RolesList[i].IsSelected">#Model.RolesList[i].RoleName</label>
</div>
}
I have a form:
#using (Html.BeginForm("QuoteUpdate", "Home", FormMethod.Post))
{
#Html.DropDownList("network", Model.availableNetworks);
#Html.DropDownList("grade", Model.grades);
#Html.HiddenFor(o => o.Product.id);
<button type="submit">Get Quote</button>
}
And a controller:
[HttpPost]
public ActionResult QuoteUpdate(int? id, string network, string grade )
{
}
The id property is always null after form is submitted. I have checked source and the hidden field has the correct value in the rendered HTML.
I cannot figure out why this parameter is always null. What am I missing?
Since you're accessing a nested property on your model, the generated HTML is probably something like this:
<input type="hidden" id="Product_id" name="Product.id" />
When your form gets posted to the controller action, there's not a parameter that matches up with Product.Id.
You could work around this by changing the generated name of the input (see this answer for more about how to do that):
#Html.HiddenFor(o => o.Product.id, new { Name = "id" });
Which will generate:
<input type="hidden" id="Product_id" name="id" />
Then things should model bind correctly.
I have a model with one of the property of type object . This property is a dynamic property and could sometime contain a string or a date or a Boolean.
I have a editor template for each type i.e boolean , string , date etc .
The problem I have is when the page is posted , the postback contains a array instead of the actual value. The first element of the array contains the actual value.
Why is the value being returned as a array ?
My model
public string Description;
public string Name { get; set; }
public Type Type{ get; set; }
object _value;
public object Value { get;set;}
statement in the view
#Html.EditorFor( m => m.Value)
Edit : Corrected the object name from _value to Value. It was a wrong Ctrl V operation.
Edit : The HTML rendered in the browser
When the object contain a boolean value (checkbox):
<div>
<input checked="checked" data-val="true" data-val-required="The BoolJPY field is required." id="FurtherInformationFieldObject_Properties_1__Value" name="FurtherInformationFieldObject.Properties[1].Value" type="checkbox" value="true"><input name="FurtherInformationFieldObject.Properties[1].Value" type="hidden" value="false">
When the object contains a string(Textbox) :
<div id="divStringField"><input class="text-box single-line valid" data-val="true" data-val-required="The String Field field is required." id="FurtherInformationFieldObject_Properties_2__Value" name="FurtherInformationFieldObject.Properties[2].Value" type="text" value=""> </div>
Edit 2 : Posting the complete model and view code.
Controller code :
public ActionResult Edit(string name ="field1" )
{
Models.DynamicData data1 = new Models.DynamicData();
//all this comes from the database table. I am putting the value directly in field just for simplicity
// this is exactly how I convert the value from the entity to the model
data1.Description = "Field1 Description";
data1.Name = "field1";
data1.Type = typeof(string);
data1.Value = Convert.ChangeType("MyStringValue", data1.Type);
//similarly add few more fields to the model collection
return View(data1);
}
[HttpPost]
public ActionResult Edit(Models.DynamicData model)
{
// break point here : model.Value shows a array of string instead of the edited value.
return View(model);
}
View :
#model SampleDynamicDataProject.Models.DynamicData
#{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>DynamicData</legend>
<div class="editor-label">
#Model.Description
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Value)
#Html.ValidationMessageFor(model => model.Value)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
I should explain I used object as type for Value property because the value could be string or bool or date ex data1 in above controller could look like below
data1.Description = "Field2 Description";
data1.Name = "field2";
data1.Type = typeof(bool);
data1.Value = Convert.ChangeType("true", data1.Type); // database stores "true" as string which is converted into a boolean and stored in the object.
As shown in the code , my problem is in the post action for Edit , I get Value as an array even for a simple string.
The sample project code here https://drive.google.com/file/d/0B3xCaeRk2IQZSTM0aHdoWEtNYW8/edit?usp=sharing
I got a answer to my question at one of the other forums.
Basically the reason MVC binder is returning a array is because it does not understand what type of data/control is used in the html and the model binder fails.
I got around my issue by modifying the model to have two different property a
public String StringValue
public Bool BooleanValue
I use the StringValue field when the Type is String , Date , Number etc.
I use the BooleanValue for field with Type as Boolean.
Its not the cleanest approach but it will have to do till the point I write my own custom model binder.
Thanks to bruce who answered my question here http://forums.asp.net/p/1961776/5605374.aspx?Re+MVC+4+Postback+returns+a+array+for+property+of+type+object+in+my+model
I now understand why the model binder fails.
Pasting his answer here for the benefit of others
you need to understand how browser postback is done. on form submit a collection of name/value pairs is sent. the name is the form element name, the value is the elements value. standard url encoding is done. so for:
the postdata is
foo=1&bar=true
note the post data is just a string with no type data. the brwser allows duplicate name, so
the post data is:
foo=1&foo=true
when asp.net load the post data into the form collection (which is just a dictionary), it can not add the key "foo" twice, but concats the values seperated by a "," ("1,true"). the binder just treats it as a string array named foo with 2 values.
now we get to another browser behavior. form elements that support checked (radio and checkbox) are only include the post data if checked. this causes a problem for the mvc binder with checkbox, becuase it can not tell from the postback data if the element was not checked or not included. this is important if you are using tryupdate to apply only a subset of the model properties, becuase only a subset was rendered. to get around this, the checkbox helper renders two fields with the same name, a hidden with the value "false" and a checkbox with the value "true".
I'm trying to pass a nullable bool to my controller. But when I pass the bool value from my view to the controller, it's being passed null all the time regardless of the value it has in my view.
Here is my view:
#model Cars.Models.Car
#using (Html.BeginForm("Index", "Home", FormMethod.Post,
new { id = "CategoryFormID"})) {
<label>Convertible:</label>
<input type="checkbox" id="ConvertibleID" name="convertible"/>
<button type="submit" name="submit" value="search" id="SubmitID">Search</button>
}
And my controller:
[HttpPost]
public ActionResult Index(bool? convertible){
var cars = from d in db.Car
select d;
if (convertible.HasValue)
{
cars = cars.Where(x => x.Convertible == convertible);
}
return View("SearchResult", cars);
}
I also have other fields such as drop down lists and text fields, but they're being passed flawless. Any help would be really appreciated.
Update:
Thank you for your fast responds. However, I did try giving it a value="True" as you guys suggested. There is only 2 options now: null and true. But my intention is to use nullable bool to have three options: null (when user doesn't touch the checkbox), true(checked) and false(unchecked). I know it sounds not smart and silly, but I'm just trying to figure out how nullable bool is working, and what is the intention of having such a thing in C# (I'm new with C#).
I was wondering if it is possible to do so with just checkbox and without the use of dropdownlist or radio buttons.
1 give a value in your checkbox
<input type="checkbox" value="True" id="ConvertibleID" name="convertible"/>
Maybe you can take it from request
Request.Form["convertible"]