ASP.NET View IEnumerable Item validation - asp.net-mvc

I have this ASP:NET MVC Razor View which has a IEnumerable as model .
I'm creating a table where each line represents a item from the IEnumerable.
I'm using this code:
#foreach (var item in Model)
{
<tr>
<td>
<input type="checkbox" name="selectedFoo" value="#item.isAdded"
#(Html.Raw(item.isAdded? "checked=\"checked\"" : "")) />
#item.FooName
</td>
<td>
#Html.EditorFor(modelItem=> item.Name, new { htmlAttributes = new { #class = "form-control", style = "width: 70px" } })
#Html.ValidationMessageFor(modelItem=> item.Name, "", new { #class = "text-danger" })
</td>
</tr>
}
My problem is that when I enter an incorrect value for the "Name" property all the other text input get the validation error.
Solutions?
Thank you.

I would remove '#:' from the table elements. Also using the razor syntax I would generate a form tag. That may have something to do with it. Also add the ValidationSummary method.
Is that snippet you posted the complete view?
#using (Html.BeginForm())
{
#Html.ValidationSummary(true, "", new class{#class="text-danger"})
#foreach (var item in Model)
{
<tr>
<td>
<input type="checkbox" name="selectedFoo" value="#item.isAdded"
#(Html.Raw(item.isAdded? "checked=\"checked\"" : "")) />
#item.FooName
</td>
<td>
#Html.EditorFor(modelItem=> item.Name, new { htmlAttributes = new { #class = "form-control", style = "width: 70px" } })
#Html.ValidationMessageFor(modelItem=> item.Name, "", new { #class = "text-danger" })
</td>
</tr>
}
}
If it's a partial view make sure the property 'Name' doesn't exist on any other fields in the view. 'Name' is very generic, perhaps you should rename it something more descriptive. 'FullName', 'ProductName'. ect....

Your foreach loop is generating duplicated name attributes for each textbox, and the ValidationMessageFor() applies to all elements with that name. In addition, you will never be able to bind to you model when you submit your form.
You need to use a for loop or custom EditorTemplate to generate your elements (refer this answer for more detail)
Assuming your model is IList<T>, then
#for(int i = 0; i < Model.Count; i++)
{
#Html.EditorFor(m => m[i].Name, new { htmlAttributes = new { #class = "form-control", style = "width: 70px" } })
#Html.ValidationMessageFor(m => m[i].Name, "", new { #class = "text-danger" })
}
In addition, use the CheckBoxFor() method to generate checkboxes (assumes selectedFoo is typeof bool)
#Html.CheckBoxFor(m => m[i].selectedFoo)
#Html.LabelFor(m => m[i].selectedFoo, Model[i].FooName)

This can be solved by creating another instance of the model solely for validation purpose in the view.
#{YourNameSpace.Models.Model ValidationModel = new YourNameSpace.Models.Model ();}
Than you can use it in the form like this:
<div class="form-group">
<input asp-for="#ValidationModel.PropertyName" name="[0].Name" class="form-control" />
<span asp-validation-for="#ValidationModel.PropertyName" class="text-danger"></span>
</div>
Or using HTML helper:
#Html.ValidationMessageFor(modelItem => ValidationModel.Name, "", new { #class = "text-danger" })

Related

Textbox of editorfor htmlhelper is different for integer type

I am using to Editorfor for displaying values of a class in the view. I am using MVC . While the textbox properties and appearance are the same for values of string type, the textbox appearance for value of integer is changed and not consistence with the other textbox appearances.
I have uploaded a snap of the view as well as the code.
<tr>
<td>
#Html.LabelFor(model => model.Id, "Project Id", new { #class = "control-label" })
</td>
<td>
#Html.EditorFor(model=>model.Id)
#Html.ValidationMessageFor(model => model.Id)
</td>
</tr>
<tr>
<td>
#Html.LabelFor(model => model.Name, "Project Name", new { #class = "control-label" })
</td>
<td>
<div>
#Html.EditorFor(model => model.Name, new { #readonly = "readonly" })
#Html.ValidationMessageFor(model => model.Name)
</div>
</td>
</tr>
enter image description here
I tried going through the files to find the exact styling, but couldn't afford to spend too much time searching as the CSS file contained the code in a single line (to reduce size of the file). I however worked out a work-around by adding a string variable to the same class and assigning the integer value before passing the model to the view.

