Adding a Number of Items to a Model list? - asp.net-mvc

I have a page with the same input box added a number of times.
#Html.TextBoxFor(m => m.Product)
#Html.TextBoxFor(m => m.Product)
#Html.TextBoxFor(m => m.Product)
How to I bind this to the Model.
I've tried:
public class Shop
{
public string ShopName { get; set; }
[Remote("ProductExists", "Account", AdditionalFields = "ShopName", ErrorMessage = "Product is already taken.")]
public List<String> Product { get; set; }
}
But I can only ever see the data in the first field. Also I tried:
#Html.TextBoxFor(m => m.Product[0])
#Html.TextBoxFor(m => m.Product[1])
#Html.TextBoxFor(m => m.Product[2])
But remote validation doesn't work so I'm a little stumped here. Essential what I would like to achieve is to send the list of products with the shop so that it can be validated via a remote call to a function. I tried putting the products within there own public class but then I wasn't able to access the shop name from within that class.
This is the Controller Action I'm trying to use:
public JsonResult ProductExists(List<String> Product, string ShopName)
Any Ideas how I could solve this would be so much appreciated?
EDIT
This Semi works but remote validation still isn't passing ShopName:
public class Shops
{
[Required]
public string ShopName { get; set; }
public List<Products> Product { get; set; }
}
public class Products
{
[Required]
[Remote("ProductExists", "Home", AdditionalFields = "ShopName", ErrorMessage = "Product is already taken.")]
public String Product { get; set; }
}
Controller Action:
public JsonResult ProductExists(List<String> Product, string ShopName)
{
return Json(true, JsonRequestBehavior.AllowGet);
}
View:
#model Shop.Models.Shops
#{
ViewBag.Title = "Shop";
}
<h2>Shop</h2>
<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)
<fieldset>
<legend>Shop</legend>
<div class="editor-label">
#Html.LabelFor(model => model.ShopName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ShopName)
#Html.ValidationMessageFor(model => model.ShopName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Product[0])
#Html.ValidationMessageFor(model => model.Product[0])
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Product[1])
#Html.ValidationMessageFor(model => model.Product[1])
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Product[2])
#Html.ValidationMessageFor(model => model.Product[2])
</div>
<input type="submit" value="Create" />
</fieldset>
}

look at the following answer. I would make product a class on its own like you tried. Loot at the rendered html code for the page and check out the field name for the ShopName textbox. I think it should be ShopName, if so you dont need to change the AdditionalFields attribute if not change it to the name rendered.
So something like this:
public class Shop
{
public string ShopName { get; set; }
public List<Products> Product { get; set; }
}
public class Products
{
[Remote("ProductExists", "Account", AdditionalFields = "ShopName", ErrorMessage = "Product is already taken.")]
public String Product { get; set; }
}
in your view do something like this:
foreach(var item in Model.products)
{
#Html.TextBoxFor(item => item.product) // not sure if the syntax is right
}

Related

Create record using Model List

