Model Binding in List - c#

I have a list inside ViewModel but when I make the Post to the Controller it's not binding it and shows an error on Parameter: null, Value: Null.
If you could help me out on this.
ViewModel:
public class OperaRateImportRuleViewModel
{
public SelectList ResortsSelectList { get; set; }
[Required]
[DisplayName("Resorts")]
public List<string> ListResort { get; set; }
public List<Rules> ExtraRuleList { get; set; }
public OperaRateImportRuleViewModel()
{
ExtraRuleList = new List<Rules>();
ListResort = new List<string>();
}
}
Controller:
public ActionResult EditRules(string id)
{
OperaRateImportRuleViewModel model = new OperaRateImportRuleViewModel();
var getRulesForResort = service.GetAllOperaRateRules().Where(x => x.ResortCode == id).ToList();
foreach (var item in getRulesForResort)
{
var ruleModel = new Rules()
{
Id = item.Id,
IsReferenceRule = item.IsReferenceRule,
PercentVariation = item.PercentVariation == null ? 0 : Decimal.Round(item.PercentVariation.Value, 2),
RateCode = item.RateCode,
ResortCode = item.ResortCode,
RoomType = item.RoomType,
SingleRoomDifference = item.SingleRoomDifference == null ? 0 : Decimal.Round(item.SingleRoomDifference.Value, 2),
SupplementValue = item.SupplementValue == null ? 0 : Decimal.Round(item.SupplementValue.Value, 2)
};
model.ExtraRuleList.Add(ruleModel);
}
return View(model);
}
[HttpPost]
public ActionResult EditRules(OperaRateImportRuleViewModel model)
{
foreach (var item in model.ExtraRuleList)
{
var rule = service.GetAllOperaRateRules().Where(x => x.Id == item.Id).FirstOrDefault();
rule.RateCode = item.RateCode;
rule.ResortCode = item.ResortCode;
rule.RoomType = item.RoomType;
rule.PercentVariation = item.PercentVariation;
rule.SupplementValue = item.SupplementValue;
rule.SingleRoomDifference = item.SingleRoomDifference;
rule.IsReferenceRule = item.IsReferenceRule;
service.Edit(rule);
}
return RedirectToAction("ManageRules");
}
And finally my View:
for (int i = 0; i < Model.ExtraRuleList.Count; i++)
{
#Html.HiddenFor(x => Model.ExtraRuleList[i].Id)
<div class="row">
<div class="col-md-2">
<div class="form-group">
#Html.TextBoxFor(x => x.ExtraRuleList[i].ResortCode, new { #class = "form-control" })
</div>
</div>
<div class="col-md-2">
<div class="form-group">
#Html.TextBoxFor(x => x.ExtraRuleList[i].ResortCode, new { #class = "form-control" })
</div>
</div>
<div class="col-md-2">
<div class="form-group">
#Html.TextBoxFor(x => x.ExtraRuleList[i].RoomType, new { #class = "form-control" })
</div>
</div>
<div class="col-md-1">
<div class="form-group">
#Html.TextBoxFor(x => x.ExtraRuleList[i].PercentVariation, new { #class = "form-control textBoxSize" })
</div>
</div>
<div class="col-md-1">
<div class="form-group">
#Html.TextBoxFor(x => x.ExtraRuleList[i].SupplementValue, new { #class = "form-control textBoxSize" })
</div>
</div>
<div class="col-md-1">
<div class="form-group">
#Html.TextBoxFor(x => x.ExtraRuleList[i].SingleRoomDifference, new { #class = "form-control textBoxSize" })
</div>
</div>
<div class="col-md-1">
<div class="form-group">
#Html.LabelFor(m => m.ExtraRuleList[i].IsReferenceRule, new { #class = "checkbox-label" })
#Html.CheckBoxFor(x => x.ExtraRuleList[i].IsReferenceRule)
</div>
</div>
<div class="col-xs-2">
<div class="form-group">
<span id="deleteSeason" title="Delete" onclick="$(this).closest('.row').remove().trigger(review());" class="glyphicon glyphicon-remove text-danger row-action"></span><span> </span>
</div>
</div>
</div>
}
<input type="submit" value="Edit" class="btn btn-primary" />
Back
Error:
Error when Binding
Much appreciated!
Thanks :)
EDIT
ViewModel Rules:
public class Rules
{
public int Id { get; set; }
[DisplayName("Rate Code")]
public string RateCode { get; set; }
[DisplayName("Resort Code")]
public string ResortCode { get; set; }
[DisplayName("Room Type")]
public string RoomType { get; set; }
[DisplayName("Percent Variation")]
public decimal? PercentVariation { get; set; }
[DisplayName("Supplement Value")]
public decimal? SupplementValue { get; set; }
[DisplayName("Single Room Difference")]
public decimal? SingleRoomDifference { get; set; }
[DisplayName("")]
public bool IsReferenceRule { get; set; }
public string HotelString { get; set; }
}