C# MVC form doesn't work with input form attribute

I need to use form inside table cell with fields in another cells. I'm using input form attribute. MVC passing data to controller works correctly but validation before form send doesn't work with this attribute.
<tr>
<td>
#Html.EditorFor(model => item.Code, new { htmlAttributes = new { #class = "form-control inline-edit", form = "editForm" + item.Id } })
#Html.ValidationMessageFor(model => item.Code)
</td>
<td>
#Html.EditorFor(model => item.Name, new { htmlAttributes = new { #class = "form-control inline-edit", form = "editForm" + item.Id } })
#Html.ValidationMessageFor(model => item.Name)
</td>
<td class="text-right">
#using (Html.BeginForm("UpdatePrintMaterial", "Production", FormMethod.Post, new { id = "editForm" + item.Id }))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => item.Id)
<button class="btn btn-default save" data-toggle="tooltip" title="Zapisz"><i class="fa fa-save"></i></button>
}
</td></tr>
How to workaroud this issue to use validation before sending data?

Incorrect Item gets deleted in MVC

After I delete a list item ex:2nd item out of 4 from the below code by clicking delete button on the row, it shows 1,2,3 in view instead of 1,3,4. When I debug the view it shows right value, however on page rendering TextBoxFor AeroDromeID is showing wrong. Also the delete aerodrome button shows the right value. Can some one help me to fix this.
Controller
[HttpPost]
[MultipleButton(Name = "action", Argument = "DeleteAerodrome")]
public ActionResult DeleteAerodrome(FormCollection fc)
{
int AerodromeID = Int32.Parse(fc["action:DeleteAerodrome"]);
var studentSignoutModel = new StudentSignoutViewModel();
UpdateModel<StudentSignoutViewModel>(studentSignoutModel);
if (studentSignoutModel.Aerodromes.Count > 0)
studentSignoutModel.Aerodromes.RemoveAll(m => m.AerodromeID == AerodromeID);
return View("StudentSignoutStageView", studentSignoutModel);
}
View
#if (Model.Aerodromes != null && Model.Aerodromes.Any())
{
for (int i = 0; i < Model.Aerodromes.Count; i++)
{
<tr>
<td>#Html.TextBoxFor(a => Model.Aerodromes[i].AerodromeID, new { #class = "form-control"})</td>
<td>#Html.TextBoxFor(a => Model.Aerodromes[i].CloudBase, new { #class = "form-control" })</td>
<td>#Html.TextBoxFor(a => Model.Aerodromes[i].Visibility, new { #class = "form-control" })</td>
<td>#Html.TextBoxFor(a => Model.Aerodromes[i].Wind, new { #class = "form-control" })</td>
<td>#Html.TextBoxFor(a => Model.Aerodromes[i].Crosswind, new { #class = "form-control" })</td>
<td>#Html.TextBoxFor(a => Model.Aerodromes[i].InterTempo, new { #class = "form-control" })</td>
<td>
<input type="submit" id="BtnDelAerodrome" value="#Model.Aerodromes[i].AerodromeID" name="action:DeleteAerodrome" style="font-size:1em" class="btn btn-danger" onclick="return confirm('Are you sure you want to delete the record with AerodromeID = '+ #Model.Aerodromes[i].AerodromeID);" />
</td>
</tr>
}
}
I think there should be a bug with Data Binding concept while rendering from server side, when we use TextBoxFor.
When I use html tag input with the model binder,it worked.
This is easily replicable.

send Dynamic list to Controller from View