I am trying to create an invoice. I have two models, Invoice & InvoiceItems.
I am able to insert using hardcoded values, but I want to be able to use TextBoxes to create an invoice on the fly. How do I insert a record that takes the data for the invoice and the dynamic data from the invoice items and inserts into both tables, using the same view? I'd like to have an add more button eventually where I can stay on the same page and keep adding items to the same invoice. You can see what I've tried so far below.
Invoice Model:
public class Invoice
{
[Key]
public int InvoiceId { get; set; }
public int ClientId { get; set; }
[Display(Name = "Amount")]
public decimal Amount { get; set; }
[Display(Name = "Invoice Creation Date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime CreationDate { get; set; }
[Display(Name = "Invoice Due Date")]
[DisplayFormat(ApplyFormatInEditMode = true, DataFormatString = "{0:MM/dd/yyyy}")]
public DateTime DueDate { get; set; }
[Display(Name = "Notes")]
public string InvoiceNotes { get; set; }
public List<InvoiceDetails> InvoiceDetails { get; set; }
public List<Clients> Clients { get; set; }
}
InvoiceItem Model:
public class InvoiceDetails
{
[Key]
public int InvoiceDetailsId { get; set; }
public int InvoiceId { get; set; }
[DisplayName("Item Name")]
public string Name { get; set; }
[DisplayName("Item Note")]
public string Note { get; set; }
[DisplayName("Qty")]
public decimal? Quantity { get; set; }
[DisplayName("Rate/Hour")]
public decimal? Price { get; set; }
[DisplayName("Item Total")]
public decimal? Total { get; set; }
}
Invoice Controller:
private NovaDb _db = new NovaDb();
public ActionResult InvoiceInformation()
{
var invoice = new Invoice();
invoice.InvoiceDetails = new List<InvoiceDetails>();
return View(invoice);
}
[HttpPost]
public ActionResult InvoiceInformation(Invoice model)
{
if (ModelState.IsValid)
{
var invoices = new Invoice()
{
Amount = model.Amount,
CreationDate = model.CreationDate,
DueDate = model.DueDate,
InvoiceNotes = model.InvoiceNotes,
InvoiceId = model.InvoiceId,
ClientId = model.ClientId
};
_db.Invoices.Add(invoices);
_db.SaveChanges();
return RedirectToAction("Index");
}
return View(model);
}
Invoice View:
#model NovaFinancial.Models.Invoice
#{
ViewBag.Title = "InvoiceInformation";
}
<h2>InvoiceInformation</h2>
#using (Html.BeginForm()) {
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<fieldset>
<legend>Invoice</legend>
#Html.HiddenFor(model => model.InvoiceId)
<div class="editor-label">
#Html.LabelFor(model => model.ClientId)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.ClientId)
#Html.ValidationMessageFor(model => model.ClientId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Amount)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Amount)
#Html.ValidationMessageFor(model => model.Amount)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.CreationDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CreationDate)
#Html.ValidationMessageFor(model => model.CreationDate)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.DueDate)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.DueDate)
#Html.ValidationMessageFor(model => model.DueDate)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.InvoiceNotes)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.InvoiceNotes)
#Html.ValidationMessageFor(model => model.InvoiceNotes)
</div>
<div>
<table>
<tr>
<th>Name</th>
<th>Notes</th>
<th>Qty</th>
<th>Price</th>
<th>Total</th>
</tr>
#for (int i = 0; i < Model.InvoiceDetails.Count; i++)
{
#Html.HiddenFor(m=>m.InvoiceDetails[i].Name)
#Html.HiddenFor(m=>m.InvoiceDetails[i].Note)
#Html.HiddenFor(m=>m.InvoiceDetails[i].Quantity)
#Html.HiddenFor(m=>m.InvoiceDetails[i].Price)
#Html.HiddenFor(m=>m.InvoiceDetails[i].Total)
<tr>
<td>#Html.DisplayFor(m=>m.InvoiceDetails[i].Name) | #Html.TextBoxFor(m=>m.InvoiceDetails[i].Name)</td>
<td>#Html.DisplayFor(m=>m.InvoiceDetails[i].Note) | #Html.TextBoxFor(m=>m.InvoiceDetails[i].Note)</td>
<td>#Html.DisplayFor(m=>m.InvoiceDetails[i].Quantity) | #Html.TextBoxFor(m=>m.InvoiceDetails[i].Quantity)</td>
<td>#Html.DisplayFor(m=>m.InvoiceDetails[i].Price) | #Html.TextBoxFor(m=>m.InvoiceDetails[i].Price)</td>
<td>#Html.DisplayFor(m=>m.InvoiceDetails[i].Total) | #Html.TextBoxFor(m=>m.InvoiceDetails[i].Total)</td>
</tr>
}
</table>
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
I worked out "a" solution that might not be the best, but it works. You'd still have to add to the code to make it more robust, but the general framework is there.
In your view, keep all the text boxes for the invoice details (Name, Quantity, Price) the same value for the name attribute, but do keep the id's unique. I used a bit of jQuery and JavaScript to generate extra rows as needed per a button that the user would click. For example,
<input type="text" name="Name" id="Name"> <!--first line item for Item Name-->
<input type="number" name="Quantity" id="Quantity"> <!--first for Quanitity -->
<input type="text" name="Name" id="Name2"> <!--second line item for Item Name-->
<input type="number" name="Quantity" id="Quantity2"> <!-- second for Quanitity -->
The values for the InvoiceDetail lines will pass back to the server as comma-delimited strings (better make certain that your item names don't have commas!). On the server-side,
var Names = Request["Name"]; // this would yield something like "Labor,Parts"
In the controller, you'll need to parse the strings into arrays and create the instances of your InvoiceDetail from them. I wrote a private method to split the strings and return a list of InvoiceDetail objects to the action method. The onus is on you to validate this data: both client-side and server-side need validation.
I did a few experiments. You can see all the code here: http://mefixme.blogspot.com/2014/10/aspnet-mvc-how-to-add-model-with.html
I hope that this helps you.

Multi Table in a view

I'm trying to make a strongly typed "Create View" so I could insert data into two tables simultaneously. However, when I create the view, the fields from my table are not included in my model. Can you tell what is the right way of doing this?
My Table Model:
public partial class CanaClie0012
{
public string Client00130012 { get; set; }
public string F1Pais00200012 { get; set; }
public string F1Cana02530012 { get; set; }
public string Direcc0012 { get; set; }
public Nullable<System.DateTime> TmStmp0012 { get; set; }
}
public partial class Clientes0013
{
public string Client0013 { get; set; }
public string Nombre0013 { get; set; }
public string F1Pais00200013 { get; set; }
}
My Custom Model to combine the two table is:
public class ClientModel
{
public IQueryable<CanaClie0012> CanaClie0012 { get; set; }
public IQueryable<Clientes0013> Clientes0013 { get; set; }
}
My Controller: (Not really sure if im this correctly)
[HttpPost]
public ActionResult ClientCreate(CanaClie0012 canaclie0012, Clientes0013 clientes0013)
{
ClientModel vm = new ClientModel();
if (ModelState.IsValid)
{
db.CanaClie0012.Add(canaclie0012);
db.SaveChanges();
db.Clientes0013.Add(clientes0013);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(vm);
}
My view:
#model MvcApplication1.Models.ClientModel
#{
ViewBag.Title = "ClientCreate";
}
<h2>ClientCreate</h2>
<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)
<fieldset>
<legend>ClientModel</legend>
<div class="editor-label">
Client Name
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Client00130012)
#Html.ValidationMessageFor(model => model.Client00130012)
</div>
<div class="editor-label">
Pais
</div>
<div class="editor-field">
#Html.EditorFor(model => model.F1Pais00200012)
#Html.ValidationMessageFor(model => model.F1Pais00200012)
</div>
<div class="editor-label">
Address
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Direcc0012)
#Html.ValidationMessageFor(model => model.Direcc0012)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Nombre0013)
#Html.ValidationMessageFor(model => model.Nombre0013)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
One big problem is that your view is strongly-typed to your ClientModel class. This class does not include the members that you are referencing in your view. Instead, that class includes lists of objects that include those members. At this point, your view probably doesn't even compile.
Try changing your ClientModel class as follows:
public class ClientModel
{
public CanaClie0012 CanaClie0012 { get; set; }
public Clientes0013 Clientes0013 { get; set; }
}
then your view would reference the members such as:
<div class="editor-label">
Client Name
</div>
<div class="editor-field">
#Html.EditorFor(model => model.CanaClie0012.Client00130012)
#Html.ValidationMessageFor(model => model.CanaClie0012.Client00130012)
</div>
similarly for members of your Clientes0013 members.
One additional thing: you don't need to call db.SaveChanges(); twice in your controller. Instead, insert into both tables and do a single save:
db.CanaClie0012.Add(canaclie0012);
db.Clientes0013.Add(clientes0013);
db.SaveChanges();

