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.
Related
I'm trying to understand how to use CheckBoxFor in MVC 5. I'm new to MVC and trying to learn how Entity Framework works using code first migrations.
Here are my main class Request:
[KeyAttribute] //One solution said to add this. Made no difference.
public virtual Guid ID { get; set; }
[Required(ErrorMessage = "First Name is required.")] [Display(Name = "First Name:")] [StringLength(25, ErrorMessage = "First Name must not exceed 25 characters.")]
public virtual string FName { get; set; }
[Display(Name = "Middle Initial:")] [StringLength(1, ErrorMessage = "Middle Initial must not exceed 1 character.")]
public virtual string MI { get; set; }
[Required(ErrorMessage = "Last Name is required.")] [Display(Name = "Last Name:")] [StringLength(25, ErrorMessage = "Last Name must not exceed 25 characters.")]
public virtual string LName { get; set; }
[Required(ErrorMessage = "Date of Birth is required.")] [Display(Name = "Date of Birth:")]
public virtual DateTime DOB { get; set; }
[Required(ErrorMessage = "Email is required.")] [Display(Name = "Email:")] [StringLength(25, ErrorMessage = "Email must not exceed 50 characters.")]
public virtual string Email { get; set; }
[Required(ErrorMessage = "Phone Number is required.")] [Display(Name = "Phone Number")] [StringLength(14, ErrorMessage = "Phone number must not exceed 14 characters.")]
public virtual string Phone { get; set; }
[Required(ErrorMessage = "Phone Type selection is required.")] [Display(Name = "Phone Type:")] [StringLength(4, ErrorMessage = "Phone Type selection must not exceed 4 characters.")]
public virtual string PhoneType { get; set; }
[Required(ErrorMessage = "Preferred Contact Method selection is required.")] [Display(Name = "Preferred Contact Method:")] [StringLength(16, ErrorMessage = "Preferred Contact Method selection must not exceed 16 characters.")]
public virtual PrefContactViewModel PrefContactViewModel {get;set;}
[Display(Name = "Preferred Contact Time:")] [StringLength(50, ErrorMessage = "Preferred Contact Time must not exceed 50 characters.")]
public virtual string PrefContactTime { get; set; }
...
Here is my ViewModel PrefContactViewModel:
public int ID { get; set; }
public string Name { get; set; }
public bool Checked { get; set; }
Here is my controller RequestsController Index Action:
public ActionResult Index()
{
var requests = db.Requests.Include(r => r.PrefContactViewModel);
return View(requests.ToList());
}
Here is the same controller RequestForm Action:
public ActionResult RequestForm()
{
return View();
}
And here is my View:
#model AMYA.Models.Request
<div class="opensans margin-sides">
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
#Html.ValidationSummary(false, "", new { #class = "text-danger" })
<hr />
//FIRST NAME, MIDDLE INITIAL, LAST NAME
<div>
<div class="form-group col-md-5">
#Html.LabelFor(model => model.FName, htmlAttributes: new { #class = "control-label" })<span class="red">*</span>
<div>
#Html.EditorFor(model => model.FName, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group col-md-2">
#Html.LabelFor(model => model.MI, htmlAttributes: new { #class = "control-label" })
<div>
#Html.EditorFor(model => model.MI, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group col-md-5">
#Html.LabelFor(model => model.LName, htmlAttributes: new { #class = "control-label" })<span class="red">*</span>
<div>
#Html.EditorFor(model => model.LName, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
</div>
//DATE OF BIRTH, EMAIL, PHONE NUMBER, PHONE TYPE
<div>
<div class="form-group col-md-3">
#Html.LabelFor(model => model.DOB, htmlAttributes: new { #class = "control-label" })<span class="red">*</span>
<div class="">
#Html.EditorFor(model => model.DOB, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group col-md-4">
#Html.LabelFor(model => model.Email, htmlAttributes: new { #class = "control-label" })<span class="red">*</span>
<div class="">
#Html.EditorFor(model => model.Email, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group col-md-3">
#Html.LabelFor(model => model.Phone, htmlAttributes: new { #class = "control-label" })<span class="red">*</span>
<div class="">
#Html.EditorFor(model => model.Phone, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
<div class="form-group col-md-2">
#Html.LabelFor(model => model.PhoneType, htmlAttributes: new { #class = "control-label" })<span class="red">*</span>
<div class="">
#Html.CheckBoxFor(model => model.PrefContactViewModel.Checked, new { #class = "form-control" })
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
<div class="">
<input type="checkbox" id="PrefContact" name="PrefContact" value="#Request["PrefContact"]" />
#*#Html.EditorFor(model => model.PrefContact, new { htmlAttributes = new { #class = "checkbox" } })*#
#Html.CheckBoxFor(model => model.PrefContactViewModel.Checked, new { #class = "form-control" })
</div>
</div>
</div>
<div class="col-md-6">
<div class="form-group">
#Html.LabelFor(model => model.PrefContactTime, htmlAttributes: new { #class = "control-label" })
<div class="">
#Html.EditorFor(model => model.PrefContactTime, new { htmlAttributes = new { #class = "form-control" } })
</div>
</div>
</div>
...
I just need to create a checkbox for each of the Name fields in my RequestForm Action. I've tried several of the solutions found here and elsewhere, but I cannot get the CheckBoxFor to work.
Can anyone offer me some insight into how to get CheckBoxFor to populate with the four selections? Or is there a way to do this using regular HTML <input> field?
And I get the following in my View:
(Update: I wrote this answer before I realised that the OP was using the List<> where each list item is its own field (i.e. as runtime customizable form builder) instead of representing each form field as a view-model class property, and this answer is not entire suitable for that scenario - but using EditorFor rather than CheckBoxFor should still work in this case).
You can't use an IEnumerable<T> as the static-type for a View's #model because ASP.NET model-binding (when repopulating the Model from the POST request body) works with List<T>, not IEnumerable<T>.
To use model-binding with a List<T> you need to use a for( int i = 0; i < list.Count; i++ )-style loop, you cannot use a foreach-style loop because it doesn't expose the item's index for use with the List<T>'s indexer property (okay, you can technically use foreach with .Select( (e, idx) => ... ) but it only results in more unproductive keyboard typing.
Change your ViewModel to its own class rather than directly exposing a List<T> anyway, because it makes everything else easier (such as adding a page title or other non-list content to the page).
Use a for() loop to render each item.
Your #model line must match the actual model object passed into View(Object).
Like so:
Controller action:
public class MyViewModel
{
// this is an example of an additional non-list-item property you'd add to the viewmodel:
[BindNever] // this is a one-way (server-to-client) property, so use [BindNever]
public String PageTitle { get; internal set; }
[Required]
public List<PrefContactViewModel> MyList { get; set; }
}
public ActionResult RequestForm()
{
MyViewModel vm = new MyViewModel()
{
PageTitle = "My list page!",
MyList = new List<PrefContactViewModel>
{
new PrefContactViewModel { ID = 1, Name = "Call", Checked = false },
new PrefContactViewModel { ID = 2, Name = "Text", Checked = false },
new PrefContactViewModel { ID = 3, Name = "Email", Checked = false },
new PrefContactViewModel { ID = 4, Name = "Traditional Mail", Checked = false },
}
};
return View( vm );
}
Your view:
#model MyViewModel
<div class="opensans margin-sides">
#using (Html.BeginForm()) {
<h1>#this.Model.PageTitle</h1>
#Html.AntiForgeryToken()
#Html.ValidationSummary(false, "", new { #class = "text-danger" })
<hr />
#for( int i = 0; i < this.Model.MyList.Count; i++ ) {
<!-- HTML markup removed for readability -->
#Html.LabelFor( m => m.MyList[i].FName )
#Html.TextBoxFor( m => m.MyList[i].FName )
#Html.ValidationMessageFor( m => m.MyList[i].FName )
#** Checkboxes should be wrapped in a <label> rather than using a separate label so you can't use #LabelFor **#
<label>#Html.CheckBoxFor( m => m.MyList[i].Checked ) #Html.DisplayNameFor( m => m.MyList[i].Checked )</label>
#Html.ValidationMessageFor( m => m.MyList[i].Checked )
#** etc for other members of each list item **#
} #** for loop **#
} #** </form> **#
I have this Issue class. Is there
public class Issue
{
public int IssueId { get; set; }
[Required]
public string ReporterName { get; set; }
[Required]
public string Description { get; set; }
public int? IssueTypeId { get; set; }
public virtual IssueType type { get; set; }
}
Then this is the view I have so I can change the value of the IssueTypeId. However when I try to save it in the database with that code in the controller, I am having an error saying that the ReporterName and Description are still required. How can I only update one specific value in database, in this case IssueTypeId?
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Issue</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
#Html.HiddenFor(model => model.IssueId)
<div class="form-group">
#Html.LabelFor(model => model.IssueTypeId, "IssueTypeId", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("IssueTypeId", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.IssueTypeId, "", 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>
}
My code in controller
public ActionResult AssignFixer(int issueId, int issueTypeId)
{
var issue = new Issue { IssueId = issueId , IssueTypeId= issueTypeId};
using (var newContext = new SystemContext())
{
newContext.Issues.Attach(issue);
newContext.Entry(issue).Property(i => i.IssueTypeId).IsModified = true;
newContext.SaveChanges();
}
return View(issue);
}
Instead of attaching the issue in newContext.Issues. First get the instance and then update it. Like:
var existingIssue = newContext.Issues.Where(i => i.IssueId == issueId).FirstOrDefault();
existingIssue.IssueTypeId = issueTypeId;
newContext.SaveChanges();
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.
I got two model classes like this
public class JobViewModel
{
public int Id { get; set; }
public float Price { get; set; }
public int JobSubCategoryId { get; set; }
public string jobDescription { get; set; }
public List<Machine> Machines { get; set; }
public int SpecialRequirementId { get; set; }
}
public class Machine
{
public int Id { get; set; }
public string Name { get; set; }
public string Type { get; set; }
public string Brand { get; set; }
}
Now, When a user creates a new "Job" he must be able to create multiple "Machine" with it. How can I make this possible inside my create view?
As well as Stephen's method, you can achieve all this relatively simply, without partial views.
In the first instance, you should revise your model a little. Add to Machine:
// Foreign key
public int JobID { get; set; }
// Navigation properties
public virtual Job Job{ get; set; }
The Job model, you have not shown, but it needs to be:
public class Job
{
public int Id { get; set; }
public float Price { get; set; }
public int JobSubCategoryId { get; set; }
public string JobDescription { get; set; }
public int SpecialRequirementId { get; set; }
public virtual List<Machine> Machines { get; set; }
}
Here is my complete JobViewModel:
public class JobViewModel
{
public JobViewModel()
{
Machines = new List<Machine>();
}
public int Id { get; set; }
public float Price { get; set; }
public int JobSubCategoryId { get; set; }
public string JobDescription { get; set; }
public int SpecialRequirementId { get; set; }
public List<Machine> Machines { get; set; }
public string NewMachineBrand { get; set; }
public string NewMachineType { get; set; }
public string NewMachineName { get; set; }
public void AddMachine()
{
Machine tmp = new Machine { Brand = NewMachineBrand, Type = NewMachineType, Name = NewMachineName };
Machines.Add(tmp);
NewMachineBrand = NewMachineType = NewMachineName = null;
}
public Job GetJob()
{
Job job = new Job();
job.JobDescription = JobDescription;
job.Price = Price;
job.JobSubCategoryId = JobSubCategoryId;
job.SpecialRequirementId = SpecialRequirementId;
job.Machines = new List<Machine>();
foreach (Machine m in Machines)
{
job.Machines.Add(m);
}
return job;
}
}
When creating your create view based on JobViewModel, you will need to add two things that are not defaulted for you, firstly a table to hold the new Machines, and secondly a button to add each machine in turn.
My complete create.cshtml view looks like this:
#model JobMachinesMVC.Models.JobViewModel
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
<div class="form-horizontal">
<h4>Job</h4>
<hr />
#Html.ValidationSummary(true, "", new { #class = "text-danger" })
<div class="form-group">
#Html.LabelFor(model => model.Price, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Price, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.Price, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.JobSubCategoryId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.JobSubCategoryId, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.JobSubCategoryId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.JobDescription, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.JobDescription, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.JobDescription, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.SpecialRequirementId, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.SpecialRequirementId, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.SpecialRequirementId, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.NewMachineBrand, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.NewMachineBrand, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.NewMachineBrand, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.NewMachineType, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.NewMachineType, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.NewMachineType, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
#Html.LabelFor(model => model.NewMachineName, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.NewMachineName, new { htmlAttributes = new { #class = "form-control" } })
#Html.ValidationMessageFor(model => model.NewMachineName, "", new { #class = "text-danger" })
</div>
</div>
<div class="form-group">
<table>
<thead>
<tr>
<th style="text-align:right">
#Html.DisplayNameFor(model => model.Machines.FirstOrDefault().Brand)
</th>
<th style="text-align:right">
#Html.DisplayNameFor(model => model.Machines.FirstOrDefault().Name)
</th>
<th style="text-align:right">
#Html.DisplayNameFor(model => model.Machines.FirstOrDefault().Type)
</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.Machines.Count; i++)
{
<tr>
<td style="text-align:right">#Html.HiddenFor(m => m.Machines[i].Id)#Html.DisplayFor(m => m.Machines[i].Brand)#Html.HiddenFor(m => m.Machines[i].Brand)</td>
<td style="text-align:right">#Html.DisplayFor(m => m.Machines[i].Name)#Html.HiddenFor(m => m.Machines[i].Name)</td>
<td style="text-align:right">#Html.DisplayFor(m => m.Machines[i].Type)#Html.HiddenFor(m => m.Machines[i].Type)</td>
</tr>
}
</tbody>
</table>
</div>
<div class="form-group">
<input type="submit" value="Add Machine" name="addmachine" class="btn btn-default" />
</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")
}
A couple of things to note here. I always include a #Html.HiddenFor in such a sub-table, because #Html.DisplayFor items can be lost when posting back to the controller. Secondly, there are two input type="submit" on the same View. One is given a name attribute. This is so that the Controller can distinguish between the two clicks.
The relevant lines from my controller are these:
// GET: Jobs/Create
public ActionResult Create()
{
JobViewModel job = new JobViewModel();
return View(job);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(JobViewModel jobvm)
{
if (Request.Form["addmachine"] != null)
{
jobvm.AddMachine();
ModelState.Remove("NewMachineName");
ModelState.Remove("NewMachineType");
ModelState.Remove("NewMachineBrand");
return View(jobvm);
}
if (ModelState.IsValid)
{
Job job = jobvm.GetJob();
db.Jobs.Add(job);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(jobvm);
}
If "addmachine" is clicked, the new machine values get added to the Machines List, get reset and the form is redisplayed. Note you need to set the ModelState even though the ViewModel sets the values to null, otherwise you old values persist in the view. If Create is clicked, the model is checked for ValidState, and the job is saved. What about the Machine table? Because the models are set up as outlined above, internally MVC knows that it has to save the values to Machine as well.
Please note that the above illustration is very crude. I have applied no styling apart from that which you get "out of the box". You will want to tidy this up (a lot!), but I hope I have given you a good start in one way to approach this problem.
I have a simple example.
Two class. User and Company like :
public class User() {
public int UserID { get; set; }
[Display(Name = "User name")]
public string Name { get; set; }
[Display(Name = "Company")]
public int CompanyID { get; set; }
public virtual Company Company { get; set; }
}
public class Company() {
public int CompanyID { get; set; }
[Display(Name = "Company")]
public string Name { get; set; }
public virtual ICollection<User> Users { get; set; }
}
My problem is in the Create and the Edit views of the User.
The label for "Name" is displayed correctly in "User name" but the label for "CompanyID" stay displayed at "CompanyID" (the drop down list is created correctly with all Companies). I want the label display "Company" like I make it in the class.
I've try to change my view but all I do block compilation so I'm lost.
I'm begginer in MVC so excuse me if it easy to do but I don't see it.
Edit (add Create View code) :
#model Intranet3.Models.User
#{
ViewBag.Title = "Add a user";
}
#using (Html.BeginForm(null, null, FormMethod.Post, htmlAttributes: new { #class = "form-horizontal form-bordered" })) {
#Html.AntiForgeryToken()
<div class="row-fluid">
<div class="span12">
<div class="box">
<div class="box-title">
<h3>
<i class="icon-table"></i>
New
</h3>
</div>
<div class="box-content nopadding">
<div class="form-horizontal">
#Html.MyValidationSummary()
<div class="control-group #Html.ClassErrorFor(model => model.Name)">
#Html.LabelFor(model => model.Name, new { #class = "control-label" })
<div class="controls">
#Html.EditorFor(model => model.Name)
#Html.MyValidationMessageFor(model => model.Name)
</div>
</div>
<div class="control-group #Html.ClassErrorFor(model => model.CompanyID)">
#Html.LabelFor(model => model.CompanyID, "CompanyID", new { #class = "control-label" })
<div class="controls">
#Html.DropDownList("CompanyID", String.Empty)
#Html.MyValidationMessageFor(model => model.CompanyID)
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Create</button>
<button onclick="location.href='#Url.Action("Index","User")'" type="button" class="btn">Cancel</button>
</div>
</div>
</div>
</div>
</div>
</div>
}
Edit 2 :
Problem solved by delete the string force in Labels.
So this :
#Html.LabelFor(model => model.CompanyID, "CompanyID", new { #class = "control-label" })
Need to be
#Html.LabelFor(model => model.CompanyID, new { #class = "control-label" })
Why have u passed parameter CompanyId
#Html.LabelFor(model => model.CompanyID, "CompanyID", new { #class = "control-label" })
Should be
#Html.LabelFor(model => model.CompanyID, new { #class = "control-label" })
#Html.TextBoxFor(c => c.CompanyID, new { data_bind = "text:Contacts.FirstName", #class = "form-control" })
If you have knockoutjs binding