MVC3 Empty model on post - asp.net-mvc-3

I have model which has data.
The i create form with only on button, i don't use the model's data in there.
When i press the button it goes correctly to the HTTPPOST method but the model is completely empty.
HTML:
#model ViewModels.RequestDeletionViewObject
#using (Html.BeginForm())
{
<input type="submit" value="submit"/>
}
The RequestDeletionViewObject:
public class RequestDeletionViewObject : ViewModelBase
{
public TreeGridData NodeFilespacesData { get; set; }
public Dictionary<long, string> EmailList{ get; set; }
}
Controller:
public ActionResult RequestDel()
{
return View(_businessLogic.GetData());
}
[HttpPost]
public ActionResult RequestDel(RequestDeletionViewObject model)
{
return View(_businessLogic.GetData());
}
Please help me, I have done similar thing in many other places and it worked there, but not here, i don't know what am i missing?
Thanks

You don't have any form controls in your form. A form will only post data that is in a form control (textbox, hidden field, checkbox, etc..)
It doesn't matter what data you send to the view, it will only post back data in form controls within the form.

Related

Validation in mvc4 not taking place

MODEL
public class SearchTerm
{
[Required(ErrorMessage="please enter")]
public string SearchTrm { get; set; }
}
View
#using (#Html.BeginForm())
{
#Html.ValidationSummary();
#Html.AntiForgeryToken();
....
#Html.TextBoxFor(m=>m.SearchTrm)</span>
<input type="submit" value="Search"/>
#Html.ValidationMessageFor(m=>m.SearchTrm)
#using (Html.BeginForm("Search","Home"))
{
#Html.DropDownList("SelectedFieldId", new SelectList(Model.Fields, "FieldID", "NiceName", Model.SelectedFieldId));
}
}
controller
[HttpPost]
public ActionResult Search(SearchTerm Model)
{
// some code here....
}
When i click a empty search I want the validation message to take place but instead page is getting postback and i am having NullReferenceException
Mention the script name, #section scripts { ...} and check whether jqueryval has 2 files - ~/scripts/jquery.validate.min.js","~/scripts/jquery.validate.unobtrusive.min.js","~/scripts

MVC3: button to send both form (model) values and an extra parameter

In an MVC3 project, i use an Html.BeginForm to post some (model-)values. Along with those i want to send an extra parameter that is not part of the form (the model) but in the ViewBag. Now, when i use a Button (code in answer here: MVC3 razor Error in creating HtmlButtonExtension), all the form values are posted but the extra parameter remains null. When i use an ActionLink, the parameter is posted but the form values are not :) Any know how i can combine the two? Thanks!
#Html.Button("Generate!", new { id = ViewBag.ProjectID })
#Html.ActionLink("Generate!", "Post", new { id = #ViewBag.ProjectID })
My advice would be to declare a new Object in your App.Domain.Model something like this
namespace App.Domain.Model
{
public class CustomEntity
{
public Project projectEntity { get; set; }
public int variableUsed { get; set; }
}
}
In your view you can acces them easily by using CustomEntity.projectEntity and CustomEntity.variableUsed.
Hope it helps
You can do something like below.
View code
#using (Html.BeginForm("ActionName", "ControllerName", FormMethod.Post, new { #id = "frmId", #name = "frmId" }))
{
#*You have to define input as a type button not as a sumit. you also need to define hidden variable for the extra value.*#
<input type="hidden" name="hndExtraParameter" id="hndExtraParameter" />
<input value="Submit" type="button" id="btnSubmit" onclick="UpdateHiddenValue()" />
}
<script type="text/javascript">
function ValidateUser() {
$("#hndExtraParameter").val('Assignvaluehere');
$("#frmId").submit();
}
</script>
Controller Code
[HttpPost]
public ActionResult ActionName(Model model, string hndExtraParameter)
{
//Do your operation here.
}

Losing data in models and collections inside the ViewModel on postback

