How can I retrieve the List in my controller? - asp.net-mvc-3

My controller always gets "null" for the "adjModel" parameter.
How can I retrieve the values?
CONTROLLER
[HttpPost]
public ActionResult AdjustmentList(List<AdjustmentVM> adjModel)
{
// adjModel is null
}
VIEW
#model List<ExtFramework.ViewModels.BillingArea.AdjustmentVM>
<div class="no-fancybox">
#using (Html.BeginForm("AdjustmentList", "Deposit", new { depositId = ViewBag.depositId }))
{
<div>
<table id="adjustment">
<tr>
<th>Description</th>
<th>Montant</th>
</tr>
#foreach(var item in Model)
{
<tr>
<td>#Html.TextBoxFor(model => item.Description)</td>
<td>#Html.TextBoxFor(model => item.Amount)</td>
</tr>
}
</table>
<input type="submit" value="" class="save" />
</div>
}
</div>
MODEL
namespace ExtFramework.ViewModels.BillingArea
{
public class AdjustmentVM
{
public int AdjustmentId { get; set; }
public string Description { get; set; }
public decimal Amount { get; set; }
public int DepositId { get; set; }
}
}

This is where editor templates are useful. Instead of using a foreach loop to go through the list of view models, use #Html.EditorFor(m => m). Then, in a subfolder named EditorTemplates (an MVC naming convention) add a view with the name AdjustmentVM.cshtml. Again, this is another MVC naming convetion - using the name of the type being used. This file would look like:
#model AdjustmentVM
<tr>
<td>#Html.TextBoxFor(model => model.Description)</td>
<td>#Html.TextBoxFor(model => model.Amount)</td>
</tr>
The runtime will automatically loop over the items in the list and render the contents of the editor template, giving unique names for each form value, so that the default model binder can map these to the properties on the view model on postback.
You can customise the name of the editor template if you want, look a the UIHintAttribute class.

By default, when you want a collection, you need to make sure the names of the controls indicate they are from an array, etc. The default binder doesn't have this magic to my knowledge.

Related

ASP.NET MVC 3 WITH RAZOR : How to pass selected checkbox' ids in a Partial view to controller action?

I have a partialview [_SearchProduct] within the main view, let's say [product] view. The Partialview has a number of checkboxes segregated into different sections like search by company,search by product etc. with one [search] button.
A User can select multiple checkboxes. When user clicks [search] button I need to pass ids of all selected checkbox to controller action and re-render the page again considering the user's selection . Please guide me how to pass selected checkbox ids to my controller action.
My partial view is something like below:
<fieldset>
<legend>By Company</legend>
<table style="border-style: none;">
<tr>
#{
int i = 0;
foreach (var item in Model.CompanyName)
{
i = i + 1;
<td style="border-style: none;text-decoration:none;" >
#Html.CheckBox("chkCompany",new {id="chkCompany_" + Model.CompanyId.Tostring()}) #Model.CompanyName
</td>
if (i == 5)
{
#:</tr><tr>
i = 0;
}
}
}
</tr>
</table>
</fieldset>
<fieldset>
<legend>By Product</legend>
<table style="border-style: none;">
<tr>
#{
i = 0;
foreach (var item in Model.Product)
{
i = i + 1;
<td style="border-style: none;text-decoration:none;" >
#Html.CheckBox("chkProduct",new {id="chkProduct_" + Model.CompanyId.Tostring()}) #Model.ProductName
</td>
if (i == 10)
{
#:</tr><tr>
i = 0;
}
}
}
</tr>
</table>
</fieldset>
checkboxes are dynamic
Checkbox id represent the primarykey of respective table based on which i do filtering.
Please guide me>>
So it sounds like you have a structure containing names (of companies/products), and ids.
I would create a View Model structure that looked like
public class PartialViewModel //Make sure this is included in your main view model
{
public List<ObjectsToInclude> Companies { get; set; }
public List<ObjectsToInclude> Products { get; set; }
}
public class ObjectsToInclude //Give this a better name
{
public string Name { get; set; }
public string Id { get; set; }
public bool Include { get; set; }
}
Then in order to bind them you could do
for (int i =0; i<Model.Companies.Count(); i++)
{
<td style="border-style: none;text-decoration:none;" >
#Html.HiddenFor(m => m.Companies[i].Id)
#Html.CheckBoxFor(m => m.Companies[i].Include) #Model.Companies[i].Name
</td>
if (i == 5)
{
#:</tr><tr>
i = 0;
}
}
Then provided your post takes a parameter of PartialViewModel (or some MainViewModel where that contains an instance of PartialViewModel), you'll have lists of companies and products binded. You can loop through the list, and take the respective ids of anything checked to be included.
Edit: If you wanted a single comma separated array to be posted, it would be possible by by creating an onclick event for your checkboxes, and then setting a value of a hidden input every time a checkbox is clicked. But then your code would only work with JavaScript enabled. If you need a comma separated string, you can create it server side with the view model I suggested.
string companyIds = String.Join(",", model.Companies
.Where(company => company.Include)
.Select(company => company.Id));
http://dotnetnsqlcorner.blogspot.in/2012/09/multiple-checkboxes-in-mvc-3-and-post.html

