asp.net mvc5 validating list of objects - asp.net-mvc

I have a view for entering delivery quantities on every items in the list, and sends a list of objects back to controller. I would like to validate all textboxes on the page, and I have added annotations in the model.
The problem is, I can only validate the first row (also in the output html only the first row has validation markups). Since there are generated validations on the first row, so I don't think it's about the model. Is there a way to have generated validations in all rows? If not, what are the workarounds?
#{int i = 0;}
#foreach (var item in Model)
{
<tr>
<td>
#Html.HiddenFor(modelItem => item.eo, new { Name = "[" + i + "].eo" })
#Html.DisplayFor(modelItem => item.barcode)
#Html.Hidden("[" + i + "].barcode", Model[i].barcode)
</td>
<td>
#Html.DisplayFor(modelItem => item.itemno)
</td>
<td>
#Html.DisplayFor(modelItem => item.cdesc)
</td>
<td>
#Html.DisplayFor(modelItem => item.acost)
</td>
<td>
#Html.DisplayFor(modelItem => item.qty)
</td>
<td>
#Html.EditorFor(modelItem => item.dqty, new { htmlAttributes = new { Name = "[" + i + "].dqty", id = "[" + i + "].dqty", #class = "form-control" } })
#Html.ValidationMessage("[" + i + "].dqty", "", new { #class = "text-danger" })
</td>
</tr>
i++;
}
This is the generated html for the textbox in the first row.
<input Name="[0].dqty" class="form-control text-box single-line" data-val="true" data-val-number="The field 出貨數量 must be a number." data-val-required="必須填上出貨數量" id="[0].dqty" name="item.dqty" type="text" value="10" />
<span class="field-validation-valid text-danger" data-valmsg-for="[0].dqty" data-valmsg-replace="true"></span>
And the second row onwards...
<input Name="[1].dqty" class="form-control text-box single-line" id="[1].dqty" name="item.dqty" type="text" value="7" />
<span class="field-validation-valid text-danger" data-valmsg-for="[1].dqty" data-valmsg-replace="true"></span>
The Model
[MetadataType(typeof(EorderDetailsMetaData))]
public partial class EorderDetails
{
public string eo { get; set; }
public string barcode { get; set; }
public string itemno { get; set; }
public string cdesc { get; set; }
public Nullable<decimal> qty { get; set; }
public Nullable<decimal> dqty { get; set; }
public Nullable<decimal> acost { get; set; }
public string sdate { get; set; }
public string edate { get; set; }
public string shop { get; set; }
public string sname { get; set; }
public string saddr { get; set; }
public string shoptel { get; set; }
public string shopfax { get; set; }
}
public class EorderDetailsMetaData
{
[Display(Name = "訂單編號")]
public string eo { get; set; }
[Display(Name = "電腦條碼")]
public string barcode { get; set; }
[Display(Name = "貨品編號")]
public string itemno { get; set; }
[Display(Name = "貨品名稱")]
public string cdesc { get; set; }
[Display(Name = "訂購數量")]
[DisplayFormat(DataFormatString = "{0:n0}", ApplyFormatInEditMode = true)]
public Nullable<decimal> qty { get; set; }
[Display(Name = "出貨數量")]
[DisplayFormat(DataFormatString = "{0:n0}", ApplyFormatInEditMode = true)]
[Required(ErrorMessage = "必須填上出貨數量")]
public Nullable<decimal> dqty { get; set; }
[Display(Name = "成本價")]
[DisplayFormat(DataFormatString = "{0:0.##}", ApplyFormatInEditMode = true)]
public Nullable<decimal> acost { get; set; }
public string sdate { get; set; }
public string edate { get; set; }
public string shop { get; set; }
public string sname { get; set; }
public string saddr { get; set; }
public string shoptel { get; set; }
public string shopfax { get; set; }
}