I am getting quite responses when I add/delete rows.
When page is loaded the default rows 3 will be added.(But user can delete those rows)
When I am trying to add new row then 4 value rows are coming in controller but when I am trying to delete any default row then it is not showing the newly added row.
Here is my below code.
#model ProductRegistration.Models.InstalledProductInformationModel
#using (Html.BeginForm())
{
#for (int i = 0; i < ((Model != null && Model.listInstallProducts != null) ? Model.listInstallProducts.Count : 3); i++)
{
<tr>
<td>
#Html.TextBoxFor(m => m.listInstallProducts[i].SerialNumber, new { #class = "form-control input-sm input-Serial", #placeholder = "Enter serial number", #maxlength = 50, #OnKeypress = "return alphanumeric_only(event);" })
#Html.ValidationMessageFor(m => m.listInstallProducts[i].SerialNumber)
<div class="SerialErrorDisplay" style="color: red"></div>
</td>
<td>
#Html.DropDownListFor(m => m.listInstallProducts[i].ModelNumber, new SelectList(string.Empty, "Value", "Text"), "--Select Model--", new { #class = "form-control input-sm input-Model" })
#Html.ValidationMessageFor(m => m.listInstallProducts[i].ModelNumber)
<div class="ModelErrorDisplay" style="color: red"></div>
</td>
<td>
#Html.TextBoxFor(m => m.listInstallProducts[i].InstallationDate, new { #class = "form-control input-sm input-Date", #maxlength = 50, #placeholder = "DD/MM/YYYY" })
#Html.ValidationMessageFor(m => m.listInstallProducts[i].InstallationDate)
<div class="DateErrorDisplay" style="color: red"></div>
</td>
<td>
<img src="~/Images/delete_icon.png" onclick="DeleteProduct(this);" />
</td>
</tr>
}
<input class="btn btn-primary top-padding" type="submit" value="Previous" id="back-step" name="direction"/>
}
In Controller:
public ActionResult InstalledProductInfo(InstalledProductInformationModel InstalledProducts, string direction)
{
if(ModelState.IsValid)
{
return RedirectToAction("EquipmentOwnerInfo");
}
return View(InstalledProducts);
}
Model is:
public class InstalledProductInformationModel : InstalledProductInformation
{
public InstalledProductInformation installedProductInformation { get; set; }
public List<InstalledProductInformation> listInstallProducts { get; set; }
}
please help me out.
By default, the DefaultModelBinder required collection indexers to start at zero and be consecutive. If you delete the first item (indexer = 0) then no items will be bound. If you delete the 3rd item (indexer = 2) then only the first 2 items will be bound. You can override this behavior by adding an input for a special property named Index where the value equals the indexer.
#for (int i = 0; i < ((Model != null && Model.listInstallProducts != null) ? Model.listInstallProducts.Count : 3); i++)
{
<tr>
<td>
#Html.TextBoxFor(m => m.listInstallProducts[i].SerialNumber, new { #class = "form-control input-sm input-Serial", #placeholder = "Enter serial number", #maxlength = 50, #OnKeypress = "return alphanumeric_only(event);" })
#Html.ValidationMessageFor(m => m.listInstallProducts[i].SerialNumber)
<div class="SerialErrorDisplay" style="color: red"></div>
</td>
....
<td>
// Add the following input
<input type="hidden" name="listInstallProducts.Index" value="#i" />
<img src="~/Images/delete_icon.png" onclick="DeleteProduct(this);" />
</td>
</tr>
}
<input class="btn btn-primary top-padding" type="submit" value="Previous" id="back-step" name="direction"/>
Side note: You should be adding the 3 items in the controller before you pass the model to the view so you get proper model binding and then simply use #for (int i = 0; i < Model.listInstallProducts.Count; i++)
List indexes are enumerated continuously, so there can be no gap. If You remove one row, the list index is no more continuous. The new row should be there if You will deal with indexes.

asp net mvc partial view validation