I am using a viewmodel that contains a few other models(entities) for each partial view.
I am rendering a partial view by passing the entity which is inside the ViewModel. My partial view has a few fields and some buttons. On click of button (which is inside my partial view) the form is being posted back with the data in a sub entity, whereas my viewmodel is always posted back as null...
I need the data to be present in my viewmodel on post back.
All views are strongly typed:
Code:
public class OrdersVM
{
public FiltersVM filterCriteria { get; set; }
public IEnumerable<MeterInventory> meters { get; set; }
public string assignTo { get; set; }
public IEnumerable<SelectListItem> AssigneeOptions { get; set; }
}
public partial class Meters
{
public int MTRNO { get; set; }
public string LOCName { get; set; }
}
public class FiltersVM
{
public string Center { get; set; }
public DateTime? DueDate { get; set; }
}
View Code
#model OrdersVM
#{
ViewBag.Title = "Orders";
}
#using (Html.BeginForm())
{
<div>
#Html.Partial("~/Views/Base/Filters.cshtml", Model.filterCriteria)
</div>
#foreach (var item in Model.meters)
{
<table>
<tr>
<td>
#Html.Encode(item.LOCNAME)
</td>
</tr>
</table>
}
}
Controller code
[HttpPost]
public ActionResult Index(OrdersVM orders, FiltersVM filters)
{
//orders is null
//filters has values
}
Thanks Olivehour. I am using the partial view "Filters.cshtml". and am rendering the same.
Below is the code for partial view :
#model ViewModels.FiltersVM <fieldset>
<legend>Order Assignment</legend>
<table id="tbl1" class="tableforcontrols">
<tr>
<td>
<div class="editor-label">
#Html.LabelFor(model => model.LDC)
</div>
</td>
<td>
<div class="editor-field">
<input type="submit" value="Search" id="btnSearch" name="button" />
</div>
</td>
<td>
<div class="editor-field">
<input type="submit" class="cancel" value="Reset" id="btnReset" name="button" />
</div>
</td>
</tr>
</table> </fieldset>
I tried with single argument "OrdersVM" (parent view model) but no luck.
[HttpPost]
public ActionResult Index(OrdersVM orders)
but if I pass the parent viewmodel to the partial view it was holding the data in OrdersVM.filterCriteria but not for properties (IEnumerable meters, string assignTo and Enumerable AssigneeOptions)
#Html.Partial("~/Views/Base/Filters.cshtml", Model)
I am new to MVC. Please let me know if any one finds the solution.
Thanks in advance.
It looks like you have a couple of problems here. One probable reason why the orders arg is null in your action method is because it doesn't look like you are rendering any input elements. You just have #Html.Encode(item.LOCNAME).
In order for the default model binder to construct an instance of OrdersVM and pass it to the action method, it needs to have input from the HTTP POST. You need something more like #Html.TextBoxFor(m => item.LOCNAME).
The second problem I think is that you have 2 arguments in the action method. Since the OrdersVM already has a FiltersVM property, you should just be able to have a single OrdersVM argument to the action method. During the HTTP POST, you can just access FiltersVM properties from OrdersVM.filterCriteria. This will lead to your 3rd challenge, though, since the meters property on OrdersVM is an IEnumerable collection.
To solve this, first have a couple reads of this article about model binding collections. It's old, but it still applies in MVC3. Read it and really wrap your head around it.
If you don't like using integers to index your collection fields, there is an HTML helper written by Steve Sanderson that allows you to index collection inputs using GUID's. We use this all the time, but it can be tricky -- mainly, you should always put the collection item in a partial view. For now, you might just be better off using integer-based indexing as outlined in the Haacked article.
It sounds like you are comming from Webforms. To transition to MVC you need to remove the thought of PostBack. This is concept that doesn't really exist on the web but Webforms faked it for us.
In MVC you usually start with a GET request like /edit/{someId}. From here you load the data for the viewmodel from the database and render the view. Now let's say that all data in the viewmodel is editable so each property have it's own input field. The user edits some data and saves the form. This issues a POST to the server.
Assume we have this POST method
[HttpPost]
public ActionResult Edit(MyViewModel model)
In this case you have all the data you need modelbinded because all data existed in the form.
You could do this and get the same view rendered because all data was databinded.
[HttpPost]
public ActionResult Edit(MyViewModel model){
return View(model);
}
Now let's pretend you have a dropdown in your form. Then you would have these two properties in your viewmodel.
public int CarId { get; set; }
public IEnumerable<SelectListItem> CarOptions {get; set; }
When you post the form this time the CarId will be populated in the ViewModel but not CarOptions because they are not a part of the form data. What you do if you would want to return the same view again is to reload the missing parts.
[HttpPost]
public ActionResult Edit(MyViewModel model){
model.CarOptions = LoadCarOptions();
return View(model);
}
It's certainly possible to modelbind that too if you put it in a hidden field. But it's easier and probably more effective to reload it from server/database again. This is the normal approach taken when working with MVC.

Strongly Typed RadioButtonlist

