I have two entities Gabarit and Local_Gabarit and i have a foreign key that related those entities called CodeBarre which is the primary Key of Gabarit. So on my Gabarit Edit Page i don't want to edit Gabarit attributes but the related attributes existing in Local_Gabarit such as Armoire and Emplacement.
Please, I need your help and Thank you :)
Gabarit Model:
public Gabarit()
{
this.Demande = new HashSet<Demande>();
this.Local_Gabarit = new HashSet<Local_Gabarit>();
this.Mvt_Gabarit = new HashSet<Mvt_Gabarit>();
}
public int CodeBarre { get; set; }
public string Designation { get; set; }
public string Photo { get; set; }
public string Exemplaire { get; set; }
public Nullable<int> Produit { get; set; }
public Nullable<int> Poste { get; set; }
public virtual ICollection<Demande> Demande { get; set; }
public virtual ICollection<Local_Gabarit> Local_Gabarit { get; set; }
public virtual ICollection<Mvt_Gabarit> Mvt_Gabarit { get; set; }
Local_Gabarit Model:
public partial class Local_Gabarit
{
public int id_local { get; set; }
public Nullable<int> Emplacement { get; set; }
public string Armoire { get; set; }
public int CodeBarre { get; set; }
public int id_demande { get; set; }
public string InOut { get; set; }
public virtual Demande Demande { get; set; }
public virtual Gabarit Gabarit { get; set; }
}
GabaritViewModel:
public class GabaritViewModel
{
public int CodeBarre { get; set; }
public string Designation { get; set; }
public string Photo { get; set; }
public Nullable<int> Produit { get; set; }
public string Exemplaire { get; set; }
public Nullable<int> Poste { get; set; }
public int id_local { get; set; }
public Nullable<int> Emplacement { get; set; }
public string Armoire { get; set; }
}
GabaritsController:
// GET: Gabarits/Edit/5
public ActionResult Edit(int? id)
{
var localrepository = new LocalGabaritRepository(db);
var dbgabarit = gabaritrepository.GetById(id);
var req = localrepository.Get(p => p.CodeBarre == dbgabarit.CodeBarre).SingleOrDefault();
var armoire = req.Armoire;
var emplacement = req.Emplacement;
if (dbgabarit != null)
{
var gabarit = new GabaritViewModel()
{
CodeBarre = dbgabarit.CodeBarre,
Designation = dbgabarit.Designation,
Photo = dbgabarit.Photo,
Exemplaire = dbgabarit.Exemplaire,
Poste=dbgabarit.Poste,
Produit=dbgabarit.Produit,
Emplacement=emplacement,
Armoire=armoire,
};
return View(gabarit);
}
else
return HttpNotFound();
}
// POST: Gabarits/Edit/5
// To protect from overposting attacks, please enable the specific properties you want to bind to, for
// more details see http://go.microsoft.com/fwlink/?LinkId=317598.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit( GabaritViewModel gabarit)
{
var localrepository = new LocalGabaritRepository(db);
if (ModelState.IsValid)
{
var req= localrepository.Get(p => p.CodeBarre == gabarit.CodeBarre).SingleOrDefault();
var armoire = req.Armoire;
var emplacement = req.Emplacement;
var idl = req.id_local;
var idd = req.id_demande;
var InOut = req.InOut;
var CodeBarre = req.CodeBarre;
var dblocal = new Local_Gabarit()
{
Emplacement = emplacement,
Armoire = armoire,
id_local=idl,
id_demande=idd,
InOut=InOut,
CodeBarre=CodeBarre
};
localrepository.Update(dblocal);
}
return View(gabarit);
}
Edit.cshtml (Gabarit):
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Gabarit</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.CodeBarre)
<div class="form-group">
#Html.LabelFor(model => model.CodeBarre, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.CodeBarre, new { htmlAttributes = new { #class = "form-control", #readonly = "readonly" } })
#Html.ValidationMessageFor(model => model.CodeBarre, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group" >
#Html.LabelFor(model => model.Designation, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Designation, new { htmlAttributes = new { #class = "form-control", #readonly = "readonly" } })
#Html.ValidationMessageFor(model => model.Designation, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Photo, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
<img src="#Url.Content(Model.Photo)" width="200" height="200" />
#Html.ValidationMessageFor(model => model.Photo, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Produit, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Produit, new { htmlAttributes = new { #class = "form-control", #readonly = "readonly" } })
#Html.ValidationMessageFor(model => model.Produit, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Armoire, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Armoire, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Armoire, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Emplacement, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Emplacement, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Emplacement, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
I don't know if i'm in the right way or not but this is what i tried to do.
Thank you.
Related
I have created a viewmodel like this:
public class M_Master
{
public string SUPWH { get; set; }
public string STKGL { get; set; }
public string SALGL { get; set; }
public string COSGL { get; set; }
public decimal ROQ { get; set; }
public int MIN { get; set; }
public int MAX { get; set; }
public int LT { get; set; }
public string PRODSPEC { get; set; }
public string REMK1 { get; set; }
public string REMK2 { get; set; }
public decimal openingQty { get; set; }
public decimal openingAmt { get; set; }
public decimal onHandQty { get; set; }
public decimal ytdReceiptQty { get; set; }
}
Then I use this ViewModel to create a View. So the view looks something like the below:
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>INV_Master</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.PROD, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.PROD, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.PROD, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.WH, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.WH, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.WH, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.DESCR, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.DESCR, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.DESCR, "", new { #class = "text-danger" })
</div>
</div>
What I realise is that when I visit the page/view, the html inputfields by default are filled in with values like 0 for those properties in the ViewMoel which are int, decimal, datetime. How do I make sure that all the input fields by default are empty ?
Use Nullable types like int?, decimal?, and DateTime?. When objects are instantiated, each property will be initialized to the datatype default. Int defaults to 0, and int? defaults to null. And so on. You can still put a [Required] on the property to ensure they are entered by the user before being posted back to the server.
I'm just a beginner in MVC, so please help me out and bear with my question. I have a scenario, but I can't figure it out how to create a proper ViewModel for this. I have a ProjectViewModel which consists of 4 tables related to one another.
So far my this is my ProjectViewModel.
public class ProjectViewModel
{
public int? ProjectId { get; set; }
public string ProjectName { get; set; }
public string ProjectLocation { get; set; }
public string ProjectDescription { get; set; }
public double? WorkArea { get; set; }
public string ModeOfPayment { get; set; }
public string Duration { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public double? TotalDirectCost { get; set; }
public double? ProfitSupervision { get; set; }
public double? TotalProjectCost { get; set; }
public List<ScopeOfWork> ScopeOfWork { get; set; }
}
public class ScopeOfWork
{
public int? ScopeOfWorkId { get; set; }
public string ScopeOfWorkName { get; set; }
public List<Materials> Materials { get; set; }
public int? ProjectId { get; set; }
}
public class Materials
{
public string MaterialName { get; set; }
public int? Quantity { get; set; }
public double? Cost { get; set; }
public int? ScopeOfWorkId { get; set; }
//This should be a select list
public IEnumerable<SelectListItem> Category { get; set;
}
Supposed to be a Project can have many ScopeOfWork object and the ScopeOfWork object can have many material object. And I will fill the category property of list of categories in the controller.
I just tried to create a view for List ScopeOfWork Object to see if the ScopeOfWork object will show but didn't show in the view.
Controller:
[HttpGet]
public ActionResult _CreateProject()
{
return View();
}
//POST: Create Project
[HttpPost]
public ActionResult _CreateProject(ProjectViewModel project)
{
return View(project);
}
View:
#model MigratingDB.Models.ProjectViewModel
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>ProjectViewModel</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.ProjectId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProjectId, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProjectId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ProjectName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProjectName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProjectName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ProjectLocation, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProjectLocation, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProjectLocation, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ProjectDescription, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProjectDescription, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProjectDescription, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.WorkArea, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.WorkArea, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.WorkArea, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ModeOfPayment, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ModeOfPayment, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ModeOfPayment, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Duration, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Duration, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Duration, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.StartDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.StartDate, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.StartDate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.EndDate, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.EndDate, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.EndDate, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.TotalDirectCost, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TotalDirectCost, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TotalDirectCost, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ProfitSupervision, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProfitSupervision, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProfitSupervision, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.TotalProjectCost, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TotalProjectCost, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TotalProjectCost, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.ProjectStatus, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.ProjectStatus, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.ProjectStatus, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
Kindly give suggestions to setup a correct ViewModel for this and that will allow me to create project with multiple ScopeOfWork and its corresponding materials for each ScopeOfWork created.
UPDATED: I separated the classes in each ViewModel
ProjectViewModel
public class ProjectViewModel
{
//tbl Project
public int? ProjectId { get; set; }
public string ProjectName { get; set; }
public string ProjectLocation { get; set; }
public string ProjectDescription { get; set; }
public double? WorkArea { get; set; }
public string ModeOfPayment { get; set; }
public string Duration { get; set; }
public DateTime? StartDate { get; set; }
public DateTime? EndDate { get; set; }
public double? TotalDirectCost { get; set; }
public double? ProfitSupervision { get; set; }
public double? TotalProjectCost { get; set; }
//SelectList
public int? ProjectStatus { get; set; }
//SelectList
public int? ForemanId { get; set; }
//SelectList
public int? ClientId { get; set; }
//tbl ScopeOfWork
public List<ScopeOfWorkViewModel> ScopeOfWork { get; set; }
}
ScopeOfWorkViewModel
public class ScopeOfWorkViewModel
{
public int? ScopeOfWorkId { get; set; }
public string ScopeOfWorkName { get; set; }
public List<MaterialsViewModel> Materials { get; set; }
public int? ProjectId { get; set; }
}
MaterialsViewModel
public class MaterialsViewModel
{
public string MaterialName { get; set; }
public int? Quantity { get; set; }
public double? Cost { get; set; }
public int? ScopeOfWorkId { get; set; }
public IEnumerable<SelectListItem> Category { get; set; }
}
I am trying to save changes to in a basic CRUD. I have edited my view for 3 columns in my model (table has 7 columns).
I tried attach method which was referenced in a different post which did not work. Any thoughts would be appreciated.
Model
public class AssetRequest
{
public int Id { get; set; }
[DataType(DataType.Date)]
[Display(Name = "Request date")]
public DateTime AssetRequestDate { get; set; }
[Display(Name = "Facility")]
public int FacilityId { get; set; }
[Required]
[Display(Name = "Asset requested")]
public int AssetId { get; set; }
[Display(Name ="Serial no.")]
public string AssetSN { get; set; }
[Required]
[Display(Name = "Request type")]
public int RequestTypeId { get; set; }
[Required]
[DataType(DataType.Date)]
[Display(Name = "Job date")]
public DateTime JobRequestDate { get; set; }
[Required]
[Display(Name = "Request status")]
public int RequestStatusId { get; set; }
[Display(Name = "Tracking no.")]
public string TrackingNo { get; set; }
[Display(Name = "Comments")]
public string Comments { get; set; }
[Display(Name = "Sending facility")]
public string SendingFacilityt { get; set; }
public virtual Asset Asset { get; set; }
public virtual Facility Facility { get; set; }
public virtual ApplicationUser User { get; set; }
public virtual RequestType RequestType { get; set; }
public virtual RequestStatus RequestStatus { get; set; }
}
}
Controller
public async Task<ActionResult> Edit([Bind(Include = "RequestStatusId, TrackingNo, Comments")] AssetRequest assetRequest)
{
if (ModelState.IsValid)
{
//db.AssetRequestTable.Attach(assetRequest);
db.Entry(assetRequest).State = EntityState.Modified;
await db.SaveChangesAsync();
return RedirectToAction("All");
}
}
View
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>AssetRequest</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.Id)
<div class="form-group">
#Html.LabelFor(model => model.DistrictId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("DistrictId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.DistrictId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.AssetId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("AssetId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.AssetId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.RequestStatusId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("RequestStatusId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.RequestStatusId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.TrackingNo, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.TrackingNo, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.TrackingNo, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Comments, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Comments, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Comments, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "All")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
You ned to include the Id property (which is the primary key) to the Include list so that EF can get the item and update it.
public async Task<ActionResult> Edit([Bind(Include = "Id,RequestStatusId, TrackingNo,
Comments")] AssetRequest assetRequest)
{
// your code
}
Looks like you are using your entity model as the parameter to update the entity values. A better approach to prevent over posting is to use a view model.
I have a page with 2 textboxes and 2 dropdown lists.
The dropdown lists have optionLabels on them and I want the required validation to show if these are selected.
One of the textboxes and both of the dropdown lists are 'Required'.
If I complete the required textbox and submit the form, the 'Required' validation on the dropdown lists doesn't fire, it only fires if the 'Required' textbox doesn't have a value.
MODELS
public class CreateCaseModel
{
public CaseRegModel CaseDetails { get; set; }
public List<StartTypeListModel> CaseStartTypeList { get; set; }
public List<ClientListModel> ClientsList { get; set; }
}
public class CaseRegModel
{
[Key]
public int CaseId { get; set; }
[Required]
[Display(Name = "PX Request Date")]
public DateTime PxRequestDate { get; set; }
[Required]
public int ClientId { get; set; }
[Required]
[Display(Name = "Start Type")]
public string StartType { get; set; }
[Display(Name = "List Price")]
public string ListPrice { get; set; }
}
public class ClientListModel
{
[Required]
public int ClientId { get; set; }
[Display(Name = "Client Name")]
public string ClientName { get; set; }
}
public class StartTypeListModel
{
public int Id { get; set; }
[Required]
[Display(Name = "Start Type")]
public string StartType { get; set; }
}
VIEW - CreateCaseModel
<div class="form-group">
#Html.LabelFor(model => model.CaseDetails.StartType, new { #class = "control-label col-md-4" })
<div class="col-md-6">
#*#Html.DropDownList("StartType", null, "Select Start Type...", new { #class = "form-control" })*#
#Html.DropDownListFor(model => model.CaseDetails.StartType, new SelectList(Model.CaseStartTypeList, "StartType", "StartType"), "Select Start Type...", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.CaseDetails.StartType, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CaseDetails.PxRequestDate, htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-6">
#Html.EditorFor(model => model.CaseDetails.PxRequestDate, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CaseDetails.PxRequestDate, "", new { #class = "text-danger" })
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
#Html.LabelFor(model => model.CaseDetails.ClientId, new { #class = "control-label col-md-4" })
<div class="col-md-6">
#*#Html.DropDownList("ClientId", null, "Select Client...", new { #class = "form-control" })*#
#Html.DropDownListFor(model => model.CaseDetails.ClientId, new SelectList(Model.ClientsList, "ClientId", "ClientName"), "Select Client...", new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.CaseDetails.ClientId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.CaseDetails.ListPrice, htmlAttributes: new { #class = "control-label col-md-4" })
<div class="col-md-6">
#Html.EditorFor(model => model.CaseDetails.ListPrice, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.CaseDetails.ListPrice, "", new { #class = "text-danger" })
</div>
</div>
CONTROLLER
// GET: Case/Create
public ActionResult Index()
{
//start type list
//var CategoryList = new List<string>();
var qStartTypes = (from st in efContext.CaseStartTypes
orderby st.Id
select new StartTypeListModel
{
Id = st.Id,
StartType = st.StartType
}).ToList();
//client list
//var CategoryList = new List<string>();
var qClients = (from c in efContext.PgsClients
orderby c.ClientName
select new ClientListModel
{
ClientId = c.ClientId,
ClientName = c.ClientName
}).ToList();
return View(new CreateCaseModel
{
CaseDetails = new CaseRegModel(),
CaseStartTypeList = qStartTypes,
ClientsList = qClients
});
}
Thanks for any help.
I want to add a custom validation attribute to my model to check if any of the answers contain duplicates. That is, if the user types in the same answer for any of the fields, I want to display an error when they type in a duplicate answer.
Here's my model:
public class SecurityQuestions
{
public int Question1Id { get; set; }
public int Question2Id { get; set; }
public int Question3Id { get; set; }
public int Question4Id { get; set; }
public int Question5Id { get; set; }
public int Question6Id { get; set; }
[UniqueAnswersOnly]
public string Answer1 { get; set; }
[UniqueAnswersOnly]
public string Answer2 { get; set; }
[UniqueAnswersOnly]
public string Answer3 { get; set; }
[UniqueAnswersOnly]
public string Answer4 { get; set; }
[UniqueAnswersOnly]
public string Answer5 { get; set; }
[UniqueAnswersOnly]
public string Answer6 { get; set; }
}
Here's my attempt at custom attribute:
public class UniqueAnswersOnly: ValidationAttribute, IClientValidatable
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
//Get a list of all properties that are marked with [UniqueAnswersOnly]
var props = validationContext.ObjectInstance.GetType().GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(UniqueAnswersOnly)));
var values = new HashSet<string>();
//Read the values of all other properties
foreach(var prop in props)
{
var pValue = (string)prop.GetValue(validationContext.ObjectInstance, null);
if (prop.Name!=validationContext.MemberName && !values.Contains(pValue))
{
values.Add(pValue);
}
}
if (values.Contains(value))
{
return new ValidationResult("Duplicate answer", new[] { validationContext.MemberName });
}
return null;
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule()
{
ErrorMessage = metadata.DisplayName + " is required!",
ValidationType = "duplicateanswers"
};
yield return rule;
}
}
The problem I'm having now is the the validation is sucessful even though I enter in duplicate answers. I can still continue to next dialog (I am expecting validation to fail if duplicates are entered). I think it's because my custom attribute isn't being fired or hit because I added breakpoints but nothing is hit.
In my controller, I have if(ModelState.IsValid) { //continue to next dialog} and the model state does return valid.
You could create a custom validation attribute like this:
public class UniqueAnswersOnly : ValidationAttribute
{
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
//Get a list of all properties that are marked with [UniqueAnswersOnly]
var props = validationContext.ObjectInstance.GetType().GetProperties().Where(
prop => Attribute.IsDefined(prop, typeof(UniqueAnswersOnly)));
var values = new HashSet<string>();
//Read the values of all other properties
foreach(var prop in props)
{
var pValue = (string)prop.GetValue(validationContext.ObjectInstance);
if (prop.Name!=validationContext.MemberName && !values.Contains(pValue))
{
values.Add(pValue);
}
}
if (values.Contains(value))
{
return new ValidationResult("Duplicate answer", new[] { validationContext.MemberName });
}
return null;
}
}
and here is a test case:
public class SecurityQuestions
{
public int Question1Id { get; set; }
public int Question2Id { get; set; }
public int Question3Id { get; set; }
public int Question4Id { get; set; }
public int Question5Id { get; set; }
public int Question6Id { get; set; }
[UniqueAnswersOnly]
public string Answer1 { get; set; }
[UniqueAnswersOnly]
public string Answer2 { get; set; }
[UniqueAnswersOnly]
public string Answer3 { get; set; }
[UniqueAnswersOnly]
public string Answer4 { get; set; }
[UniqueAnswersOnly]
public string Answer5 { get; set; }
[UniqueAnswersOnly]
public string Answer6 { get; set; }
}
class Program
{
static void Main(string[] args)
{
var questions = new SecurityQuestions();
questions.Answer1 = "Test";
questions.Answer2 = "Test";
questions.Answer3 = "Test3";
questions.Answer4 = "Test4";
questions.Answer5 = "Test5";
questions.Answer6 = "Test6";
var vc = new ValidationContext(questions, null, null);
var results = new List<ValidationResult>();
var validationResult = Validator.TryValidateObject(questions, vc, results, true);
}
}
Edit:
I created a default MVC project and added your current code. That validation works just fine.
I added this to the home controller:
public ActionResult AskQuestions()
{
var questions = new SecurityQuestions();
return View(questions);
}
[HttpPost]
public ActionResult CheckQuestions(SecurityQuestions questions)
{
if (ModelState.IsValid)
{
return View();
}
else
{
return HttpNotFound();
}
}
And the AskQuestions view looks like this:
#model WebApplicationValidation.Models.SecurityQuestions
#{
ViewBag.Title = "AskQuestions";
}
<h2>AskQuestions</h2>
#using (Html.BeginForm("CheckQuestions", "Home",FormMethod.Post))
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>SecurityQuestions</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Question1Id, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Question1Id, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Question1Id, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Question2Id, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Question2Id, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Question2Id, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Question3Id, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Question3Id, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Question3Id, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Question4Id, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Question4Id, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Question4Id, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Question5Id, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Question5Id, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Question5Id, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Question6Id, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Question6Id, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Question6Id, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Answer1, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Answer1, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Answer1, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Answer2, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Answer2, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Answer2, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Answer3, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Answer3, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Answer3, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Answer4, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Answer4, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Answer4, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Answer5, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Answer5, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Answer5, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.Answer6, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Answer6, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Answer6, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Create" class="btn btn-default" />
</div>
</div>
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
#section Scripts {
#Scripts.Render("~/bundles/jqueryval")
}
If I run the app the ModelState.IsValid() is false if I enter two identical answers. And setting a breakpoint in the validation routine shows that it's being run.
You can implement the IValidatableObject interface in your Model like below, and use jQuery to take care of the validation on the client side.
public class SecurityQuestions : IValidatableObject
{
public int Question1Id { get; set; }
public int Question2Id { get; set; }
public int Question3Id { get; set; }
public int Question4Id { get; set; }
public int Question5Id { get; set; }
public int Question6Id { get; set; }
public string Answer1 { get; set; }
public string Answer2 { get; set; }
public string Answer3 { get; set; }
public string Answer4 { get; set; }
public string Answer5 { get; set; }
public string Answer6 { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var securityAnswers = new List<string>();
securityAnswers.Add(this.Answer1);
securityAnswers.Add(this.Answer2);
securityAnswers.Add(this.Answer3);
securityAnswers.Add(this.Answer4);
securityAnswers.Add(this.Answer5);
securityAnswers.Add(this.Answer6);
bool hasDuplicates = securityAnswers.GroupBy(x => x).Where(g => g.Count() > 1).Any();
if (hasDuplicates)
{
yield return new ValidationResult(
"There are duplicate Answers...");
}
}
}