Validations not show up using EF Code First with complex types

This is a continuation of this question Model class and Mapping
I had my Client class now working fine and it's defined as
using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using DataAnnotationsExtensions;
using System.ComponentModel.DataAnnotations.Schema;
using System.Collections.Generic;
namespace CardNumbers.Objects
{
[ComplexType]
public class PhoneInfo
{
[DataType(DataType.PhoneNumber)]
[StringLength(10)]
[DisplayName("Phone")]
public virtual string Phone { get; set; }
[StringLength(5)]
[DisplayName("Ext")]
public virtual string Ext { get; set; }
public bool HasValue
{
get
{
return (Phone != null || Ext != null);
}
}
}
[ComplexType]
public class ContactDetail
{
//Constructor
public ContactDetail()
{
phoneInfo = new PhoneInfo();
}
[StringLength(100)]
[DisplayName("Contact Name")]
[DisplayFormat(NullDisplayText = "")]
public virtual string Contact { get; set; }
[Email]
[StringLength(100)]
[DisplayName("Email")]
public virtual string Email { get; set; }
public virtual PhoneInfo phoneInfo { get; set; }
public bool HasValue
{
get
{
return (Contact != null || Email != null || phoneInfo.HasValue);
}
}
}
/// <summary>
/// Client class (Client No, Client Name, Address, Contact1, Contact2 info, Created By, Modified By (operator and date)
/// </summary>
public class Client
{
public Client()
{
Contact1 = new ContactDetail();
Contact2 = new ContactDetail();
}
[Key]
[Column("ClientId",TypeName = "int")]
public virtual int Id { get; set; }
[Required]
[DisplayName("Client No")]
[Column("client_no", TypeName = "smallint")]
public virtual Int16 Number { get; set; }
[Required]
[Column("client_name", TypeName = "varchar")]
[DisplayName("Client Name")]
[MaxLength(30, ErrorMessage = "Client Name should not be longer than 30 characters" )]
[MinLength(3, ErrorMessage = "Client Name is too short")]
public virtual string Name { get; set; }
[DataType(DataType.MultilineText)]
public virtual string Address { get; set; }
public virtual ContactDetail Contact1 {get; set;}
public virtual ContactDetail Contact2 {get; set;}
[ForeignKey("EnteredByOperator")]
public string EnteredBy { get; set; }
[InverseProperty("ClientsEnteredBy")]
public virtual Operator EnteredByOperator { get; set; }
[ForeignKey("ModifiedByOperator")]
public string ModifiedBy { get; set; }
[InverseProperty("ClientsUpdatedBy")]
public virtual Operator ModifiedByOperator { get; set; }
[DataType(DataType.DateTime)]
[DisplayName("Created on")]
public DateTime EnteredOn { get; set; }
[DataType(DataType.DateTime)]
[DisplayName("Modified on")]
public DateTime? ModifiedOn { get; set; }
public virtual ICollection<ClientOrder> ClientOrders { get; set; }
public virtual ICollection<Reorder> Reorders { get; set; }
}
}
I mapped column names using Fluent API and I also re-defined my original "repository" classes to be very similar to defined in this tutorial http://code.msdn.microsoft.com/ASPNET-MVC-Application-b01a9fe8
This is my current partial view for the client form called _ClientForm:
#using WebDemo.Helper
#model CardNumbers.Objects.Client
<fieldset>
<legend>Client Info</legend>
#Html.ValidationSummary(true)
<input type="hidden" id="fntype" name="fntype">
#Html.HiddenFor(model => model.Id)
#Html.EditorFor(model => model.Number, EditorTemplate.TextBox)
#Html.EditorFor(model => model.Name, EditorTemplate.TextBox)
#Html.EditorFor(model => model.Address, EditorTemplate.EditBox)
<div id="ContactsInfo">
#*Contact 1*#
<div id="Contact1">
#*#Html.EditorFor(model=>model.Contact1)*#
#Html.EditorFor(model=>model.Contact1.Contact, EditorTemplate.TextBox)
#Html.EditorFor(model=>model.Contact1.Email, EditorTemplate.TextBox)
</div>
#*Contact2*#
<div id="Contact2">
#* #Html.EditorFor(model => model.Contact2)*#
</div>
</div>
#*<div class="clear"></div>*#
<div id="SaveCancel" class="float-right">
<button type="Submit" id="btnSave">Save</button>
<button type="reset" id="btnCancel">Cancel</button>
</div>
</fieldset>
I already tried to revert to original way of only one level and I also commented the second Contact2 info but still the e-mail validation doesn't work and all other validations also don't seem to work.
The EditorFor textboxes are defined based on this blog post http://fusionovation.com/post/2010/02/15/adding-a-rich-text-editor-to-asp-net-mvc-using-strongly-typed-helpers-dataannotations-amp-jquery.aspx
And these are two of the new EditorFor I added:
PhoneInfo.cshtml
#using WebDemo.Helper
#model CardNumbers.Objects.PhoneInfo
<div id="PhoneInfo">
<div class="float-left">
#Html.EditorFor(model => model.Phone, EditorTemplate.TextBox)
</div>
<div class="float-right">
#Html.EditorFor(model => model.Ext, EditorTemplate.TextBox)
</div>
</div>
And ContactDetail.cshtml
#using WebDemo.Helper
#model CardNumbers.Objects.ContactDetail
#Html.EditorFor(model => model.Contact, EditorTemplate.TextBox)
#Html.EditorFor(model => model.Email, EditorTemplate.TextBox)
#Html.EditorFor(model=>model.phoneInfo)
So, as you can see, the code of the views is now very compact.
However, with all of these in place the validations don't see to fire anymore. I used to test validation on EMail by typing some garbage. It used to provide a validation message near the textbox. Now I observe that the email textbox takes the red border, but there is no message.
Do you see what I am missing now and if it's possible to use complex type and validations?
To clarify, the _ClientForm is called from this Client view:
#model CardNumbers.Objects.Client
#{
ViewBag.Title = "Client";
}
#section scripts {
<script src="#Url.Content("~/Scripts/Clients.js")" type="text/javascript" ></script>
}
<form id="frmClientsSearch">
<label for="clientNo">Client No: </label>
<input type="number" name="searchClientNo" class="numericOnly" /><br />
<label for="clientName">Client Name: </label>
<input type = "text" size =25 value ="Please enter the search value" class="SelectOnEntry"
name ="searchClientName" />
<input type="button" id="btnClientsSearch" value ="Find / Refresh" />
</form>
<div style="padding-left: 150px; padding-top: 50px; padding-bottom: 50px;" id="ClientsResults">
<table id="flexClients" style="display: none">
</table>
</div>
<div style="display: none">
<form id="sform" title="Client Info">
#{Html.RenderPartial("_ClientForm", Model) ;}
</form>
</div>
Thanks.
I don't see a form anywhere on your page. A form context is required for validation to work. You need to wrap the Editor attributes in BeginForm block.
After some trials and error I found that the TextBox EditorFor view was the culprit. I documented what I found in my answer here http://forums.asp.net/t/1855963.aspx/1?Validation+messages+don+t+show+up+what+is+missing+
Basically, as long as I use this EditorFor
#*#using WebDemo.Helper*#
#model CardNumbers.Objects.PhoneInfo
<div id="PhoneInfo">
<div class="float-left">
#* #Html.EditorFor(model => model.Phone, EditorTemplate.TextBox)*#
<div class="editor-label">
#Html.LabelFor(model => model.Phone)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Phone)
#Html.ValidationMessageFor(model => model.Phone)
</div>
</div>
<div class="float-right">
#*#Html.EditorFor(model => model.Ext, EditorTemplate.TextBox)*#
<div class="editor-label">
#Html.LabelFor(model => model.Ext)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Ext)
#Html.ValidationMessageFor(model => model.Ext)
</div>
</div>
</div>
All seems to work OK. But if I try to switch to a shorter syntax and use this EditorFor for the textbox:
<div class="editor-label">
#Html.Label((ViewData.ModelMetadata.DisplayName??ViewData.ModelMetadata.PropertyName),
new Dictionary<string, object>
{
{ "for", ViewData.ModelMetadata.PropertyName }
})
</div>
<div class="editor-field">
#Html.TextBox("", (object)Model,
new Dictionary<string, object>
{
{ "id", ViewData.ModelMetadata.PropertyName },
{ "name", ViewData.ModelMetadata.PropertyName },
{ "class", "text-box single-line"},
{ "data-bind", "value: " + ViewData.ModelMetadata.PropertyName },
})
#Html.ValidationMessage(ViewData.ModelMetadata.PropertyName,
new Dictionary<string, object>
{
{ "data-valmsg-for", ViewData.ModelMetadata.PropertyName }
})
</div>
Validation messages do not show anymore.
Hopefully this answer will help someone or you may see what I am missing here.