You should be generating the collection in a for loop and let the helpers generate the correct html for you. If you inspect the html you have posted in the second snippet you will see the issue (two name attributes!)
#model IList<EorderDetails>
#using(Html.BeginForm())
{
for(int i = 0; i < Model.Count; i++)
{
#Html.HiddenFor(m => m[i].eo)
#Html.DisplayFor(m => m[i].barcode)
....
#Html.EditorFor(m => m[i].dqty, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(m => m[i].dqty, new { #class = "text-danger" })
}
<input type="submit" />
}
Alternatively you can create a custom EditorTemplate for your model
/Views/Shared/EditorTemplates/EorderDetails.cshtml
#model EorderDetails
#Html.HiddenFor(m => m.eo)
#Html.DisplayFor(m => m.barcode)
....
and in the main view
#model IList<EorderDetails>
#using(Html.BeginForm())
{
#Html.EditorFor(m => m)
<input type="submit" />
}

To omit this strange behavior, there should:
Define the property as Array, instead of ICollection or IList.
Should use for in cshtml, instead of forEach.
Have no idea why this will cause the difference. But I think it should not. And I think this is a bug should be fixed.

Related

MVC Net 6 Get Object from SelectList using its id

Hello i am triying to figure out how to get the object after is selected in the selectlist, the selectlist holds the "Id" field and "Code" field, but i want to get access to the other fields of the object after is selected. I would like to show the "Amount" field and the "Coin.Name" of the object in the view after the selecction.
Order Model
public class Order
{
[Required]
[Key]
public int Id { get; set; }
[ForeignKey("Id")]
[Display(Name = "Proveedor")]
public int ProviderId { get; set; }
[Display(Name = "Proveedor")]
public virtual Provider Provider { get; set; } = null!;
[ForeignKey("Id")]
[Display(Name = "Pais")]
public int CountryId { get; set; }
[Display(Name = "Pais")]
public virtual Country Country { get; set; } = null!;
[ForeignKey("Id")]
[Display(Name = "Categoria")]
public int CategoryId { get; set; }
[Display(Name = "Categoria")]
public virtual Category Category { get; set; } = null!;
[Required]
[StringLength(100)]
[Display(Name = "Coigo de Orden")]
public string Code { get; set; } = null!;
[Required]
[Display(Name = "Moneda")]
public int CoinId { get; set; }
[Display(Name = "Moneda")]
public virtual Coin Coin { get; set; } = null!;
[Required]
[Display(Name = "Monto")]
[Precision(18, 2)]
public decimal Amount { get; set; }
[Required]
[DisplayFormat(DataFormatString = "{0:dd-MM-yyyy}", ApplyFormatInEditMode = true)]
[Display(Name = "Fecha")]
public DateTime Date { get; set; }
[Required]
[DisplayFormat(DataFormatString = "{0:dd-MM-yyyy}", ApplyFormatInEditMode = true)]
[Display(Name = "Fecha Tope")]
public DateTime DateEnd { get; set; }
[ForeignKey("Id")]
[Display(Name = "Comprador")]
public int BuyerId { get; set; }
[Display(Name = "Comprador")]
public virtual Buyer Buyer { get; set; } = null!;
[StringLength(500)]
[Display(Name = "Comentarios")]
public string Comments { get; set; }
[StringLength(500)]
[Display(Name = "Campo 1")]
public string Field1 { get; set; }
[StringLength(500)]
[Display(Name = "Campo 2")]
public string Field2 { get; set; }
[StringLength(500)]
[Display(Name = "Campo 3")]
public string Field3 { get; set; }
[StringLength(500)]
[Display(Name = "Campo 4")]
public string Field4 { get; set; }
[ForeignKey("Id")]
public int AuditUserId { get; set; }
public virtual User AuditUser { get; set; } = null!;
public DateTime AuditDateTime { get; set; }
public bool AuditDelete { get; set; }
}
Coin Model
public class Coin
{
[Required]
[Key]
public int Id { get; set; }
[Required]
[StringLength(100)]
[Display(Name = "Nombre")]
public string Name { get; set; }
[ForeignKey("Id")]
public int AuditUserId { get; set; }
public virtual User AuditUser { get; set; } = null!;
[Required]
public DateTime AuditDateTime { get; set; }
[Required]
public bool AuditDelete { get; set; }
}
Create Controller
public async Task<IActionResult> Create(int idPayment)
{
ViewData["id"] = idPayment;
ViewData["OrderId"] = new SelectList(_context.Orders.Include(o => o.Coin).Where(x => x.AuditDelete == false).OrderBy(x => x.Code), "Id", "Code");
ViewData["PaymentStatusId"] = new SelectList(_context.PaymentsStatus.Where(x => x.AuditDelete == false).OrderBy(x => x.Status), "Id", "Status");
return View();
}
Create View
#model WebApplicationDailyPayments.Models.Database.PaymentDetails
#{
ViewData["Title"] = "Crear";
}
<h1>Crear</h1>
<h4>Detalle de pagos</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="PaymentId" class="control-label"></label>
<select asp-for="PaymentId" class ="form-control" asp-items="ViewBag.PaymentId"></select>
</div>
<div class="form-group">
<label asp-for="OrderId" class="control-label"></label>
<select asp-for="OrderId" class ="form-control" asp-items="ViewBag.OrderId"></select>
</div>
<div class="form-group">
<label asp-for="PaymentStatusId" class="control-label"></label>
<select asp-for="PaymentStatusId" class ="form-control" asp-items="ViewBag.PaymentStatusId"></select>
</div>
<div class="form-group">
<label asp-for="AmountPaid" class="control-label"></label>
<input asp-for="AmountPaid" class="form-control" id="AmountPaid" />
<span asp-validation-for="AmountPaid" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Rate" class="control-label"></label>
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" id="rateChecked" checked="">
<label class="form-check-label" for="flexSwitchCheckChecked">Multiplicar - Dividir</label>
</div>
<input asp-for="Rate" class="form-control" id="Rate"/>
<span asp-validation-for="Rate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AmountPaidFinal" class="control-label"></label>
<input asp-for="AmountPaidFinal" class="form-control" id="AmountPaidFinal" readonly />
<span asp-validation-for="AmountPaidFinal" class="text-danger"></span>
</div>
<br/>
<div class="form-group">
<input type="submit" value="Crear" class="btn btn-primary" /> <a class="btn btn-primary" asp-action="Index" asp-route-idPayment="#ViewData["id"]">Regresar a la Lista</a>
</div>
</form>
</div>
</div>
#section Scripts {
#{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
<script>
$(function(){
$("#AmountPaid,#Rate").keyup(function (e) {
var q=$("#AmountPaid").val().toString().replace(",",".");
var p = $("#Rate").val().toString().replace(",", ".");
var c = document.getElementById('rateChecked');
var result=0;
if(q!=="" && p!=="" && $.isNumeric(q) && $.isNumeric(p))
{
if(c.checked)
{
result = parseFloat(q) * parseFloat(p);
}
else
{
result = parseFloat(q) / parseFloat(p);
}
}
$("#AmountPaidFinal").val((Math.round(result * 100) / 100).toString().replace(".", ","));
});
});
</script>
}
Edit 1
I added in the controller to pass the Orders to the view
ViewData["Orders"] = _context.Orders.Include(o => o.Coin).Where(x => x.AuditDelete == false).ToList();
I added in the view to get the orders
#{
foreach (var item in (IEnumerable<WebApplicationDailyPayments.Models.Database.Order>)(ViewData["Orders"]))
{
var a = item.Id;
}
}
Now i get the Orders in the view, now i need to filter by Id selecetd in the selectlsit
Thank you
You can monitor select changes to perform corresponding operations.
Below is my test code, you can refer to it.
In the view, I use JavaScript to monitor whether the select changes, so as to obtain the selected Id for matching:
<div class="form-group">
<label asp-for="OrderId" class="control-label"></label>
<select id="my_select" asp-for="OrderId" class="form-control" asp-items="#ViewBag.OrderId"></select>
</div>
<script>
$("#my_select").change(function () {
var id = $(this).children(":selected").attr("value");
var array = #Html.Raw(Json.Serialize(ViewData["Orders"]));
for (var i = 0; i < array.length; i++) {
if(array[i].id == parseInt(id))
{
console.log("Coin.Name:"+array[i].coin.name);
console.log("Amount:" + array[i].amount);
}
}
});
</script>
Test Result:
Is this what you want?