Your error is thrown because your IsReferenceRule property is decorated with [DisplayName("")]. You need to either delete it, or give it a value, for example
[DisplayName("Is Reference Rule ")]
public bool IsReferenceRule { get; set; }
but in any case, you should be using the DisplayAttribute, not the DisplayNameAttribute
[Display(Name = "Is Reference Rule ")]
Specifically, the error occurs when the public override IEnumerable<ModelValidationResult> Validate(object container) method of DataAnnotationsModelValidator is called. The offending line in the source code is context.DisplayName = Metadata.GetDisplayName(); which returns null because of the missing value in the attribute.

Related

Combox causing razor page not to render

I should expect to see a drop down box with items from the staff remembers table but instead my view does not render. This there for makes the page appear broken I was wondering also is their a way to get the error that caused the page not to render correclty.
#model FuelActivityTrackerDal.ViewModels.ActivityEditViewModal
<div class="container py-5">
<div class="row">
<div class="col-md-10 mx-auto">
<form>
<div class="form-group row">
<div class="col-sm-6">
<label for="inputFirstname">Activty Name</label>
#Html.TextBoxFor(model => model.Name, new { #class = "whatever-class", #cols = 10, #rows = 25 })
</div>
<div class="col-sm-3">
<label for="inputLastname">Activity Start Date</label>
</div>
</div>
<div class="form-group row">
<div class="col-sm-6">
#Html.TextAreaFor(model => model.Description, new { #class = "whatever-class", #cols = 10, #rows = 25 })
</div>
<div class="col-sm-3">
<label for="inputAddressLine2">HoursLeftOnProject </label>
<input type="text" class="form-control" id="inputAddressLine2" placeholder="HoursLeftOnProject ">
</div>
</div>
<div class="form-group row">
<div class="col-sm-6">
<label for="inputCity">Status </label>
<input type="text" class="form-control" id="inputCity" placeholder="Status ">
</div>
<div class="col-sm-3">
<label for="inputState">ActivityType </label>
<input type="text" class="form-control" id="inputState" placeholder="ActivityType ">
</div>
<div class="col-sm-3">
<label for="inputPostalCode">SOP</label>
<div class="form-group">
#Html.LabelFor(x => Model.StaffID, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-5">
<div class="form-group">
#Html.LabelFor(x => Model.StaffID, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-5">
#Html.DropDownListFor(x => Model.StaffID, new SelectList(Model.StaffMemmbers, "Value", "Text"), htmlAttributes: new { #class = "form-control", #id = "Region" })
#Html.ValidationMessageFor(x => x.StaffID, "", new { #class = "text-danger" })
</div>
</div>
</div>
</div>
</div>
</div>
<button type="button" class="btn btn-primary px-4 float-right">Save</button>
<button type="button" class="btn btn-primary px-4 float-right">Cancel</button>
</form>
</div>
</div>
</div>
However I am using the ActivityEditViewModal to populate my view.
public class ActivityEditViewModal
{
public int ActivityHeaderId { get; set; } //(int, null)
public DateTime? ActivityDate { get; set; } //(date, null)
public string Name { get; set; } //(nvarchar(350), null)
public DateTime? ActivityEndDate { get; set; } //(datetime, null)
public string Description { get; set; } //(nvarchar(max), null)
public int? ActivityLinesId { get; set; } //(int, null)
public int? HoursLeftOnProject { get; set; } //(time(7), null)
public int Status { get; set; } //(nchar(10), null)
public int ActivityType { get; set; } //(int, null)
public DateTime? CreatedDate { get; set; } //(date, null)
public string CreatedBy { get; set; } //(nvarchar(50), null)
public bool? isActive { get; set; } //(bit, null)
public bool? isDeleted { get; set; } //(bit, null)
public bool? isArchived { get; set; } //(bit, null)
public int? SOP { get; set; } //(nvarchar(50), null)
public int? DepartmentId { get; set; } //(int, null)
public string EmployeeName { get; set; } //(nvarchar(301), null)
public string StaffName { get; set; }
public int StaffID { get; set; }
public IEnumerable<SelectListItem> StaffMemmbers { get; set; }
}
In my Activity Repo I am populating the staff members are such which you can see from screen shot is working.
public List<ActivityEditViewModal> GetAllActivites()
{
var staffRepo = new StaffRepositry(_db);
List<ActivityHeader> activity = new List<ActivityHeader>();
activity = _db.ActivityHeader.AsNoTracking()
.Include(x => x.StaffMembers)
.ToList();
if (activity != null)
{
List<ActivityEditViewModal> activityDisplay = new List<ActivityEditViewModal>();
foreach (var x in activity)
{
var customerDisplay = new ActivityEditViewModal()
{
ActivityHeaderId = x.ActivityHeaderId,
ActivityDate =x.ActivityDate,
Name=x.Name,
ActivityEndDate = x.ActivityEndDate,
Description = x.Description,
StaffID =(int)x.StaffId
};
customerDisplay.StaffMemmbers = staffRepo.GetStaffs();
activityDisplay.Add(customerDisplay);
}
return activityDisplay;
}
return null;
}
public IEnumerable<SelectListItem> GetStaffs()
{
List<SelectListItem> staff = _db.Staff
.OrderBy(n => n.FirstName)
.Select(n =>
new SelectListItem
{
Value = n.StaffID.ToString(),
Text = n.FirstName + " " + n.LastName
}).ToList();
var staffip = new SelectListItem()
{
Value = null,
Text = "--- select staff ---"
};
staff.Insert(0, staffip);
return new SelectList(staff, "Value", "Text");
}
}
The result is a full broken page. When i take out the code for the dropdown it works fine.

