I have a checkbox like this:
<%= Html.CheckBoxFor(x => x.IsSendingQualitySurvey) %>/>
when checking the checkbox and submitting, I get the typical 2 form values:
IsSendingQualitySurvey: true
IsSendingQualitySurvey: false
This is perfectly valid for the way the mvc modelbinder handles checkboxes.
Then in the controller action:
[HttpPost]
public ActionResult Edit(Guid id, TicketEditViewModel ticketEditViewModel)
ticketEditViewModel.IsSendingQualitySurvey (a normal bool) is always false.
I don't have any custom model binders and it works elsewhere.
Strangely enough I've had the same problem in another view quite a while ago, where I had to create the input manually, looks like this:
<input type="checkbox" value="true" name="<%=Html.NameFor(x => x.IsRequestingVisit) %>" id="<%=Html.NameFor(x => x.IsRequestingVisit) %>" />
This worked, but when I copied the exact same over to the other view, the value is still always false.
Wheres the catch?
Thanks.
EDIT Got the wrong end of the stick... sorry
Have you tried fetching the raw value out of the post data like so:
In the Controller:
[HttpPost]
public ActionResult Edit(Guid id, TicketEditViewModel ticketEditViewModel,
FormCollection fc) {
if(fc["IsSendingQualitySurvey"].Contains("true")) {
//Do something.
}
}
In the View:
<%= Html.CheckBoxFor(model => model.IsSendingQualitySurvey) %>
Hope this helps..
The model binder likely wont pickup the binding. My advice is to change your action to:
[HttpPost]
public ActionResult Edit(Guid id, TicketEditViewModel model)
The model binder wants to find properties in the Request that have a prefix that match your object name. Since there is no naming prefix on the client side, there are no properties with the prefix "ticketEditViewModel".
The alternative is to name a variable on the page:
<% var ticketEditViewModel = Model; %>
<%= Html.CheckBoxFor(model => ticketEditViewModel.IsSendingQualitySurvey) %>
For following model:
public class StartViewModel {
public string Id{ get; set; }
public string Name { get; set; }
public bool Accept { get; set; }
}
-> Accept =false.
The solution: Change public bool Accept { get; set; } to public string Accept { get; set; }
When submit, if checkbox is checked, The "Accept" value = "on". You can dectect checked value by the way.
I'm not sure whether this will work for you, but in fact it solved the same issue for me. Try adding (not replacing) a hidden field for the same model's property like this:
#Html.HiddenFor(x => x.IsSendingQualitySurvey)
Related
I have a model object structure with a Foo class that contains a Bar with a string value.
public class Foo
{
public Bar Bar;
}
public class Bar
{
public string Value { get; set; }
}
And a view model that uses that structure like this
public class HomeModel
{
public Foo Foo;
}
I then have a form in view that in Razor looks something like this.
<body>
<div>
#using (Html.BeginForm("Save", "Home", FormMethod.Post))
{
<fieldset>
#Html.TextBoxFor(m => m.Foo.Bar.Value)
<input type="submit" value="Send"/>
</fieldset>
}
</div>
</body>
In html that becomes.
<form action="/Home/Save" method="post">
<fieldset>
<input id="Foo_Bar_Value" name="Foo.Bar.Value" type="text" value="Test">
<input type="submit" value="Send">
</fieldset>
</form>
Finally the controller to handle the post loos like this
[HttpPost]
public ActionResult Save(Foo foo)
{
// Magic happends here
return RedirectToAction("Index");
}
My problem is that Bar in Foo is null once it hits the Save controller action (Foo is created but with an null Bar field).
I thought the model binder in MVC would be able to create the Foo and the Bar object and set the Value property as long as it looks like the above. What am I missing?
I also know my view model is a bit over complicated and could be simpler but I for what I'm trying to do I'd really help me if I could use the deeper object structure. The examples above uses ASP.NET 5.
Firstly, the DefaultModelBinder will not bind to fields so you need to use properties
public class HomeModel
{
public Foo Foo { get; set; }
}
Secondly, the helpers are generating controls based on HomeModel but you posting back to Foo. Either change the POST method to
[HttpPost]
public ActionResult Save(HomeModel model)
or use the BindAttribute to specify the Prefix (which essentially strips the value of prefix from the posted values - so Foo.Bar.Value becomes Bar.Value for the purposes of binding)
[HttpPost]
public ActionResult Save([Bind(Prefix="Foo")]Foo model)
Note also that you should not name the method parameter with the same name as one of your properties otherwise binding will fail and your model will be null.
I just discovered another reason this can happen, which is if your property is named Settings! Consider the following View model:
public class SomeVM
{
public SomeSettings DSettings { get; set; } // named this way it will work
public SomeSettings Settings { get; set; } // property named 'Settings' won't bind!
public bool ResetToDefault { get; set; }
}
In code, if you bind to the Settings property, it fails to bind (not just on post but even on generating the form). If you rename Settings to DSettings (etc) it suddenly works again.
I had the same problem and after I followed #Stephen Muecke steps I realized that the problem was caused because my inputs were disabled (I was disabling them with JQuery on document ready) as you can see it here: How do I submit disabled input in ASP.NET MVC?. At the end I used read-only instead of disabled attribute and all the values were sent successfully to the controller.
I had the same problem, but once I created a HIDDEN FIELD for the foreign-key...it all worked just fine...
FORM EXAMPLE:
#using (Html.BeginForm("save", "meter", FormMethod.Post))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
#Html.HiddenFor(model => Model.Entity.Id)
#Html.HiddenFor(model => Model.Entity.DifferentialMeter.MeterId)
#Html.HiddenFor(model => Model.Entity.LinearMeter.MeterId)
#Html.HiddenFor(model => Model.Entity.GatheringMeter.MeterId)
... all your awesome controls go here ...
}
ACTION EXAMPLE:
// POST: /Meter/Save
[HttpPost]
public ActionResult Save(Meter entity)
{
... world-saving & amazing logic goes here ...
}
PRETTY PICTURES:
In ASP.Net MVC I am opening one view from another view. The first view sends two values to the second view. In the second view the user can send an email.
I am having two problems.
The first problem is that the two values that I send from the first view aren't being shown in my second view.
The second problem is that I can't get the email form to trigger my email function in the controller.
Here's a more detailed explanation.
My first view named ViewOne is using the controller ControllerOne. In ViewOne I have the following code to call the second view, ViewTwo:
#Html.ActionLink("Go to second view", "ViewTwo", "Home", new { firstval = firstval, secondval = secondval }, null)
When the ActionLink is clicked, the following function in the controller HomeController is called:
public ActionResult ViewTwo(string firstval, string secondval)
{
MyModel model = new MyModel();
model.firstval = firstval;
model.secondval = secondval;
var list = new SelectList(new[]
{
new {ID="1",Name="One"},
new{ID="2",Name="Two"},
new{ID="3",Name="Three"},
},
"ID", "Name", 1);
model.myList = list;
return View(model);
}
So in the controller HomeController I attempt to populate the model myModel with the values I get from the first view, ViewOne.
The model MyModel looks like this:
public class MyModel
{
public string firstval { get; set; }
public string secondval { get; set; }
public IEnumerable<SelectListItem> myList { get; set; }
[Required]
[DisplayName("My name")]
public string reporter { get; set; }
[Required]
[DisplayName("Description")]
public string description { get; set; }
[DisplayName("Dropdown")]
public string myDropDownListValue { get; set; }
}
The view ViewTwo looks like this:
#model myapp.Models.MyModel
#{ ViewBag.Title = "Send e-mail"; }
<hgroup class="title">
<h1>#ViewBag.Title</h1>
<h2>#ViewBag.Message</h2>
</hgroup>
#using (Html.BeginForm("sendEmail"))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<fieldset>
<legend>Send e-mail</legend>
<p>First value:</p>
<p>#Html.LabelFor(m => m.firstval)</p>
<p>Second value:</p>
<p>#Html.LabelFor(m => m.secondval)</p>
<p>Reporter</p>
<p>#Html.TextBoxFor(m => m.reporter)</p>
<p>Dropdownlist</p>
<p>#Html.DropDownListFor(m => m.myDropDownListValue, Model.myList as SelectList)</p>
<p>Description:</p>
<p>#Html.TextAreaFor(m => m.description, new { #cols = 150, #rows = 5})</p>
<input type="submit" value="Send e-mail"/>
</fieldset>
}
In the controller HomeController, which is the same controller that has the ViewTwo() function that gets triggered right before the above form gets drawn, I have the following function:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult sendEmail(ContactModel model) // (string keyword, string partofspeech, string reporter, string category, string description, string acceptance)
{
// code to send email
}
So I want this function, sendEmail, to get triggered whenever I submit the form. But that doesn't happen. What happens when I click the submit button (labeled "Send e-mail") is that the view ViewTwo gets reloaded and the ActionResult ViewTwo() in the controller HomeController gets triggered. This is my second (and biggest) problem.
Also, my first problem is that
<p>#Html.LabelFor(m => m.firstval)</p>
Doesn't show the value that gets sent from the first view. It shows the string "firstval". Before the form is drawn I can see in the function ViewTwo() that the value gets correctly sent from the first view.
Any ideas?
EDIT:
Second problem solved. See my reply below.
You have a few options, normally with a postback you would submit the form with an <input type="submit" value="sendEmail" />, the values in the form would be represented in a ViewModel like:
public class EmailFormViewModel()
{
public string value1 {get; set;}
public string reporter {get; set;}
//More properties on the form
}
Your endpoint would look like this:
[HttpPost]
public ActionResult SendEmail(EmailFormViewModel model)
{
//Send the email
}
If you still want to use a hyperlink to submit the form, which natively performs a GET request, you will can catch the click with javascript, and manually send the form via Ajax.
Something like:
$('#sendEmail').click(function(e) {
e.preventDefault();
$.ajax({
type: 'POST',
data: $('#formId').serialize(),
url: '/controllerName/sendemail'
}).done(function(response) {
//Do something on success response
});
});
Update:
You should also decorate your post action sendEmail with [ValidateAntiForgeryToken] and add a #Html.AntiForgeryToken() within the form. This will help protect against cross site forgery requests.
You can build your form, endpoint and model like this:
#using (Html.BeginForm("sendEmail"))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary()
<p>#Html.LabelFor(m => m.value1)</p>
<p>#Html.EditorFor(m => m.value1)</p>
<p>#Html.LabelFor(m => m.reporter)</p>
<p>#Html.EditorFor(m => m.reporter)</p>
<p>#Html.LabelFor(m => m.myDropDownListValue)</p>
<p>#Html.DropDownListFor(m => m.myDropDownListValue, Model.myList as SelectList)</p>
<p>#Html.LabelFor(m => m.myTextAreaValue)</p>
<p>#Html.TextAreaFor(m => m.myTextAreaValue, new { #cols = 150, #rows = 5})</p>
<input type="submit" value="Send Email"/>
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult SendEmail(myModel model)
{
//Send the email
}
public class myModel
{
public IEnumerable<SelectListItem> myList { get; set; }
[DisplayName('Value 1')]
public string value1 { get; set; }
[DisplayName('Reporter')]
public string reporter { get; set; }
[DisplayName('Text Area')]
public string myTextAreaValue { get; set; }
[DisplayName('Dropdown')]
public string myDropDownListValue { get; set; }
}
As long as you are already on the same controller, it will postback to /controllername/sendemail with the form data inside the post. You should also look up attributes on your models, you can enforce descriptions and validations for example. Check here for more details, its MVC 2 but still relevant.
If you really want to be able to GET the values instead of POSTing them, change the form's action to GET and change the target to be sendEmail
Remove the ActionLink and replace it with a simple submit button
I know you said you wanted to keep the ActionLink, but this will achieve the same thing
I managed to solve my first problem. Once I specified which controller the function sendEmail is in, that code finally got triggered. Like so:
#using (Html.BeginForm("sendEmail", "Home"))
Now if I can only figure out why
<p>#Html.LabelFor(m => m.firstval)</p>
isn't working then I'm home safe.
It actually prints out the string "firstval" instead of taking the value of the string variable firstval that I set in the model. (See my first post for more detailed explanation).
EDIT:
I fixed that last problem. The very werid thing is that the above code with LabelFor doesn't work. But if I do this instead:
<p>#Model.firstval</p>
then I get the value. But it doesn't get sent back to the controller when the form is submitted. But that I solved with:
#Html.HiddenFor(m => m.firstval)
So HiddenFor works for me, LabelFor doesn't.
Case closed. I'm throwing the "solved checkmark" to the guy who gave me all that help here above. He did awesome. But the real solution is in this post. :)
I have my model as follows
public class PlaceOrder
{
public int orderCode { set; get; }
public string Order_ID { set; get; }
public int orderDetailCode { set; get; }
[Required]
public string Topic { set; get; }
//50 more fields are there
}
Using editorforModel displays all the fields in the model. I want to have a editor helper which takes the property name and only shows editor for that specific property.
I wrote a create/edit/details actions for my model and working fine. What my final goals is that I want to have edit button next to every field on the details view. As soon I click on edit it allows to update and validate the input as well
EDIT
I am using following snippet for edit link
#(Html.Awe().PopupFormActionLink()
.LinkText("Edit")
.Name("editP")
.Url(Url.Action("PropertyEdit", "PlaceOrder", new
{
PropertyName = Html.NameFor(model => model.SubjectCategoryCode),
propertyValue = Html.IdFor(model => model.SubjectCategoryCode),
ordercode = Model.orderCode
})
)
.Title("Editor for " + Html.NameFor(model => model.SubjectCategoryCode))
and I want something that I pass the field name and it dispalys the relevant fields and do the validation
You could just use an EditorFor and a form for each field:
#using Html.BeginForm("action", "controller")
{
#Html.EditorFor(m => m.ordercode)
<input type="submit" />
}
#using Html.BeginForm("action", "controller")
{
#Html.EditorFor(m => m.orderDetailCode)
<input type="submit" />
}
Of course, you would need a different action for each item and you need a way to get the other values as well, since you're only posting one value to the controller. To achieve this you could include a hidden field with the id and retrieve the other values on the server.
There's the Html.EditorFor(m => m.Property) method for this (your model should be set to PlaceOrder to use this helper, as with any statically typed helpers).
Edit: Bah, Kenneth was faster :-).
Right guys. I need your brains as I can't find a way to do this properly.
I have a view model:
public class EditUserViewModel
{
public User User;
public IQueryable<ServiceLicense> ServiceLicenses;
}
User is unimportant as I know how to deal with it.
ServiceLicenses has the following implementation:
public class ServiceLicense
{
public Guid ServiceId { get; set; }
public string ServiceName { get; set; }
public bool GotLic { get; set; }
}
Getting a checked list of users is cool. It works like a charm.
<fieldset>
<legend>Licenses</legend>
#foreach (var service in Model.ServiceLicenses)
{
<p>
#Html.CheckBoxFor(x => service.GotLic)
#service.ServiceName
</p>
}
</fieldset>
The problem I'm having is getting the updated ServiceLicenses object with new checked services back to the HttpPost in my controller. For simplicity lets say it looks like this:
[HttpPost]
public ActionResult EditUser(Guid id, FormCollection collection)
{
var userModel = new EditUserViewModel(id);
if (TryUpdateModel(userModel))
{
//This is fine and I know what to do with this
var editUser = userModel.User;
//This does not update
var serviceLicenses = userModel.ServiceLicenses;
return RedirectToAction("Details", new { id = editUser.ClientId });
}
else
{
return View(userModel);
}
}
I know I am using CheckBox the wrong way. What do I need to change to get serviceLicenses to update with the boxes checked in the form?
i understand that ServiceLicenses property is a collection and you want MVC binder to bind it to you action parameters property. for that you should have indices attached with inputs in your view e.g
<input type="checkbox" name = "ServiceLicenses[0].GotLic" value="true"/>
<input type="checkbox" name = "ServiceLicenses[1].GotLic" value="true"/>
<input type="checkbox" name = "ServiceLicenses[2].GotLic" value="true"/>
Prefix may not be mandatory but it is very handy when binding collection property of action method parameter. for that purpose i would suggest using for loop instead of foreach and using Html.CheckBox helper instead of Html.CheckBoxFor
<fieldset>
<legend>Licenses</legend>
#for (int i=0;i<Model.ServiceLicenses.Count;i++)
{
<p>
#Html.CheckBox("ServiceLicenses["+i+"].GotLic",ServiceLicenses[i].GotLic)
#Html.CheckBox("ServiceLicenses["+i+"].ServiceName",ServiceLicenses[i].ServiceName)//you would want to bind name of service in case model is invalid you can pass on same model to view
#service.ServiceName
</p>
}
</fieldset>
Not using strongly typed helper is just a personal preference here. if you do not want to index your inputs like this you can also have a look at this great post by steve senderson
Edit: i have blogged about creating master detail form on asp.net mvc3 which is relevant in case of list binding as well.
Description
I have a payment page that includes a form for entering bank account information. I have encapsulated the bank account information into a Model / Editor Template. The page itself has its own View Model, which happens to contain a BankAccount property to be passed in to the Editor.
[[View Models]]
public class PaymentPageModel {
public SomeProperty1 { get; set; }
public SomeProperty2 { get; set; }
public BankAccount BankAccount { get; set; }
...
}
public class BankAccount {
public int BankAccountTypeID { get; set; }
public string BankName { get; set; }
public string ABACode { get; set; }
public string AccountNumber { get; set;}
public IEnumerable<SelectListItem> BankAccountTypes {
get { ... // Constructs the list }
}
}
[[Payment Page HTML]]
<% using (Html.BeginForm()) { %>
<%: Html.EditorFor(m => m.BankAccount) %>
... // Other miscellaneous stuff not related to the actual BankAccount
<% } %>
[[Editor Template]
...
<%: Html.DropDownListFor(m => m.BankAccountTypeID, Model.BankAccountTypes) %>
...
Problem
Initially, this worked perfectly when I was strongly-typing the Payment page directly to the BankAccount model. The dropdown list was being populated properly, and the correct value from the model was being selected.
I recently modified the page, strongly-typing it to the PaymentPageModel, which contains the BankAccount model as a property. The HTML has not been modified. The result now, is that all the HTML values in the Editor Template are being populated properly, except for the DropDownList. It is binding the list of values properly from the BankAccountTypes select list, but the selected value is NOT being bound. I have checked to make sure that the value it is supposed to be binding to IS set properly by outputting it right next to the DropDownList.
This is driving me nuts, and is making me really question the reliability of Model binding and HTML Helpers in general, especially if I am unable to combine complex view models with Editor Templates to encapsulate presentation/functionality.
Any suggestions are greatly appreciated.
If you have strongly typed the editor template to PaymentPageModel in your main view instead of:
<%: Html.EditorFor(m => m.BankAccount) %>
you could try:
<%: Html.EditorForModel() %>
and in your editor template:
<%: Html.DropDownListFor(m => m.BankAccount.BankAccountTypeID,
Model.BankAccount.BankAccountTypes) %>