Validation on selecting text and dropdown. if one is used, both musts be used

There is a textbox and a dropdown list box. If textbox is selected then shows a validation for dropdown which means dropdown should also be selected and If dropdown is selected then shows a validation for textbox which means textbox should also be selected If none is selected then do not show any validation.
And I want the condition on Model class in mvc.
<table class="simple">
<thead>
<tr>
<th colspan="2">Heading </th>
</tr>
</thead>
<tbody>
<tr>
<td>
#Html.TextBoxFor(model.prop2,new
{#class = "form- control font-9 p-1" })
</td>
<td>
#(Html.Kendo().DropDownListFor(m =>
m.prop1))
.DataTextField("Type")
.DataValueField("Id")
.OptionLabel(PleaseSelect)
.HtmlAttributes(new { #class = "form-control" }))
</td>
</tr>
<tr>
<td>
#Html.TextBoxFor(model.prop4,new
{#class = "form- control font-9 p-1" })
</td>
<td>
#(Html.Kendo().DropDownListFor(m =>
m.prop3))
.DataTextField("Type")
.DataValueField("Id")
.OptionLabel(PleaseSelect)
.HtmlAttributes(new { #class = "form-control" }))
</td>
</tr>
</tbody>
</table>
Model Class is -
public class ViewModel
{
public int? prop1 { get; set; }
public decimal? prop2 { get; set; }
public int? prop3 { get; set; }
public decimal? prop4 { get; set; }
}
Create a new class
public class Custom : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
//Get your model and do magic!
var model = (yourmodel)validationContext.ObjectInstance;
//Your condtions
if ((model.prop1== null && model.prop2 == null) || (model.prop1!= null && model.prop2 != null))
{
return ValidationResult.Success;
}
else
{
return new ValidationResult("You must select both of them");
}
}
}
now add your custom Annotation
public class RefractionFinalViewModel
{
[custom]
[Display(Name = "Select type")]
public int? prop1 { get; set; }
public decimal? prop2 { get; set; }
public int? prop3 { get; set; }
public decimal? prop4 { get; set; }
}
View
#Html.LabelFor(m => m.prop3 )
#Html.DropDownListFor(m => m.prop3 , new SelectList(your items, "Id", "type"), "", new { #class = "form-control" })
#Html.ValidationMessageFor(m => m.prop3 )
Or you can use Foolproof package
[RequiredIf("prop2!= null", ErrorMessage = "prop1")]
public int? prop1{ get; set; }
[RequiredIf("prop1> 0", ErrorMessage = "prop2")]
public decimal? prop2{ get; set; }
[RequiredIf("prop4!= null", ErrorMessage = "prop3")]
public int? prop3{ get; set; }
[RequiredIf("prop3> 0", ErrorMessage = "prop4")]
public decimal? prop4{ get; set; }
Simply I applied the validation on Model Class.
[RequiredIf("prop2!= null", ErrorMessage = "prop1 required")]
public int? prop1{ get; set; }
[RequiredIf("prop1> 0", ErrorMessage = "prop2 required")]
public decimal? prop2{ get; set; }
[RequiredIf("prop4!= null", ErrorMessage = "prop3 required")]
public int? prop3{ get; set; }
[RequiredIf("prop3> 0", ErrorMessage = "prop4 required")]
public decimal? prop4{ get; set; }

insert multiple row in a single form using ASP.NET MVC

here my question is that where user enters area details like place-name, noofplaces, like that after creating user details here user entered noofplaces, so how many enters(10,20 or even 100) that many rows should be created.
here is the code worked for me
Area Model
`public class Area
{
[Key]
public int AreaID { get; set; }
public string ParkingPlaceName { get; set; }
public int NoOfParkingPlaces { get; set; }
[DataType(DataType.Currency)]
public decimal MinimumCharges { get; set; }
public string Address { get; set; }
[Precision(25,22)]
public decimal Latitude { get; set; }
[Precision(25,22)]
public decimal Longitude { get; set; }
public virtual ICollection<Car> Cars { get; set; }
public virtual ICollection<ParkingPlace> ParkingPlaces { get; set; }
}
}`
ParkingPlace Model
public class ParkingPlace
{
[Key]
public int ParkID { get; set; }
public string PlaceId { get; set; }
public int AreaID { get; set; }
public virtual ICollection<Car> Cars { get; set; }
public virtual Area Areas { get; set; }
}
controller code
public ActionResult Create(string areaName)
{
Area area = db.Areas.SingleOrDefault(x => x.ParkingPlaceName == areaName);
return View(area);
}
[HttpPost]
public ActionResult Create([Bind(Include = "ParkID,PlaceId,AreaID")] List<Place> place)
{
if (ModelState.IsValid)
{
foreach(var i in place)
{
db.Places.Add(i);
}
db.SaveChanges();
return RedirectToAction("Index", "Area");
}
return View(place);
}
View Code
#model CarParking.Models.Area
#using (Html.BeginForm("Create","ParkingPlace", FormMethod.Post))
{
<hr />
<table>
<tr>
<th>Place ID's</th>
<th>Area ID's</th>
</tr>
#for (int i = 0; i < Model.NoOfParkingPlaces; i++)
{
<tr class="form-group">
<td>
#Html.TextBox("[" + i + "].PlaceId", "P.Id_" + i + "", htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessage("[" + i + "].PlaceId", "", new { #class = "text-danger" })
</td>
<td>
#Html.TextBox("[" + i + "].AreaID", Model.AreaID, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessage("[" + i + "].AreaID", "", new { #class = "text-danger" })
</td>
<td>
#Html.Hidden("[" + i + "].PlaceId_Status", false)
#Html.ValidationMessage("[" + i +"].PlaceId_Status", "", new { #class = "text-danger"})
</td>
</tr>
}
</table>
<p>
<input type="submit" value="Create" class="btn btn-default" />
</p>
}

Error in my upload page after i changed my model [duplicate]

This question already has answers here:
What is a NullReferenceException, and how do I fix it?
(27 answers)
Closed 6 years ago.
I added the code to show many checkboxes from my table (HairTags) and in my form CreationUpload.cshtml i got the following error :
An exception of type 'System.NullReferenceException' occurred in
App_Web eba142hb.dll but was not handled in user code Additional
information: Object reference not set to an instance of an object.
Object reference not set to an instance of an object.
<div class="col-md-12">
#for (int i = 0; i < Model.CreationHairTags.Count; i++)
{
#Html.CheckBoxFor(m => Model.CreationHairTags[i].IsChecked)
#Model.CreationHairTags[i].Text
#Html.HiddenFor(m => Model.CreationHairTags[i].Value)
#Html.HiddenFor(m => Model.CreationHairTags[i].Text)<br />
}
</div>
this is my model Creation.cs (in bold the added code)
namespace HairCollection3.Models
{
public class Creation
{
public string UserId { get; set; }
[Key]
public int CreationId { get; set; }
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ViewRes.ValidationStrings))]
[Display(Name = "Sex", ResourceType = typeof(ViewRes.Names))]
public string CreationSex { get; set; }
[Required(ErrorMessageResourceName = "Required", ErrorMessageResourceType = typeof(ViewRes.ValidationStrings))]
[Display(Name = "CreationTitle", ResourceType = typeof(ViewRes.NamesCreation))]
[StringLength(2000)]
[AllowHtml]
public string CreationTitle { get; set; }
public string CreationPhotoBis { get; set; }
public string Creationtag { get; set; }
public virtual ICollection<CreationLike> CreationLikes { get; set; }
}
public class CreationLike
{
public int CreationId { get; set; }
public string UserId { get; set; }
public virtual ApplicationUser User { get; set; }
[Key]
public int CreationLikeId { get; set; }
public virtual Creation ParentCreation { get; set; }
}
public class HairTag
{
[Key]
public int HairTagId { get; set; }
[Required]
public string HairTagTitle { get; set; }
[Required]
public string HairTagType { get; set; }
[Required]
public int HairTagOrder { get; set; }
}
***//CHECKBOXES
public class HairTagModel
{
[Key]
public int Value { get; set; }
public string Text { get; set; }
public bool IsChecked { get; set; }
}
public class HairTagList
{
private ApplicationDbContext creationdb = new ApplicationDbContext();
public HairTagList()
{
var HairTagList = creationdb.HairTags.ToList();
List<HairTagModel> obj = new List<HairTagModel>();
foreach (var tags in HairTagList)
{
obj.Add(new HairTagModel
{
Text = tags.HairTagTitle,
Value = tags.HairTagId,
IsChecked = false
});
}
this.CreationHairTags = obj;
}
public List<HairTagModel> CreationHairTags { get; set; }
//public List<HairTagModel> ListHairTags { get; set; }
}
public class CreationHairTagsModel
{
public Creation Creation { get; set; }
public List<HairTagModel> CreationHairTags { get; set; }
}
}***
My controller CreationController.cs
// GET: /Creation/CreationUpload
[Authorize]
public ActionResult CreationUpload()
{
CreationHairTagsModel creation = new CreationHairTagsModel();
return View(creation);
//return View();
}
// POST: /Creation/CreationUpload
// Afin de déjouer les attaques par sur-validation, activez les propriétés spécifiques que vous voulez lier. Pour
// plus de détails, voir http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[Authorize]
[ValidateAntiForgeryToken]
public ActionResult CreationUpload([Bind(Include = "CreationId,CreationSex,CreationTitle,CreationPhotoBis,CreationHairTags")] CreationHairTagsModel creation, IEnumerable<HttpPostedFileBase> files)
{
if (ModelState.IsValid)
{
// update each field manually
foreach (var file in files)
{
if (file != null)
{
if (file.ContentLength > 0)
{
....CODE UPLOAD HIDDEN....
//Avoid Script
var CreationTitletocheck = Regex.Replace(creation.Creation.CreationTitle, #"<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>", string.Empty);
CreationTitletocheck = Regex.Replace(CreationTitletocheck, #"(?></?\w+)(?>(?:[^>'""]+|'[^']*'|""[^""]*"")*)>", string.Empty);
creation.Creation.CreationTitle = CreationTitletocheck;
//Tags
StringBuilder sb = new StringBuilder();
foreach (var item in creation.CreationHairTags)
{
if (item.IsChecked)
{
sb.Append(item.Text + ",");
}
}
creation.Creation.Creationtag = sb.ToString();
creation.Creation.UserId = User.Identity.GetUserId();
db.Creations.Add(creation.Creation);
db.SaveChanges();
}
}
}
}
//UserId
return RedirectToAction("CreationList", "Creation", new { UserId = User.Identity.GetUserId() });
}
return View(creation);
}
My page of upload CreationUpload.cshtml
#model HairCollection3.Models.CreationHairTagsModel
#using Microsoft.AspNet.Identity
#{
ViewBag.Title = ViewRes.NamesCreation.CreationUploadTitle;
}
<div class="col-sm-12 col-md-12 chpagetop">
<h1>#ViewRes.Shared.PublishAPhoto</h1>
<hr />
#using (Html.BeginForm("CreationUpload", "Creation", FormMethod.Post, new { id = "CreationUpload", enctype = "multipart/form-data", onsubmit = "$('#creationloading').show(); $('#creationform').hide();" }))
{
#Html.AntiForgeryToken()
<div class="col-md-12" id="creationloading" style="display:none">
<div id="progress">
<p>#ViewRes.Shared.UploadPhotoProgress<strong>0%</strong></p>
<progress value="5" min="0" max="100"><span></span></progress>
</div>
</div>
<div class="col-md-12" id="creationform">
<div class="col-md-12">
#Html.ValidationMessageFor(m => m.Creation.CreationSex)
#Html.RadioButtonFor(m => m.Creation.CreationSex, "F", new { #checked = true }) #ViewRes.Shared.WomanHairstyle #Html.RadioButtonFor(m => m.Creation.CreationSex, "M") #ViewRes.Shared.ManHairstyle
</div>
<div class="col-md-12">
#Html.ValidationMessageFor(m => m.Creation.CreationTitle)
#Html.TextBoxFor(m => m.Creation.CreationTitle, new { #class = "inputplaceholderviolet wid100x100", placeholder = HttpUtility.HtmlDecode(Html.DisplayNameFor(m => m.Creation.CreationTitle).ToHtmlString()), onfocus = "this.placeholder = ''", onblur = "this.placeholder = '" + HttpUtility.HtmlDecode(Html.DisplayNameFor(m => m.Creation.CreationTitle).ToHtmlString()) + "'" })
</div>
<div class="col-md-12">
#for (int i = 0; i < Model.CreationHairTags.Count; i++)
{
#Html.CheckBoxFor(m => Model.CreationHairTags[i].IsChecked)
#Model.CreationHairTags[i].Text
#Html.HiddenFor(m => Model.CreationHairTags[i].Value)
#Html.HiddenFor(m => Model.CreationHairTags[i].Text)<br />
}
</div>
<div class="col-md-12" style="text-align: center">
<p style="display: inline-block">
<input type="file" accept="image/*" onchange="loadFile(event)" name="files" id="file1" translate="yes" data-val="true" data-val-required="A File is required." class="wid100x100" /><label for="file1"></label>
<img id="output" style="max-width:200px;"/>
</p>
</div>
<div class="col-sm-12 col-md-12 chpagetopdiv">
<button type="submit" title="#ViewRes.Shared.Publish"><span class="glyphicon glyphicon-share-alt"></span> #ViewRes.Shared.Publish</button>
</div>
</div>
}
</div>
What is wrong in my code please help and explain ?
Important: In C#, every collection must be initialized before being accessed
The error occurs when you are trying to access from the View to the collection CreationHairTags, which is not initialized. Replace your model to initialize collection in the class constructor:
public class CreationHairTagsModel
{
public Creation Creation { get; set; }
public List<HairTagModel> CreationHairTags { get; set; }
public CreationHairTagsModel()
{
CreationHairTags = new List<HairTagModel>();
}
}