Always receive null data when inserting to db

I'm using MVC 4. I make it in partial view because in one form I want to insert a lot of data to other database in one time. But when I insert the data, it successfully inserts but the data that I take from textbox is always empty, only data that has been initialized can be inserted. It can be inserted successfully but the data is always empty.
Model
namespace admission.Models
{
public class AlamatRumahModel
{
public int id_alamat_rumah { get; set; }
public string alamat_lengkap { get; set; }
public string provinsi { get; set; }
public string kota { get; set; }
public string kode_pos { get; set; }
public string telepon_rumah { get; set; }
public string hp { get; set; }
public string fax { get; set; }
public string email { get; set; }
public DateTime created { get; set; }
public string createdBy { get; set; }
public DateTime modified { get; set; }
public string modifiedBy { get; set; }
}
}
Controller
[HttpPost]
public ActionResult IRumah(AlamatRumahModel model)
{
Tbl_Alamat_Rumah rumah = new Tbl_Alamat_Rumah();
model = new AlamatRumahModel();
using (pmbEntities db = new pmbEntities())
{
int LastRumah = db.Tbl_Alamat_Rumah.Max(r => r.id_alamat_rumah);
rumah.id_alamat_rumah = LastRumah+ 1;
rumah.alamat_lengkap = model.alamat_lengkap;
rumah.provinsi= Request["propinsiR"];
rumah.kota = Request["kotaR"];
rumah.kode_pos = model.kode_pos;
rumah.telepon_rumah = model.telepon_rumah;
rumah.hp = model.hp;
rumah.fax = model.fax;
rumah.email = model.email;
rumah.created = DateTime.Now;
rumah.modified = DateTime.Now;
rumah.createdBy = "";
rumah.modifiedBy = "";
db.Tbl_Alamat_Rumah.Add(rumah);
db.SaveChanges();
return View();
}
}
[HttpPost]
public ActionResult InsertDataDiri(DataDiriModel model, AlamatRumahModel rumah, AlamatSuratModel surat, CpModel cp)
{
IDataDiri(model);
IRumah(rumah);
ISurat(surat);
IcP(cp);
return RedirectToAction("InsertRiwayatPendidikan", "User");
}
View
#model admission.Models.AlamatRumahModel
#using (Html.BeginForm("IRumah", "User", FormMethod.Post))
{
<fieldset>
<div class="col-xs-12">
<div class="col-md-12">
<br><br>
<legend>
<h3><span>Alamat Rumah<i class="arrow-down"></i></span></h3>
</legend>
<br>
<!-- Text input-->
<div class="content">
<div class="form-group">
<label class="col-md-4 control-label">Alamat Lengkap</label>
<div class="col-md-5">
#Html.TextAreaFor(model => model.alamat_lengkap, new { #class = "form-control input-md", #autocomplete = "off"})
</div>
</div>
<br>
<br>
<br />
<!-- Select Basic -->
<div class="form-group">
<label class="col-md-4 control-label" for="stack_id">Propinsi</label>
<div class="col-md-5">
#(Html.Kendo().DropDownList()
.Name("propinsiR")
.OptionLabel("Please select Branch ...")
.HtmlAttributes(new { style = "width:100%" })
.DataTextField("Text")
.DataValueField("Value")
.Filter("contains").DataSource(source => { source.Read(read => read.Action("GetProvinsiByJSON", "Lokasi")); })
)
</div>
</div>
<br>
<br>
<!-- Select Basic -->
<div class="form-group">
<label class="col-md-4 control-label" for="stack_id">Kota</label>
<div class="col-md-5">
#(Html.Kendo().DropDownList()
.Name("kotaR")
.OptionLabel("Please select Branch ...")
.HtmlAttributes(new { style = "width:100%" })
.DataTextField("Text")
.DataValueField("Value")
.Filter("contains").DataSource(source => { source.Read(read => read.Action("GetKotaByJSON", "Lokasi")); })
)
</div>
</div>
<br>
<br>
<!-- Text input-->
<div class="form-group">
<label class="col-md-4 control-label" for="cmpny">Kode Pos</label>
<div class="col-md-5">
#Html.TextBoxFor(model => model.kode_pos, "", new { #class = "form-control input-md", #autocomplete = "off" })
</div>
</div>
<br>
<br>
This is what I get in my db:
I just noticed in your controller that you are instantiating the model again.
[HttpPost]
public ActionResult IRumah(AlamatRumahModel model)
{
Tbl_Alamat_Rumah rumah = new Tbl_Alamat_Rumah();
model = new AlamatRumahModel(); //Remove this line of code
using (pmbEntities db = new pmbEntities())
{
...
That's why you lost the values of your model. Remove that line of code, and it should work fine.

Display list of objects and set a value field for each input MVC

I have the following class defined as my ViewModel
public class CreateApplicationViewModel
{
public Step1ViewModel Step1 { get; set; }
public Step2StandAloneViewModel Step2StandAlone { get; set; }
public Step2ChildViewModel Step2Child { get; set; }
public Step3ViewModel Step3 { get; set; }
public Step4ViewModel Step4 { get; set; }
}
I'm trying to display items in the Step4ViewModel which consists of the following:
public class Step4ViewModel
{
public List<DataDetails> DataDetails = new List<DataDetails>();
}
public class DataDetails
{
public string GroupCode { get; set; }
public string GroupDesc { get; set; }
public decimal DetailSequence { get; set; }
public string DetailCode { get; set; }
public string DetailDesc { get; set; }
public string YesNoFlag { get; set; }
public string NumberFlag { get; set; }
public string ValueFlag { get; set; }
public string DateFlag { get; set; }
public string ListValuesFlag { get; set; }
public string CommentFlag { get; set; }
public string CalcRateFlag { get; set; }
public string ColumnSequence { get; set; }
public string TextFlag { get; set; }
public string CheckboxFlag { get; set; }
public string YesNoValue { get; set; }
public int NumberValue { get; set; }
public DateTime DateValue { get; set; }
public string ListValue { get; set; }
public string CommentValue { get; set; }
public string TextValue { get; set; }
public bool CheckboxValue { get; set; }
}
In my controller I populate the Step4ViewModel.DataDetails like so:
private Step4ViewModel GetCaseDataDetails(string caseType)
{
Step4ViewModel model = new Step4ViewModel();
List<DataDetails> data = new List<DataDetails>();
List<DataDetailsValues> values = new List<DataDetailsValues>();
var dataDetails = (from tb1 in db.DEFAULT_CASE_DATA_VW
join tb2 in db.CASE_DATA_DETAIL on tb1.CASE_DATA_GROUP_ID equals tb2.CASE_DATA_GROUP_ID
where tb1.BUS_CASE_CODE == caseType
orderby tb2.DETAIL_SEQUENCE
select new { tb1, tb2 });
foreach (var detail in dataDetails.ToList())
{
DataDetails i = new DataDetails();
DataDetailsValues j = new DataDetailsValues();
i.CalcRateFlag = detail.tb2.CALC_RATE_FLAG;
i.CheckboxFlag = detail.tb2.CHECKBOX_FLAG;
i.ColumnSequence = detail.tb2.COLUMN_SEQUENCE;
i.CommentFlag = detail.tb2.COMMENT_FLAG;
i.DateFlag = detail.tb2.DATE_FLAG;
i.DetailCode = detail.tb2.DETAIL_CODE;
i.DetailDesc = detail.tb2.DETAIL_DESC;
i.DetailSequence = detail.tb2.DETAIL_SEQUENCE;
i.GroupCode = detail.tb1.GROUP_CODE;
i.GroupDesc = detail.tb1.GROUP_DESC;
i.ListValuesFlag = detail.tb2.LIST_VALUES_FLAG;
i.NumberFlag = detail.tb2.NUMBER_FLAG;
i.TextFlag = detail.tb2.TEXT_FLAG;
i.ValueFlag = detail.tb2.VALUE_FLAG;
i.YesNoFlag = detail.tb2.YES_NO_FLAG;
data.Add(i);
}
model.DataDetails = data;
return model;
}
My thought process with the Step4ViewModel is that for every DataDetail I will display the DetailDesc as a label and then beside of it I will have an input for the NumberValue, YesOrNoValue, NumberValue, DateValue, ListValue, CommentValue, TextValue, or CheckboxValue depending on the control type and then post that data to server. I am able to successfully display each DataDetail.DetailDesc, but for each input, which also renders, the values I enter into the inputs never post back to the server. Here is what my view looks like:
#model Portal.Models.ViewModel.CreateApplicationViewModel
#{
ViewBag.Title = "Step 4/5";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using System.Linq
<h4>Case Data Details</h4>
#using (Html.BeginForm("Step4", "CreateApplication", FormMethod.Post, new { #class = "col-sm-12" }))
{
foreach (var group in Model.Step4.DataDetails.GroupBy(item => item.GroupDesc))
{
<div class="panel panel-primary">
<div class="panel-heading">#Html.Encode(group.Key)</div>
<div class="panel-body">
#for (var i = 0; i < group.Count(); i++)
{
<div class="form-group">
<div class="row">
<div class="col-xs-6">
<label class="form-label">#Model.Step4.DataDetails[i].DetailDesc</label>
</div>
<div class="col-xs-6">
#if (Model.Step4.DataDetails[i].TextFlag == "Y")
{
#Html.TextBoxFor(val => Model.Step4.DataDetails[i].TextValue, new { #class = "form-control" })
}
else if (Model.Step4.DataDetails[i].CheckboxFlag == "Y")
{
#Html.CheckBoxFor(val => Model.Step4.DataDetails[i].CheckboxValue, new { #class = "checkbox" })
}
</div>
</div>
</div>
}
</div>
</div>
}
<div class="col-sm-12">
<div class="row">
#Html.ActionLink("Cancel", "Welcome", "Home", null, new { #class = "btn btn-default" })
<button class="btn btn-default" onclick="history.go(-1);">Previous</button>
<button type="submit" class="btn btn-default">Next</button>
</div>
</div>
Controller to which post data
[HttpPost]
public ActionResult Step4(Step4ViewModel step4)
{
if (ModelState.IsValid)
{
CreateApplicationViewModel model = (CreateApplicationViewModel)Session["case"];
// model.Step4 = step4;
Session["case"] = model;
return View();
}
return View();
}
I was thinking this could be due the grouping, which I do to separate each group into a separate HTML panel element, but my inputs are rendering with the index number in the name. Any help or suggestions on a better way to accomplish this would be greatly appreciated. Cheers!
UPDATE
Here is my updated post controller and view:
#model Portal.Models.ViewModel.CreateApplicationViewModel
#{
ViewBag.Title = "Step 4/5";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using System.Linq
<h4>Case Data Details</h4>
#using (Html.BeginForm("Step4", "CreateApplication", FormMethod.Post, new { #class = "col-sm-12" }))
{
int index = 0;
foreach (var group in Model.Step4.DataDetails.GroupBy(item => item.GroupDesc))
{
<div class="panel panel-primary">
<div class="panel-heading">#Html.Encode(group.Key)</div>
<div class="panel-body">
<input type="hidden" name="Step4.DataDetails.Index" value="#index" />
#for (var i = 0; i < group.Count(); i++)
{
<div class="form-group">
<div class="row">
<div class="col-xs-6">
<label class="form-label">#Model.Step4.DataDetails[i].DetailDesc</label>
</div>
<div class="col-xs-6">
#if (Model.Step4.DataDetails[i].TextFlag == "Y")
{
#Html.TextBoxFor(val => val.Step4.DataDetails[i].TextValue, new { #class = "form-control" })
}
else if (Model.Step4.DataDetails[i].CheckboxFlag == "Y")
{
#Html.CheckBoxFor(val => val.Step4.DataDetails[i].CheckboxValue, new { #class = "checkbox" })
}
</div>
</div>
</div>
}
</div>
</div>
index++;
}
<div class="col-sm-12">
<div class="row">
#Html.ActionLink("Cancel", "Welcome", "Home", null, new { #class = "btn btn-default" })
<button class="btn btn-default" onclick="history.go(-1);">Previous</button>
<button type="submit" class="btn btn-default">Next</button>
</div>
</div>
}
[HttpPost]
public ActionResult Step4(CreateApplicationViewModel step4)
{
if (ModelState.IsValid)
{
CreateApplicationViewModel model = (CreateApplicationViewModel)Session["case"];
// model.Step4 = step4;
Session["case"] = model;
return View();
}
return View();
}
UPDATE 2
I am able to get the form input if I pass a FormCollection to the HttpPost controller. Any ideas as to why I can get these values as a FormCollection but not as the model?
You are posting list of complex objects. But MVC DefaultModelBinder can’t able to bind to your DataDetails object because Index must be in sequence when posting the form with list of complex objects. In your case due to nested for loop, this sequence is broken. So what you can do is take one separate variable and initialize with default 0 value like this - I have tried to modify your code.
#using (Html.BeginForm("Step4", "CreateApplication", FormMethod.Post, new { #class = "col-sm-12" }))
{
int index = 0;
foreach (var group in Model.Step4.DataDetails.GroupBy(item => item.GroupDesc))
{
<div class="panel panel-primary">
<div class="panel-heading">#Html.Encode(group.Key)</div>
<div class="panel-body">
<input type="hidden" name="Step4.DataDetails.Index" value="#index" />
#for (var i = 0; i < group.Count(); i++)
{
<div class="form-group">
<div class="row">
<div class="col-xs-6">
<label class="form-label">#Model.Step4.DataDetails[i].DetailDesc</label>
</div>
<div class="col-xs-6">
#if (Model.Step4.DataDetails[i].TextFlag == "Y")
{
#Html.TextBoxFor(val => val.Step4.DataDetails[i].TextValue, new { #class = "form-control" })
}
else if (Model.Step4.DataDetails[i].CheckboxFlag == "Y")
{
#Html.CheckBoxFor(val => val.Step4.DataDetails[i].CheckboxValue, new { #class = "checkbox" })
}
</div>
</div>
</div>
}
</div>
</div>
index++;
}
<div class="col-sm-12">
<div class="row">
#Html.ActionLink("Cancel", "Welcome", "Home", null, new { #class = "btn btn-default" })
<button class="btn btn-default" onclick="history.go(-1);">Previous</button>
<button type="submit" class="btn btn-default">Next</button>
</div>
</div>
}
Look at the hidden field i have added in the view. That do the trick to post your data even with the broken sequences. Hope this help you.
I was able to get the model back to the controller by taking the idea of using an index integer and incrementing it from the answer above and implementing the idea in a different way in my view:
#model Portal.Models.ViewModel.CreateApplicationViewModel
#{
ViewBag.Title = "Step 4/5";
Layout = "~/Views/Shared/_Layout.cshtml";
}
#using System.Linq
<h4>Case Data Details</h4>
#using (Html.BeginForm("Step4", "CreateApplication", FormMethod.Post, new { #class = "col-sm-12" }))
{
int index = 0;
foreach (var group in Model.Step4.DataDetails.GroupBy(item => item.GroupDesc))
{
<div class="panel panel-primary">
<div class="panel-heading">#Html.Encode(group.Key)</div>
<div class="panel-body">
#for (var i = 0; i < group.Count(); i++)
{
<div class="form-group">
<div class="row">
<div class="col-xs-6">
<label class="form-label">#Model.Step4.DataDetails[i].DetailDesc</label>
</div>
<div class="col-xs-6">
#Html.TextBoxFor(val => val.Step4.DataDetails[index].TextValue)
#Html.HiddenFor(val => val.Step4.DataDetails[index].GroupCode)
</div>
</div>
</div>
index++;
}
</div>
</div>
}
<div class="col-sm-12">
<div class="row">
#Html.ActionLink("Cancel", "Welcome", "Home", null, new { #class = "btn btn-default" })
<button class="btn btn-default" onclick="history.go(-1);">Previous</button>
<button type="submit" class="btn btn-default">Next</button>
</div>
</div>
}
The above code in the view gives me the proper index of every element and allows me to post

Validation in ViewModel not working

I have ViewModel which contains some proprty of class. Code below.
public class ViewModel
{
public Doctor VmDoctor { get; set; }
public Patient VmPatient { get; set; }
public List<Visit> VmVisit { get; set; }
public List<Hours> hours { get; set; }
public List<Hours> hours2 { get; set; }
public Schedule schedule { get; set; }
public bool BlockBtn { get; set; }
public Test test { get; set; }
}
In this case important property is Patient VmPatient. This is a model which has been generated by Database Model First. He has validation.Code below.
public partial class Patient
{
public Patient()
{
this.Visits = new HashSet<Visit>();
}
public int PatientID { get; set; }
[Required(ErrorMessage = "Podaj imię.")]
public string name { get; set; }
[Required(ErrorMessage = "Podaj nazwisko.")]
public string surname { get; set; }
[Required(ErrorMessage = "Podaj pesel.")]
[RegularExpression(#"^\(?([0-9]{11})$", ErrorMessage = "Nieprawidłowy numer pesel.")]
public string pesel { get; set; }
[Required(ErrorMessage = "Podaj miasto.")]
public string city { get; set; }
[Required(ErrorMessage = "Podaj kod pocztowy.")]
public string zipCode { get; set; }
[Required(ErrorMessage = "Podaj e-mail.")]
[EmailAddress(ErrorMessage = "Nieprawidłowy adres e-mail")]
public string email { get; set; }
[Required(ErrorMessage = "Podaj telefon komórkowy.")]
[RegularExpression(#"^\(?([0-9]{9})$", ErrorMessage = "Nieprawidłowy numer telefonu.")]
public string phone { get; set; }
public virtual ICollection<Visit> Visits { get; set; }
}
and i have Main Index where return my ViewModel because, display two Models in the same View. Code below
public ActionResult Index(int id)
{
ViewModel _viewModle = new ViewModel();
schedule = new Schedule();
if(Request.HttpMethod == "Post")
{
return View(_viewModle);
}
else
{
idDr = id;
_viewModle.schedule = schedule;
_viewModle.BlockBtn = _repository.BlockBtn(schedule);
_viewModle.VmDoctor = db.Doctors.Find(idDr);
_viewModle.hours = _repository.GetHours();
foreach (var item in _viewModle.hours)
{
_viewModle.hours2 = _repository.GetButtonsActiv(item.hourBtn, item.count, idDr, schedule);
}
}
if (_viewModle == null)
{
return HttpNotFound();
}
return View(_viewModle);
}
inside View Index i display my objects and rendered partial _FormPatient.Code below.
#model Dentist.Models.ViewModel
<div class="container-select-doctor">
<div class="row">
<div class="text-left">
<div class="row">
<div class="content">
<div class="profileImage">
<div class="imageContener"><img style="margin:1px;" src="#Url.Content("~/Images/" + System.IO.Path.GetFileName(#Model.VmDoctor.image))" /></div>
</div>
<div class="profileInfo">
<div class="profileInfoName">#Model.VmDoctor.name #Model.VmDoctor.surname</div>
<div class="profileInfoSpeciality">#Model.VmDoctor.specialty</div>
</div>
</div>
</div>
</div>
#ViewBag.firstDay<br />
#ViewBag.lastDay<br />
<div class="text-middle">
<div class="content">
<div id="partialZone">
#Html.Partial("_TableSchedule")
</div>
</div>
</div>
<div class="text-right">
<div class="content">
#Html.Partial("_FormPatient")
</div>
</div>
</div>
</div>
and last step is form which has been rendered inside Main Index by #Html.partial.code below
#model Dentist.Models.ViewModel
#using (Html.BeginForm("Create","Patient"))
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(true)
<font color="red">#ViewBag.Pesel</font>
<div class="form-horizontal">
<div class="form-group">
#Html.LabelFor(model => model.VmPatient.email, htmlAttributes: new { #class = "control-label col-md-2" }, labelText: "E-mail:")
<div class="col-md-10">
#Html.TextBoxFor(model => model.VmPatient.email, new { htmlAttributes = new { #class = "form-control" } })
#*<input class="form-control" id="email" name="email" type="text" value="">*#
#Html.ValidationMessageFor(model => model.VmPatient.email, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.VmPatient.phone, htmlAttributes: new { #class = "control-label col-md-2" }, labelText: "Telefon kom.:")
<div class="col-md-10">
#Html.TextBoxFor(model => model.VmPatient.phone, new { maxlength = 9 })
#*<input class="form-control" maxlength="9" id="phone" name="phone" type="text" value="" />*#
#Html.ValidationMessageFor(model => model.VmPatient.phone, "", 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>
}
Please pay attention that this form redirect to another Controller where data will be validate and save to database. Method where data from FORM will be validate and save. code below
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Patient pat)
{
ViewModel vm = new ViewModel();
DentistEntities db = new DentistEntities();
if (ModelState.IsValid)
{
db.Patients.Add(pat);
db.SaveChanges();
}
return RedirectToAction("Index", "Visit", new { id = VisitController.idDr });
}
Conclusion How can i get validation for this form! I have observed that,every time modelstate.isvalid return false.. I dont have any ideas so I would like to ask you for help.
Best regards.
I would suggest that you do this:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(Patient pat)
{
ViewModel vm = new ViewModel();
DentistEntities db = new DentistEntities();
if (ModelState.IsValid)
{
db.Patients.Add(pat);
db.SaveChanges();
}
vm.VmPatient = pat;
return View(vm);
}
Render the view again, but this time the validation error messages should appear on the page (via the ValidationMessageFor() calls in the view). That, at least you can see why the validation has failed.
Alternatively, you could interrogate the modelstate e.g.
foreach (ModelState modelState in ViewData.ModelState.Values) {
foreach (ModelError error in modelState.Errors) {
string error = error.ErrorMessage;
}
}

dependency validation based on dropdown selecion in MVC4

I have a dropdropdown list have values Yes, No. If you select yes I have validate next two textboxes. How can I do dependency validation based on dropdown selection?
public class MeetingAbstract
{
public string HasMaterialPublishedElseWhereLabel { get; set; }
public string HasMaterialPublishedElseWhereOptions { get; set; }
[Required]
public string HasMaterialPublishedElseWhereText { get; set; }
public string DtPublishedTimeId { get; set; }
public string DtPublishedTimeLabel { get; set; }
//validate this based on HasMaterialPublishedElseWhereText =Yes value
public string DtPublishedTimeText { get; set; }
public string PublishedPlaceId { get; set; }
public string PublishedPlaceLabel { get; set; }
//validate this based on HasMaterialPublishedElseWhereText =Yes value
public string PublishedPlaceText { get; set; }
}
view
<div class="row" style="padding-bottom: 10px">
<div class="col-md-10">
<div class="col-md-6">#Html.Label(Model.HasMaterialPublishedElseWhereLabel,
new {#class = "control-label mandatory"})</div>
<div class="col-md-4">
#{
options = Model.HasMaterialPublishedElseWhereOptions;
optionsList = options.Split(',').ToList();
optionSelect = optionsList.Select(option => new SelectListItem()
{Text = option, Value = option}).ToList();
}
#Html.DropDownListFor(model => model.HasMaterialPublishedElseWhereText,
optionSelect, i18n_Models_Abstract.SelectOption, new { #class = "input-
validation-error form-control" })
#Html.ValidationMessageFor(model => model.HasMaterialPublishedElseWhereText,
i18n_Models_Abstract.RequiredField,
new { style = "padding-left: 5px" })
</div>
</div>
</div>
<div class="row" style="padding-bottom: 10px">
<div class="col-md-10">
<div class="col-md-6">#Html.Label(Model.DtPublishedTimeLabel, new {#class =
"control-label mandatory"})</div>
<div class="col-md-4">#Html.TextBoxFor(model => model.DtPublishedTimeText, new \
{#class = "form-control", #placeholder = Model.DtPublishedTimeLabel,
required = "required", maxlength = 40})
#Html.ValidationMessageFor(model => model.DtPublishedTimeText,
i18n_Models_Abstract.RequiredField, new { style = "padding-left: 5px" })</div>
</div>
</div>
<div class="row" style="padding-bottom: 10px">
<div class="col-md-10">
<div class="col-md-6">#Html.Label(Model.PublishedPlaceLabel, new {#class =
"control-label mandatory"})</div>
<div class="col-md-4">#Html.TextBoxFor(model => model.PublishedPlaceText, new
{#class = "form-control", #placeholder = Model.PublishedPlaceLabel,
required = "required", maxlength = 40})
#Html.ValidationMessageFor(model => model.PublishedPlaceText,
i18n_Models_Abstract.RequiredField, new { style = "padding-left: 5px" })
</div>
</div>
</div>
I would add a Validate method on your model which will be called and exposed via ModelState.IsValid:
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) {
if (this.HasMaterialPublishedElseWhereText == "Yes") {
yield return new ValidationResult("This isn't valid! Let me tell you why...");
}
}
Then in your controller simply call:
if (ModelState.IsValid) {
//You can only get this far if your Validate method
//doesn't return any ValidationResult objects.
//Do your magic!
}
Obviously this won't be executed client-side so if validation on the client is a requirement then you might need to roll that in.
One last note is that if you only have two values (e.g. Yes and No) a radio button may make more sense than a drop down.

Categories