ASP.NET MVC 3 Binding to a Collection inside an Object

I have a model with an object that contains a collection like this:
namespace API.Example.Models
{
public class OrderTest
{
public string UserName { get; set; }
public string Token { get; set; }
public POCO.Order Order { get; set; }
}
}
namespace Supertext.API.POCO
{
public class Order
{
public List<TranslationGroup> Groups = new List<TranslationGroup>();
}
public class TranslationGroup
{
public string GroupId { get; set; }
}
}
The Order object contains a collection called Groups.
In the view I display the collection like this (with the index like explained in several examples)
#Html.LabelFor(m => m.UserName)
#Html.TextBoxFor(m => m.UserName)
#for (int i = 0; i < Model.Order.Groups.Count; i++)
{
#Html.LabelFor(m => Model.Order.Groups[i].GroupId)
#Html.TextBoxFor(m => Model.Order.Groups[i].GroupId)
}
And this is the Controller method that gets called:
[HttpPost]
public ActionResult Index(Models.OrderTest model)
The HTML of the UserName element:
<input id="UserName" name="UserName" style="width:300px;" type="text" value="">
and the GroupId element:
<input id="Order_Groups_0__GroupId" name="Order.Groups[0].GroupId" type="text" value="1">
I can access the UserName, but there is nothing in the collection.
What am I missing?
And whats the difference between using m.UserName and Model.Order.Groups (I mean m and Model). Is that my issue?
Each property of POCO entity use like a property managed by the CLR. Let the CLR manage the create of instance and etc.. or you can generate conflicts that can throw issues like you have.
Change your Order code to this:
public class Order
{
public List<TranslationGroup> Groups { get; set; }
}
--EDIT
I create a new project and add the following class:
public class TranslationGroup
{
public string GroupId { get; set; }
}
public class Order
{
public List<TranslationGroup> Groups { get; set; }
}
public class OrderTest
{
public string UserName { get; set; }
public string Token { get; set; }
public Order Order { get; set; }
}
Here my code behind of the OrderTestController:
public ActionResult Index()
{
var orderTest = new Models.OrderTest()
{
UserName = "Vinicius",
Token = "12as1da58sd558",
Order = new Models.Order()
{
Groups = new List<Models.TranslationGroup>()
{
new Models.TranslationGroup() { GroupId = "a54s"},
new Models.TranslationGroup() { GroupId = "a87d"},
new Models.TranslationGroup() { GroupId = "2gf4"}
}
}
};
return View(orderTest);
}
[HttpPost]
public ActionResult Index(Models.OrderTest model)
{
return View();
}
And Index View:
#model TestCollection.Models.OrderTest
#{
ViewBag.Title = "Index";
}
<h2>
Index</h2>
<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)
<fieldset>
<legend>OrderTest</legend>
<div class="editor-label">
#Html.LabelFor(model => model.UserName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.UserName)
#Html.ValidationMessageFor(model => model.UserName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.Token)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Token)
#Html.ValidationMessageFor(model => model.Token)
</div>
#for (int i = 0; i < Model.Order.Groups.Count; i++)
{
<div class="editor-label">
#Html.LabelFor(m => Model.Order.Groups[i].GroupId)
</div>
<div class="editor-field">
#Html.TextBoxFor(m => Model.Order.Groups[i].GroupId)
</div>
}
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
So, if you run, and go to OrderTest view, you 'll see all attributes filled, and when you click in create, all things will be binded (the collection as well).