ASP.net MVC 3 handling multiple checkboxes in each row of my table

I'm building an admin interface for a word guessing game so admin users are allowed to setup the game by selecting the words that will appear in the game and then select which letters in the words will be encoded.
MainGameTable Edit page.....
#model GameServer.ViewModels.GameTableModel
#section Javascript
{
<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>
}
#{
ViewBag.Title = "GameTableEdit";
Layout = "~/Views/Shared/_Layout.cshtml";
}
<h2>GameTableEdit</h2>
#using (Html.BeginForm("GameTableEdit", "Admin", FormMethod.Post, new { enctype = "multipart/form-data" }))
{
#Html.ValidationSummary(true)
<fieldset>
<legend>GameTable</legend>
#Html.HiddenFor(model => model.GameTableId)
#Html.HiddenFor(model => model.GameTableNumber)
<div class="editor-label">
Table #: #Html.DisplayFor(model => model.GameTableNumber)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.SubjectId)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.SubjectId, new SelectList(Model.Subjects, "Key", "Value"))
#Html.ValidationMessageFor(model => model.SubjectId)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.ComplexityId)
</div>
<div class="editor-field">
#Html.DropDownListFor(model => model.ComplexityId, new SelectList(Model.Complexities, "Key", "Value"))
#Html.ValidationMessageFor(model => model.ComplexityId)
</div>
<button type="submit" name="button" value="GetWords">Get Words</button>
#Html.Partial("GameMatrix/_LineWordsTable", Model)
<p>
<button type="submit" name="button" value="Save">Save</button>
</p>
</fieldset>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Partial Page_for each of the words in the table
#model GameServer.ViewModels.GameTableModel
#if (Model.SelectedLinewords.Count != null && Model.SelectedLinewords.Count > 0)
{
<table>
<tr>
<th>
Select Word
</th>
<th>
LineWord
</th>
<th>
Characters to Display
</th>
</tr>
#Html.EditorFor(x => x.SelectedLinewords)
</table>
}
The Editor Template for each row:
#model GameServer.ViewModels.SelectedLineWord
<tr>
<td>
#Html.CheckBoxFor(x => x.isSelected)
</td>
<td>
#Html.DisplayFor(x => x.LineWord)
</td>
<td>
#Html.HiddenFor(x=>x.LineWordId)
#Html.HiddenFor(x=>x.LineWord)
#{ char[] lineword = Model.LineWord.ToCharArray(); }
#for (int i = 0; i < Model.LineWord.Length; i++)
{
<input type="checkbox" name="DisplayCharPosition" value="#i" /> #lineword[i]
}
</td>
</tr>
Here is my ViewModel
public class SelectedLineWord
{
[Required]
public Guid LineWordId { get; set; }
[Required]
public String LineWord { get; set; }
public int[] DisplayCharPosition { get; set; }
[Required]
public bool isSelected { get; set; }
public SelectedLineWord()
{
}
public SelectedLineWord(Guid linewordid, String word, String displaycharposition)
{
LineWordId = linewordid;
LineWord = word;
String[] pos = displaycharposition.Split(',');
DisplayCharPosition = new int[word.Length];
for (int i = 0; i < word.Length; i++)
{
DisplayCharPosition[i] = 0;
}
for (int i = 0; i < pos.Length; i++)
{
DisplayCharPosition[Int32.Parse(pos[i])] = 1;
}
}
public SelectedLineWord(Guid linewordid, String word, bool issel)
{
LineWordId = linewordid;
LineWord = word;
isSelected = issel;
}
}
public class GameTableModel
{
[Required]
public Guid GameTableId { get; set; }
[Required]
public Guid GameMatrixId { get; set; }
[Required]
[Display(Name = "Table Subject")]
public int SubjectId { get; set; }
[Required]
[Display(Name = "Minimum Complexity")]
public int ComplexityId { get; set; }
[Required]
public int GameTableNumber { get; set; }
[Required]
[Display(Name = "Include a Bonus table")]
public bool IsBonus { get; set; }
[Display(Name = "Table Subject")]
public Dictionary<int, string> Subjects;
[Display(Name = "Minimum Complexity")]
public Dictionary<int, int> Complexities;
public List<GameTableLine> GameTableLines { get; set; }
public List<SelectedLineWord> SelectedLinewords { get; set; }
public GameTableModel ()
{
try
{
//get a connection to the database
var data = new GameServerDataModelDataContext();
//Fetch the subjects reference data
var subjects = from c in data.Subjects orderby c.Subject1 select new { c.SubjectId, c.Subject1};
Subjects = new Dictionary<int, string>();
foreach (var subject in subjects)
{
Subjects.Add(subject.SubjectId, subject.Subject1);
}
//Fetch the complexities questions
Table<Complexity> dComplexities = data.GetTable<Complexity>();
Complexities = new Dictionary<int, int> { { 0, 0 } };
foreach (var complexity in dComplexities)
{
if (complexity.Complexity1 != null)
Complexities.Add(complexity.ComplexityId, (int)complexity.Complexity1);
}
}
catch (Exception ex)
{
//[TODO: Complete the exception handeling code.]
}
}
}
My problem is when I hit the save button the model passed to the controller has everything populated correctly but returns null for the check boxes that where selected for the DisplayCharPosition. What i was expecting was an int[] populated with the index of the character selected for display.
Could someone please help me understand what I'm doing wrong?
I've manage to solve this (but i'm still open to suggestions for a better way to do it).
What I did was I changed the following line <input type="checkbox" name="DisplayCharPosition" value="#i" /> #lineword[i] to #Html.CheckBoxFor(x => x.DisplayCharPosition[i]) #lineword[i] and my model type to public bool[] DisplayCharPosition { get; set; } so that i store an array of bools. Now the bool array has a true/false value for each char that the user wants to display in the game table. I then store this in the database as a comma delimited string (e.g. 1,4,6 - which i later split so i know that char in pos 1, 4 and 6 must be displayed and the rest encoded).
I also changed my model as follows:
public class SelectedLineWord
{
[Required]
public Guid LineWordId { get; set; }
[Required]
public String LineWord { get; set; }
public bool[] DisplayCharPosition { get; set; }
[Required]
public bool isSelected { get; set; }
public SelectedLineWord()
{
}
public SelectedLineWord(Guid linewordid, String word, String displaycharposition)
{
LineWordId = linewordid;
LineWord = word;
String[] pos = displaycharposition.Split(',');
DisplayCharPosition = new bool[word.Length];
//set all to false
for (int i = 0; i < word.Length; i++)
{
DisplayCharPosition[i] = false;
}
//now only set the ones that were already in the db.
for (int i = 0; i < pos.Length; i++)
{
DisplayCharPosition[Int32.Parse(pos[i])] = true;
}
}

Resources