MVC3 Some of my fields validate while some do not

Im sitting here scratching my head with a validation problem in ASP MVC3.
Somehow I'm able to validate the field Quantity, but the field OrderNumber does not validate. I can leave it empty and it still accepts it. I've tried to add other restrictions to it as well (such as max and min length) but same result - it accepts anything.
I also try changing 'TextBoxFor' to 'EditorFor' - but it's the same result.
Quantity on the other hand works as I want it. It requires you to enter an integer and it cannot be blank.
Hopefully some of you will be able to see what I'm doing wrong here :)
Here is my model:
public class Order
{
[Required(ErrorMessage="Insert Ordernumber (6-digits)")]
public string OrderNumber { get; set; }
[Required]
public string Partnumber { get; set; }
[Required]
public long Quantity { get; set; }
public Order()
{
}
}
And here is my view :
model POWeb.Models.AddModel
#using (Html.BeginForm("Add", "Home", FormMethod.Post))
{
//Create table
<table>
<tr>
<td>Select Partnumber to produce</td>
<td>#Html.DropDownListFor(model => model.SelectedPartNumber, Model.PartNumbers)</td>
</tr>
<tr>
<td>Enter PO number</td>
<td>#Html.TextBoxFor(model => model.OrderNumber)#Html.ValidationMessageFor(model => model.OrderNumber)</td>
</tr>
<tr>
<td>Quantity</td>
<td>#Html.TextBoxFor(model => model.Quantity)#Html.ValidationMessageFor(model => model.Quantity)</td>
</tr>
<tr>
<td colspan="2">
<button type="submit" name="SubmitButton">Add</button>
</td>
</tr>
</table>
}
You have the view of type POWeb.Models.AddModel, but you try to validate Order type. I'm pretty sure validation attributes on those types are not the same, so you get problems
Anders,
My 'guess' is that your ViewModel model POWeb.Models.AddModel isn't mirroring the [Required] attribute on OrderNumber. Can you add the definition of AddModel to your question for verification on that please as it's more than likely that the Order class differs.

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.

Why does my [HttpPost] method not fire?

