What does values["PromoCode"] reference? - c#

I am going through this tutorial and am confused as to what he is refrencing with the first part of this statement, at first i thought it was the values in the promo code table but there is no model to build that table from. The tutorial link is as follows:http://www.asp.net/mvc/overview/older-versions/mvc-music-store/mvc-music-store-part-9
string.Equals(values["PromoCode"], PromoCode,
StringComparison.OrdinalIgnoreCase) == false

PromoCode is a constant
values["PromoCode"] is one of the posted values from the form : see in the view
You can access the FormCollection (values is a FormCollection, which stores all inputs from the form, and inherits from NameValueCollection) by its keys : the keys are the names of the inputs.
<div class="editor-field">
#Html.TextBox("PromoCode") // it's not a TextBoxFor
</div>
this will generate something like
<input type="text" name="PromoCode" id="PromoCode" />
It's not linked to anything in the model, indeed.

See the method signature:
public ActionResult AddressAndPayment(FormCollection values)
So it is a collection of all posted values.

See the signature of the method:
public ActionResult AddressAndPayment(FormCollection values) // <-- here
{
// ...
if (string.Equals(values["PromoCode"], PromoCode,
StringComparison.OrdinalIgnoreCase) == false)
{
// ...
}
// ...
}
the values is of type FormCollection, which means the data collection in the HTML <form> posted from browser, the MVC model-binding will gather form data into FormCollection instance (the values here), so you may get form data by code like values["PromoCode"], which is the same as Request.Form["PromoCode"] in classic asp/asp.net.

Related

Form not returning value to controller action due to ID conflict

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.

Getting value from jquery datetimepicker in MVC

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.)

MVC 4 : Postback returns a array for property of type object in my model

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".

MVC3 RadioButtonFor with enum

I'm having problems with the HtmlHelper, RadioButtonFor and an enum in my model. I have a strongly typed view, and I want the checkboxes to toggle my enum property.
Enum.cs
public enum Values{
Value1,
Value2
}
Model.cs
public class Model{
public Values MyProp{ get; set; }
;
View.cshtml
#Html.RadioButtonFor(model => model.MyPropi, Values.Values1)
Controller.cs
public ActionResult WizardFirstStep()
{
var model = new Model();
return View(model);
}
If I set the MyProperty value in the controller, the RadioButton is checked as expected. But after a post to the next wizard step, which gets the model as parameter, the property isn't set.
If it will help you to understand what I mean: If it would be C# and WPF I would use a IValueConverter.
btw: I use a HtmlHelper.ActionLink to get the model to the controller.
Thanks in advance
Try this, it should work as I have done the same thing before:
#Html.RadioButtonFor(model => model.MyProp, (int)Values.Values1, model.MyProp == Values.Values1)
Notice the cast to int, it ensures the correct value is used for html.
EDIT: Sorry, I think you also need the third parameter to ensure the correct radio button is set when loading the view.
I also assumed MyPropi was a typo and changed it to MyProp, please ensure this matches up correctly at your end
Sorry for any inconvenience. After posting here, I found the solution very quickly. My ActionLink was not submitting the #Html.BeginForm form. So i changed my radiobutton to:
#Html.RadioButtonFor(model => model.MyPropi, Values.Values1, new{ onClick = "this.form.submit();" })
which submits the correct value to my controller. For the moment this is okay. Maybe the ActionLink can post back the form data.
For Aspx pages:
<%:Html.RadioButtonFor(m => m.YourProp, Selected value of your enum like : demo1.enum1.value2)

ASP.NET MVC ViewModel and DropDownList

I have 2 properties in my ViewModel
class ViewModel1
{
Dictonary<int, string> PossibleValues {get;set;}//key/value
int SelectedKey {get;set}
}
I want to edit this using a Html.DropDownListFor
I want to get MVC to auto serialize the data into/from the ViewModel so I can the following
public ActionResult Edit(ViewModel1 model) ...
What's the best way to accomplish this?
As womp said, a browser will only submit the selected value of a drop down list. This is easily bound by the default model binder, see below.
If you are not editing the PossibleValues list on the client then there is no need to submit them back. If you need to repopulate the list then do it server side in your post action by using the same method you originally populated the Dictionary with.
For example in you page:
<%# Page Language="C#" Inherits="System.Web.Mvc.ViewPage<ViewModel1>" %>
<!-- some html here -->
<%= Html.DropDownListFor(x => x.SelectedKey, new SelectList(Model.PossibleValues, "key", "value"))%>
In your controller
[AcceptVerbs(HttpVerbs.Get)]
public ViewResult Edit() {
var model = new ViewModel1 {
PossibleValues = GetDictionary() //populate your Dictionary here
};
return View(model);
}
[AcceptVerbs(HttpVerbs.Post)]
public ViewResult Edit(ViewModel1 model) { //default model binding
model.PossibleValues = GetDictionary(); //repopulate your Dictionary here
return View(model);
}
Where GetDictionary() is a method that returns your populated Dictionary object.
See this similar question for more details
I don't think you'll be able to construct a dictionary from a dropdownlist on a form. A dropdownlist will only post one value back, which you could set as your SelectedKey property, but you won't be able to reconstruct the PossibleValues dictionary from it.
In order to reconstruct a dictionary, you're going to need to have a form field for every entry in it. You could do something like this, generated with a foreach loop over your dictionary:
<input type="hidden" name="PossibleValues[0].Key" value="key0">
<input type="hidden" name="PossibleValues[0].Value" value="value0">
<input type="hidden" name="PossibleValues[1].Key" value="key1">
<input type="hidden" name="PossibleValues[1].Value" value="value1">
.
.
.
Ultimately I would question the need to repopulate the dictionary from the form. If they can only choose one value, why wouldn't the PossibleValues just be a lookup from somewhere outside your ViewModel (like in your repository?) Why store it with the ViewModel?
The solution is custom ModelBinding in ASP.NET MVC framework here are some examples..
stevesmithblog.com/blog/binding-in-asp-net-mvc
www.singingeels.com/Articles/Model_Binders_in_ASPNET_MVC.aspx
odetocode.com/Blogs/scott/archive/2009/04/27/12788.aspx
odetocode.com/Blogs/scott/archive/2009/05/05/12801.aspx
hope you find them useful...
Thanks

Categories

Resources