Why is my ViewModel empty on [HttpPost]? .NET MVC 3

I'm trying my hardest to use ViewModels correctly in my web application, but I'm running into various problems. One of which, is if I set a breakpoint just after I post using a Create action, my viewModel hasn't stored any of my form values. I must be doing something wrong, but I've tried a few things. Including the code below, where I name the form items the same as the viewModel fields to see if that helps.
I'm also wondering what exactly properties in your viewmodel should represent. I've seen people use different things in blog posts and whatnot.
If the view is going to render a select list, I'm under the impression the viewmodel should hold an IEnumerable SelectListItem for this as below. Yet I've seen people use IEnumerable Entity instead, to represent the type the select list represents.
Can anybody shed some light on this for me? I scrapped my entire business logic last night so I could start a fresh and try and do it correctly.
My ViewModel:
public class ServerCreateViewModel
{
public int Id { get; set; }
// CompanyName represents a field in the Company model. I did this to see if
// it would help with model binding. Beforehand it was Companies to represent the type. I've done the same for the rest of them, so I wont comment on this again.
public IEnumerable<SelectListItem> CompanyName { get; set; }
// Represents the Game model.
public IEnumerable<SelectListItem> GameTitle { get; set; }
//Represents the Location model, etc...
public IEnumerable<SelectListItem> City { get; set; }
public IEnumerable<SelectListItem> NumberOfPlayers { get; set; }
public IEnumerable<SelectListItem> CurrencyAbbreviation { get; set; }
}
My Controller action:
public ActionResult Create()
{
var viewModel = new ServerCreateViewModel();
viewModel.CompanyName = new SelectList(_dataService.Companies.All(), "Id", "CompanyName");
viewModel.GameTitle = new SelectList(_dataService.Games.All(), "Id", "GameTitle");
viewModel.City = new SelectList(_dataService.Locations.All(), "Id", "City");
viewModel.NumberOfPlayers = new SelectList(_dataService.ServerPlayers.All(), "Id", "NumberOfPlayers");
return View(viewModel);
}
[HttpPost]
public ActionResult Create(FormCollection collection, ServerCreateViewModel viewModel)
{
try
{ // I put a breakpoint in here to check the viewModel values.
// If I dont pass the viewModel into the constructor, it doesnt exist.
// When I do pass it in, its empty.
return Content("Success");
}
catch
{
return Content("Fail");
}
}
My View:
#model GameserverCompare.ViewModels.Server.ServerCreateViewModel
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Server</legend>
#Html.HiddenFor(m => m.Id)
<div class="editor-label">
#Html.LabelFor(model => model.CompanyName)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => Model.CompanyName, Model.CompanyName)
#Html.ValidationMessageFor(model => model.CompanyName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.GameTitle)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => Model.GameTitle, Model.GameTitle)
#Html.ValidationMessageFor(model => model.GameTitle)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.City)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => Model.City, Model.City)
#Html.ValidationMessageFor(model => model.City)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.NumberOfPlayers)
</div>
<div class="editor-field">
#Html.DropDownListFor(m => Model.NumberOfPlayers, Model.NumberOfPlayers)
#Html.ValidationMessageFor(model => model.NumberOfPlayers)
</div>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
}
Since you're using SelectList properties in the form model, you will need to have a different model to represent the selected values in those lists:
public class ServerCreatePostbackModel
{
public int Id { get; set; }
// CompanyName represents a field in the Company model.
public string CompanyName { get; set; }
// Represents the Game model.
public string GameTitle { get; set; }
//Represents the Location model, etc...
public string City { get; set; }
public int NumberOfPlayers { get; set; }
public string CurrencyAbbreviation { get; set; }
}
Have your HttpPost action take one of these as its argument.
Oh, and be sure to use HiddenFor for the Id property, so it gets sent back with the other data.

Resources