I have created one page in MVC 3.0 Razor view.
Create.cshtml
#model LiveTest.Business.Models.QuestionsModel
#*<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>*#
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<table cellpadding="1" cellspacing="1" border="0">
<tr>
<td>#Html.LabelFor(model => model.TestID)
</td>
<td>
#Html.DropDownListFor(model => model.TestID, (IEnumerable<SelectListItem>)ViewBag.ItemIDList)#Html.ValidationMessageFor(model => model.TestID)
</td>
</tr>
<tr>
<td>#Html.LabelFor(model => model.Question)
</td>
<td>#Html.EditorFor(model => model.Question)#Html.ValidationMessageFor(model => model.Question)
#Html.HiddenFor(model => model.QuestionsID)
</td>
</tr>
<tr>
<td>#Html.LabelFor(model => model.IsRequired)
</td>
<td>#Html.CheckBoxFor(model => model.IsRequired)#Html.ValidationMessageFor(model => model.IsRequired)
</td>
</tr>
<tr>
<td>
</td>
<td>
<input type="submit" value="Submit" />
</td>
</tr>
</table>
}
QuestionsController.cs
public class QuestionsController : Controller
{
#region "Attributes"
private IQuestionsService _questionsService;
#endregion
#region "Constructors"
public QuestionsController()
: this(new QuestionsService())
{
}
public QuestionsController(IQuestionsService interviewTestsService)
{
_questionsService = interviewTestsService;
}
#endregion
#region "Action Methods"
public ActionResult Index()
{
return View();
}
public ActionResult Create()
{
InterviewTestsService _interviewService = new InterviewTestsService();
List<InterviewTestsModel> testlist = (List<InterviewTestsModel>)_interviewService.GetAll();
ViewBag.ItemIDList = testlist.Select(i => new SelectListItem() { Value = i.TestID.ToString(), Text = i.Name });
return View();
}
[HttpPost]
public ActionResult Create(QuestionsModel questions)
{
if (ModelState.IsValid)
{
_questionsService.Add(questions);
return RedirectToAction("Index");
}
InterviewTestsService _interviewService = new InterviewTestsService();
List<InterviewTestsModel> testlist = (List<InterviewTestsModel>)_interviewService.GetAll();
ViewBag.ItemIDList = testlist.Select(i => new SelectListItem() { Value = i.TestID.ToString(), Text = i.Name });
return View(questions);
}
#endregion
}
QuestionsModel.cs
public class QuestionsModel : IQuestionsModel
{
[ReadOnly(true)]
public Guid QuestionsID { get; set; }
[Required]
[DisplayName("Question")]
public string Question { get; set; }
[DisplayName("Test ID")]
public Guid TestID { get; set; }
[DisplayName("Is Required")]
public bool IsRequired { get; set; }
[DisplayName("Created By")]
public Guid CreatedBy { get; set; }
}
Problem:
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
If I am adding the above two lines in Create.cshtml page and then I press submit button then it will fire validation message "Question is required!" if I am entering value in *Question field and then press submit button my [HttpPost]Create Method never execute.*
If I remove the above two lines from page then press submit button then it will execute [HttpPost]Create Method and fire validation from server side if I am entering value in Question field then also [HttpPost]Create executed.
Please help me.
The QuestionsModel class includes a property CreatedBy which is not included in your View.
Try either adding CreatedBy as a hidden field, or (better yet) remove CreatedBy from the QuestionsModel class, since it is not an attribute which should be exposed in the view.
I suspect that this missing property is the cause of the problem.
UPDATE
I ran some tests on your code, and it was not the CreatedBy property. Rather, your problem is that you are not supplying a QuestionsID value, but you included a hidden field for QuestionsID on the form.
Because QuestionsID is a value type, by default, the DataAnnotationsModelValidatorProvider adds a Required validator to the QuestionsID field. Because the field did not have a ValidationMessage, you could not see the validation error.
You can override the behavior of the default DataAnnotationsModelValidatorProvider by following the instructions in my answer here.
I would check if any client side errors occurred when trying to submit the form. Check it from the browser console.
Also, make sure that you have completed your code with no validation errors before submitting the form.
Are you saying that the form doesn't validate client side and nothing ever get's POSTed back to your server?
Meaning, you click the submit button and nothing happens in the browser, correct?
The problem might be that your form isn't validating with the unobtrusive javascript library validation.

MVC3 - jQuery unobtrusive validation

I am new to MVC and need some guidance for the problem that I am running into.
I have a text field to contain Date using date picker. In the model, I didn't specify to validate the text field.
But I submit the form, the jQuery unobstrusive validates the date text field.
The model
Using System;
Using System. Collections. Generic;
Using System. ComponentModel;
Using System. ComponentModel. DataAnnotations;
Using System. Linq;
Using System. Web;
Using DataAnnotationsExtensions;
Namespace heavy_haulage_general_freight. Models
{
public class nsc_gf_job
{
[Key]
public int id { get; set; }
public DateTime pickup_date { get; set; }
}
}
The View
#model heavy_haulage_general_freight. Models. Nsc_gf_job
#using heavy_haulage_general_freight. Helpers
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html. BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Resources Division Heavy Haulage Details:</legend>
<table>
<tbody>
<tr>
<td>#Html.Label("Pickup Date")</td>
<td>
#Html.TextBox("pickup_date", "") <br />
#Html.ValidationMessage("pickup_date")
</td>
<tr>
</tbody>
</table>
</fieldset>
<p>
<input type="submit" value="Create" />
</p>
}
Try using a nullable date - ie
public DateTime? pickup_date { get; set; }
also note this applies to ints as well, as there is no 'empty' default for an int, you need a nullable value
That happens because you have defined the property of type DateTime in your model. Validation will always happen for this type because you cannot assign an invalid value to such field.

Resources