Hi how are you? I'm trying to validate a form in ASP NET MVC.
I have a partial view "Address" that I reuse for some entities, like Company, Person, etc.
My problem is that when I submit the form, only the controls of the father view gets validated, the ones in the partial view don't.
Here's some code I hope u can geive me hand
PERSON VIEW
#model Entities.Person
#using (Html.BeginForm("Create", "Person", FormMethod.Post))
{
<table>
<tr>
<td>
#Html.LabelFor(model => model.FirstName)
<div class="control">
#Html.TextBoxFor(model => model.FirstName, new { #maxlength = 7, #class = "numeric"})
#Html.ValidationMessageFor(model => model.FirstName)
</div>
<div class="spacer-short"></div>
</td>
<td>
#Html.LabelFor(model => model.LastName)
<div class="control">
#Html.TextBoxFor(model => model.LastName, new { #maxlength = 7, #class = "numeric"})
#Html.ValidationMessageFor(model => model.LastName)
</div>
<div class="spacer-short"></div>
</td>
</tr>
</table>
#{ Html.RenderAction("Index", "Address", new {id = Model.AddressId});} //Renders the Address form part
<div class="spacer"></div>
<button type="submit" data-button="true" data-button-icon="ui-icon-disk" class="">Create</button>
<button type="button" data-button="true" data-button-icon="ui-icon-circle-close" class="button-cancel">Cancel</button>
}
ADDRESS VIEW
#model Entities.Address
<table>
<tr>
<td>
#Html.LabelFor(model => model.Street)
<div class="control">
#Html.TextBox("Address.Street", Model.Street)
#Html.ValidationMessage("Address.Street")
</div>
<div class="spacer-short"></div>
</td>
<td>
#Html.LabelFor(model => model.Number)
<div class="control">
#Html.TextBox("Address.Number", Model.Number)
#Html.ValidationMessage("Address.Number")
</div>
<div class="spacer-short"></div>
</td>
</tr>
</table>
Then I have some validation metadata ([Required]) for person and address fields
A common mistake using #Html.Partial(...) and expecting validation errors to show up is not passing ViewData to that partial.:
#Html.Partial([ViewName], [EmailAddressObject], ViewData)
Try this:
#Html.EditorFor(Model.Street)
#Html.ValidationMessageFor(Model.Street)
instead of this:
#Html.TextBox("Address.Street", Model.Street)
#Html.ValidationMessage("Address.Street")
Try using Html.Partial(...) instead of Html.RenderAction(...) to render the partial view. This may be suppressing the validations.
Model
İmportant: Manage Nuget Packages
=>> JQuery.Validation
=>> Microsoft.JQueryUnobtrusive.Validation
install.
1) First step
using System.ComponentModel.DataAnnotations;
2)
public int Id { get; set; }
[Required(ErrorMessage = "* Kategori adı boş geçilemez.")]
[DisplayName("Kategori Adı")]
public string CategoryName { get; set; }
[Required(ErrorMessage = "* Kategori açıklaması boş geçilemez.")]
[DisplayName("Kategori Açıklaması")]
public string Description { get; set; }
3)
if (model != null && ModelState.IsValid)
{
var categoryCreate = new Categories
{
//code
};
_CategoriesService.Create(categoryCreate);
}
4)
Add ViewModel
#model Models.CategoryViewModel
After
#Html.TextBoxFor(model => model.CategoryName, new { #class = "form-control input-sm", id = "categoryTitle", #placeholder = "Kategori adını giriniz.", #type= "text", #required=true })
#Html.ValidationMessageFor(model => model.CategoryName, "", new { #class = "error-message" })
#Html.TextBoxFor(model => model.Description, new { #class = "form-control input-sm", id = "categoryArticle", #placeholder = "Kategori açıklamasını giriniz.", #type = "text", #required = true })
#Html.ValidationMessageFor(model => model.Description, "", new { #class = "error-message" })
In my case, I was missing
<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>
Once I put that in my _layout.cshtml file, it started working even with partial views.

Resources