I want to get some options (say payment method cash, credit card etc.) and bind these to radio buttons. I believe there is no RadioButtonList in MVC 3.
Also, once radios are bound I want to show the previously selected option to the user while editing the answer.
As always you start with a model:
public enum PaiementMethod
{
Cash,
CreditCard,
}
public class MyViewModel
{
public PaiementMethod PaiementMethod { get; set; }
}
then a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new MyViewModel();
return View(model);
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
}
and finally a view:
#model MyViewModel
#using (Html.BeginForm())
{
<label for="paiement_cash">Cash</label>
#Html.RadioButtonFor(x => x.PaiementMethod, "Cash", new { id = "paiement_cash" })
<label for="paiement_cc">Credit card</label>
#Html.RadioButtonFor(x => x.PaiementMethod, "CreditCard", new { id = "paiement_cc" })
<input type="submit" value="OK" />
}
And if you want some more generic solution which encapsulates this in a helper you may find the following answer helpful.
This is how I like to bind RadioButtonLists. The view model has a collection of my strongly typed objects. For example, maybe PaymentOptions is a code table. Along with the collection is a SelectedPaymentOptionKey (or Selected*Id if you prefix your primary keys with Id). Initially this key will just be default 0, but on postback, it will hold the value of the selected item.
public class PaymentSelectionVM
{
public ICollection<PaymentOption> PaymentOptions { get; set; }
public int SelectedPaymentOptionKey { get; set; }
}
public ViewResult PaymentSelection()
{
var paymentOptions = db.PaymentOptions.ToList();
return View(
new PaymentSelectionVM {
PaymentOptions = paymentOptions,
//This is not required, but shows how to default the selected radiobutton
//Perhaps you have a relationship between a Customer and PaymentOption already,
//SelectedPaymentOptionKey = someCustomer.LastPaymentOptionUsed.PaymentOptionKey
// or maybe just grab the first one(note this would NullReferenceException on empty collection)
//SelectedPaymentOptionKey = paymentOptions.FirstOrDefault().PaymentOptionKey
});
}
Then in the View:
#foreach (var opt in Model.PaymentOptions)
{
#*Any other HTML here that you want for displaying labels or styling*#
#Html.RadioButtonFor(m => m.SelectedPaymentOptionKey, opt.PaymentOptionKey)
}
The m.SelectedPaymentOptionKey serves two purposes. First, it groups the Radio buttons together so that the selection is mutually exclusive(I would encourage you to use something like FireBug to inspect the generated html just for your own understanding. The wonderful thing about MVC is the generated HTML is fairly basic and standard so it shouldn't be hard for you to eventually be able to predict the behavior of your views. There is very little magic going on here.). Second, it will hold the value of the selected item on postback.
And finally in the post handler we have the SelectedPaymentOptionKey available:
[HttpPost]
public ActionResult PaymentSelection(PaymentSelectionVM vm)
{
currentOrder.PaymentOption = db.PaymentOptions.Find(vm.SelectedPaymentOptionKey);
....
}
The advantage of this over using SelectListItems is you have access to more of the object's properties in the case that you are displaying a grid/table and need to display many values of the object. I also like that there are no hard coded strings being passed in the Html helpers as some other approaches have.
The disadvantage is you get radio buttons which all have the same ID, which is not really a good practice. This is easily fixed by changing to this:
#Html.RadioButtonFor(m => m.SelectedPaymentOptionKey, opt.PaymentOptionKey, new { id = "PaymentOptions_" + opt.PaymentOptionKey})
Lastly, validation is a bit quirky with most all of the radio button techniques I've seen. If I really needed it, I would wire some jquery up to populate a hidden SelectedPaymentOptionsKey whenever the radio buttons are clicked, and place the [Required] or other validation on the hidden field.
Another workaround for the validation problem
ASP.NET MVC 3 unobtrusive validation and radio buttons
This looks promising but I haven't had a chance to test it:
http://memoriesdotnet.blogspot.com/2011/11/mvc-3-radiobuttonlist-including.html
You should bind your options to SelectList in ViewModel and set Selected attribute to true for previously selected option

Have to double submit when using Remote attribute based validation

We have a field on our model which has a [Remote] attribute. When we store that field on a Hidden form element and then try to submit that form we have to click the submit button twice. Also interesting is that the 2nd time we click it no remote validation is occurring (so says Fiddler).
Thoughts?
Unable to repro. If the hidden field is decorated with the Remote attribute you won't be able to submit the form no matter how many times you click on the submit button if the remote function sends false.
For example:
Model:
public class MyViewModel
{
[HiddenInput(DisplayValue = false)]
[Remote("Check", "Home")]
public string Id { get; set; }
[Required]
public string Name { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel
{
Id = "1"
});
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
return View(model);
}
public ActionResult Check(string Id)
{
return Json(Id == "2", JsonRequestBehavior.AllowGet);
}
}
View:
#model AppName.Models.MyViewModel
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
#using (Html.BeginForm())
{
#Html.EditorForModel()
<input type="submit" value="OK" />
}
Because the remote function will always return false this form cannot be submitted. If the remote function returns true a single click would be enough to submit it assuming of course that the other validation passed.

Resources