How can i take many textboxes' value on Post in MVC - asp.net-mvc

i have a problem about MVC, but first I am sorry for my english :D .
Now i am trying to make a form for users and i have a critical issue when i want connect to values with database.
My Form is like this : https://i.hizliresim.com/vJ6r2p.png
Models :
[Table("Testers")]
public class Testers
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[StringLength(50),Required]
public string testerName { get; set; }
public ICollection<Scores> Scores { get; set; }
}
[Table("Technologies")]
public class Technologies
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[StringLength(50)]
public string technologyName { get; set; }
[StringLength(50)]
public string type { get; set; }
}
[Table("Scores")]
public class Scores
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
[DefaultValue(0)]
public int score { get; set; }
public virtual Testers tester { get; set; }
public virtual Technologies technology { get; set; }
}
ViewModels:
public class TechnologiesView
{
public List<Technologies> Technologies { get; set; }
public Scores Scores { get; set; }
}
Controller :
public ActionResult Page2()
{
TechnologiesView allTechs = new TechnologiesView();
allTechs.Technologies = db.Technologies.ToList();
return View(allTechs);
}
View:
#model TechnologiesView
#{
ViewBag.Title = "Page2";
}
<style>
#lang {
font-size: 15px;
color: gray;
}
#tech {
font-size: 13px;
color: gray;
}
</style>
<div class="container">
<div class="row col-xs-12 bilgi" style="color:black">
#HelperMethods.Title("Kendini Skorla!")
<br />
<i>Bilgi Düzeyini 0 ile 5 puan arasında notlar mısın? (0=Hiç 5= İleri Seviye)</i>
</div>
</div>
<hr />
#using (Html.BeginForm())
{
<div class="container-fluid" style="padding-left:50px; margin:0px">
<div class="row" id="lang">
#foreach (Technologies techs in Model.Technologies)
{
if (techs.type == "lang")
{
<div class="col-md-1 col-sm-2 col-xs-6">
#(techs.technologyName)
</div>
<div class="col-md-1 col-sm-2 col-xs-6">
(#(Html.TextBoxFor(x => x.Scores.score, new
{
id = techs.ID,
name = "techID",
style = "display:inline; width:20px; height:20px; font-size:smaller; padding:0px; text-align:center",
#class = "form-control"
})))
</div>
}
}
</div>
<hr style="color:black" />
<div class="row" id="tech">
#foreach (Technologies techs in Model.Technologies)
{
if (techs.type == "tech")
{
<div class="col-md-1 col-sm-2 col-xs-6" id="tech">
#(techs.technologyName)
</div>
<div class="col-md-1 col-sm-2 col-xs-6">
#Html.HiddenFor(x=>techs.ID)
(#(Html.TextBoxFor(x => x.Scores.score, new
{
id = techs.ID,
name = "techID",
style = "display:inline; width:20px; height:20px; font-size:smaller; padding:0px; text-align:center",
#class = "form-control"
})))
</div>
}
}
</div>
<hr />
<div class="row col-xs-12" id="lang">
<span>Kullandığınız IDE’ler (yazınız)</span>
<br />
<div style="margin-bottom:10px; text-align:center">
#HelperMethods.TextArea("Ide", 3)
</div>
</div>
<div style="text-align:right; margin-bottom:10px">
#HelperMethods.Button("btnPage2")
</div>
</div>
}
Now user has to give a score to him/herself for every technologies or languages and after this i want to when user click to button "Follow the next page(it's turkish)" i will select the last saved user from maxID value in Testers and i have to connect scores with technologies and testers but i don't know how can i get textboxes' values and which technology's value is this value on post :D

You generating form controls which have no relationship at all to your model (which is also wrong anyway). Never attempt to change the name attribute when using the HtmlHelper methods (and there is no reason to change the id attribute either)
Next, you cannot use a foreach loop to generate form controls for a collection. You need a for loop or EditorTemplate to generate the correct name attributes with indexers. Refer this answer for a detailed explanation.
Then you cannot use a if block inside the loop (unless you include a hidden input for the collection indexer), because by default the DefaultModelBinder required collection indexers to start at zero and be consecutive.
First start by creating view models to represent what your want to display/edit in the view.
public class ScoreVM
{
public int ID { get; set; }
public string Name { get; set; }
public int Score { get; set; }
}
public class TechnologiesVM
{
public List<ScoreVM> Languages { get; set; }
public List<ScoreVM> Technologies { get; set; }
public string Notes { get; set; } // for your textarea control
}
Note you will probably want to add validation attributes such as a [Range] attribute for the Score property
In the GET method, initialize and populate your view model and pass it to the view
public ActionResult Page2()
{
IEnumerable<Technologies> technologies = db.Technologies;
TechnologiesVM model = new TechnologiesVM
{
Languages = technologies.Where(x => x.type == "lang")
.Select(x => new ScoreVM{ ID = x.ID, Name = x.technologyName }).ToList(),
Technologies = technologies.Where(x => x.type == "tech")
.Select(x => new ScoreVM{ ID = x.ID, Name = x.technologyName }).ToList(),
};
return View(model);
}
and in the view
#model TechnologiesVM
....
#using (Html.BeginForm())
{
....
#for (int i = 0; i < Model.Languages.Count; i++)
{
#Html.HiddenFor(m => m.Languages[i].ID)
#Html.HiddenFor(m => m.Languages[i].Name)
#Html.LabelFor(m => m.Languages[i].Score, Model.Languages[i].Name)
#Html.TextBoxFor(m => m.Languages[i].Score)
#Html.ValidationMessageFor(m => m.Languages[i].Score)
}
#for (int i = 0; i < Model.Languages.Count; i++)
{
.... // repeat above
}
#Html.LabelFor(m => m.Notes)
#Html.TextAreaFor(m => m.Notes)
#Html.ValidationMessageFor(m => m.Notes)
<input type="submit" />
}
and the POST method will be
public ActionResult Page2(TechnologiesVM model)
{
if (!ModelState.IsValid)
{
return View(model);
}
... // save the data and redirect
}

Related

MVC Razor - Hierarchy / Nested IList of Checkboxes Posting with Count = 0

I am trying to post back the changes to the nested list of checkboxes for Groups and their Users but keep getting my list Count = 0 when it posts. Right now, there are no groups within groups, but I would still like to make this recursive if we move towards that in the future.
I have a hierarchical IList of GroupsUsers attached to my Activity Model as such:
Activity:
public class Activity
{
public int ActivityId { get; set; }
public string Name { get; set; }
public string Path { get; set; }
public string Description { get; set; }
public Nullable<int> ParentId { get; set; }
public IList<GroupsUsers> Hierarchy { get; set; }
}
GroupsUsers:
public class GroupsUsers
{
public Guid? Guid { get; set; }
public string Name { get; set; }
public bool IsAllowed { get; set; } = false;
public IList<GroupsUsers> Children { get; set; } = new List<GroupsUsers>();
}
I have tried EditorFor, Partial View, and Helper but am having no luck with any of them posting back the Hierarchy. My Model.Hierarchy is posting back with Count = 0.
Here's my current attempt:
Main View (watered down):
#model MyProject.Models.Activity
#using (Html.BeginForm())
{
<!-- Activity stuff -->
<ul style="list-style:none;">
#for (var i = 0; i < Model.Hierarchy.Count(); i++)
{
#Html.EditorFor(model => model.Hierarchy[i])
}
</ul>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
}
Current Attempt. GroupsUsers.cshtml:
#model MyProject.Models.GroupsUsers
<li>
#Html.HiddenFor(model => model.Guid)
#Html.HiddenFor(model => model.Name)
#Html.CheckBoxFor(model => model.IsAllowed, new { #class = "groupsusers-checkbox", #style = "margin-right:5px; cursor:pointer;", #value = Model.Guid.ToString() }) #Html.LabelFor(model => model.IsAllowed, Model.Name, new { #class = "build-checkbox-label", #style = "font-weight:normal; margin-top:-2px;" })
#if (Model.Children.Any())
{
<ul style="list-style:none;">
#for (var i = 0; i < Model.Children.Count(); i++)
{
#Html.EditorFor(model => model.Children[i])
}
</ul>
}
</li>
I'm looking for my list of checkboxes to display as a list hierarchy recursively and post Model.Hierarchy back properly.
Any help would be appreciated... I only included Attempt #2 and #3 in case I was close to having it correct.

if value null in radio button list does not show radio button mvc4

I have in model having the questions and answer list. In some of the questions having 4 options and some of the questions having the 2 options.
I am getting the options from the back end and i'm binding from model class into the razor view.
Now if the answer value null means radio button is showing but i don't want to radio button if the radio button option value nulls.
#for (int j = 0; j < Model.Count; j++)
{
i++;
<div class="panel-heading online-test" role="tab" id="">
<h5 class="panel-title">
#Html.HiddenFor(m => m[j].ID)
#Html.HiddenFor(m => m[j].SkillId)
#i) #Html.DisplayFor(m => m[j].QuestionText)
</h5>
</div>
foreach (var k in Model[j].Options)
{
<div class="panel-collapse">
<div class="panel-body online-test-options">
<div class="input-group">
<div class="col-lg-12" id="radiolist">
#Html.RadioButtonFor(m => m[j].SelectedAnswer, "A")
<label for="#k.AnswerId">#k.Option1</label>
</div>
<div class="col-lg-12">
#Html.RadioButtonFor(m => m[j].SelectedAnswer, "B")
<label for="#k.AnswerId">#k.Option2</label>
</div>
<div class="col-lg-12">
#Html.RadioButtonFor(m => m[j].SelectedAnswer, "C")
<label for="#k.AnswerId">#k.Option3</label>
</div>
<div class="col-lg-12">
#Html.RadioButtonFor(m => m[j].SelectedAnswer, "D")
<label for="#k.AnswerId">#k.Option4</label>
</div>
</div>
</div>
</div>
}
}
public class QuestionsModel
{
public decimal ID { set; get; }
public decimal SkillId { get; set; }
public string QuestionText { set; get; }
public List<Answer> Options { set; get; }
public string SelectedAnswer { set; get; }
public QuestionsModel()
{
Options = new List<Answer>();
}
}
public class Answer
{
public int AnswerId { get; set; }
public string Option1 { set; get; }
public string Option2 { set; get; }
public string Option3 { set; get; }
public string Option4 { set; get; }
}
public class Evaluation
{
public List<QuestionsModel> Questions { set; get; }
public Evaluation()
{
Questions = new List<QuestionsModel>();
}
}
Please help to solve..
Thanks in Advance.

Creating an MVC form using partial views each with complex models not binding to main model

I'm working on a form that has a main model being passed to the view. The model has sub-models within it, with partial views to render that content. The problem is that when I fill out the form, only those parameters on the main form get bound back to the model when the form is submitted.
I tried changing the Html.RenderPartial to a Html.EditorFor, and while it fixed my model binding problem, it removed all of my html formatting from the partial view.
Is there a way I can either bind my partial view elements to the main form model, or keep the html structure of my partial view using EditorFor?
Below is my code (I chopped out a bunch of stuff - especially from my main view - to try to simplify what I'm looking for).
This is my model:
public class ShipJobs
{
public String Job { get; set; }
public String Quote { get; set; }
public String PartName { get; set; }
public String Rev { get; set; }
public String Customer { get; set; }
public String CustomerName { get; set; }
public String TrackingNumber { get; set; }
public Int32 ShippedQuantity { get; set; }
public Boolean Certs { get; set; }
public Double ShippingCharges { get; set; }
public DateTime ShipDate { get; set; }
public String SelectedFreightTerms { get; set; }
public IEnumerable<SelectListItem> FreightTerms { get; set; }
public String SelectedContact { get; set; }
public IEnumerable<SelectListItem> Contacts { get; set; }
public String SelectedShipVia { get; set; }
public IEnumerable<SelectListItem> ShipVia { get; set; }
public Models.GreenFolders.Address Address { get; set; }
}
public class Address
{
public AddressType Type { get; set; }
public String ShipToId { get; set; }
public String ContactName { get; set; }
public String AddressName { get; set; }
public String Line1 { get; set; }
public String Line2 { get; set; }
public String City { get; set; }
public String State { get; set; }
public String Zip { get; set; }
public String Phone { get; set; }
public SelectList ShipToAttnDropDown { get; set; }
public IEnumerable<SelectListItem> ShipToDropDown { get; set; }
}
Controller:
public ActionResult ShipJobs(String Job, Models.Shipping.ShippingModel.ShipJobs Packlist, Models.GreenFolders.Address ShipAddress, String Submit = "")
{
var Model = new Models.Shipping.ShippingModel.ShipJobs();
if (Submit == "loadjob")
{
var shippingHelper = new BLL.Shipping.ShippingMethods(_company);
Model = shippingHelper.GetShipJobModel(Job);
Model.Address = shippingHelper.GetShipAddress(Job);
}
else if (Submit == "createpacklist")
{
}
ViewBag.Company = _company.ToString();
return View(Model);
}
Main View:
#model Models.Shipping.ShippingModel.ShipJobs
#{
ViewBag.Title = "ShipJobs";
String Company = ViewBag.Company.ToString();
}
#using (Html.BeginForm("ShipJobs", "Shipping", FormMethod.Post, new { Class = "form-horizontal" }))
{
<div class="row">
<div class="col-md-6">
<!-- Basic Form Elements Block -->
<div class="block">
<!-- Basic Form Elements Title -->
<div class="block-title">
<h2>Load <strong>Job</strong></h2>
</div>
<!-- END Form Elements Title -->
<!-- Basic Form Elements Content -->
#using (Html.BeginForm("ShipJobs", "Shipping", FormMethod.Post, new { Class = "form-horizontal form-bordered" }))
{
<div class="form-group">
<label class="col-md-3 control-label" for="example-text-input">Job Number</label>
<div class="col-md-9">
#Html.TextBoxFor(model => model.Job, new { id = "example-text-input", Name = "Job", Class = "form-control" })
</div>
</div>
<div class="form-group form-actions">
<div class="col-md-9 col-md-offset-3">
<button type="submit" class="btn btn-sm btn-primary" name="submit" value="loadjob"><i class="fa fa-angle-right"></i> Load Job Info</button>
<button type="reset" class="btn btn-sm btn-warning"><i class="fa fa-repeat"></i> Reset</button>
</div>
</div>
}
</div>
</div>
<div class="col-md-6">
#if (Model.Address != null && Model.Address != null)
{
#Html.EditorFor(model => model.Address)
//Html.RenderPartial("../Shared/_Address", Model.ShipInfo);
}
</div>
#Html.HiddenFor(model => model.Quote)
#Html.HiddenFor(model => Company)
</div>
}
Partial view:
#model Models.GreenFolders.Address
<!-- Block -->
<div class="block">
<div class="block-title">
#if(Model.Type == Models.GreenFolders.AddressType.Shipping)
{
<h2 style="float: right; margin-top: -9px; margin-right: -10px;">
<div class="dropdown shiptoddl">
<button class="btn btn-default dropdown-toggle" type="button" id="shiptoddl" data-toggle="dropdown" aria-expanded="true">
#Model.ShipToDropDown.Where(x => x.Selected).FirstOrDefault().Text
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu" aria-labelledby="dropdownMenu1">
#foreach (SelectListItem selectlistitem in Model.ShipToDropDown)
{
<li role="presentation"><a role="menuitem" tabindex="-1" href="#" data-value="#selectlistitem.Value" data-selected="#selectlistitem.Selected">#selectlistitem.Text</a></li>
}
</ul>
</div>
#*#Html.DropDownList("shiptoddl", (SelectList)Model.ShipToDropDown, new { #class = "shiptoddl", id = "shiptoddl" })*#
</h2>
}
<h4><strong>#Model.Type.ToString()</strong> Address</h4>
</div>
#{ Html.RenderPartial("../Shared/_AddressDetails", Model); }
</div>
<!-- END Block -->

MVC model not bound to collection in POST

Visual Studio 2012
MVC 5.2.0
jQuery 2.1.1
jQuery UI Combined 1.10.4
Json.Net 6.0.3
Kendo UI MVC 2014.1.528
I've read through many similar posts. I've tried to apply what I've seen to no avail. I throw myself at feet of your keyboards for your mercy and help.
I have a survey-like site, so for simplicity here, I made up a sample project to duplicate the issue. I can upload that too.
I have a model with a child object of Address - no problemo there - it binds.
The model also has a collection of questions. It never binds on the post, and this is the issue here.
Lets start with models:
The Survey itself:
public class Survey
{
[ScaffoldColumn(false)]
public int Id { get; set; }
public string Name { get; set; }
[DisplayName("Phone #")]
[StringLength(15)]
[DataType(DataType.PhoneNumber)]
public string ContactMethodPhone { get; set; }
[DisplayName("Email")]
[StringLength(120)]
[EmailAddress]
public string ContactMethodEmail { get; set; }
public virtual Address Address { get; set; }
public virtual List<Question> Questions { get; set; }
}
and the address:
public class Address
{
[ScaffoldColumn(false)]
public int AddressId { get; set; }
[DisplayName("Address line 1")]
[Required]
[StringLength(200)]
public string Street { get; set; }
[DisplayName("Address line 2")]
[StringLength(200)]
public string Street2 { get; set; }
[DisplayName(" ")]
[StringLength(50)]
public string ApartmentNum { get; set; }
[DisplayName("Type")]
[StringLength(50)]
public string Tenement { get; set; }
[DisplayName("City")]
[Required]
[StringLength(200)]
public string City { get; set; }
[DisplayName("Province/State")]
[StringLength(20)]
public string State { get; set; }
[Required]
[DisplayName("Postal/Zip Code")]
[StringLength(10)]
public string MailCode { get; set; }
[DisplayName("Country")]
[StringLength(30)]
public string Country { get; set; }
[NotMapped]
public List<SelectListItem> Cities { get; set; }
[NotMapped]
public List<SelectListItem> Provinces { get; set; }
[NotMapped]
public List<SelectListItem> Countries { get; set; }
[NotMapped]
public List<SelectListItem> MailCodes { get; set; }
[NotMapped]
public List<SelectListItem> Tenements { get; set; }
}
and the questions:
public class Question
{
[ScaffoldColumn(false)]
public int Id { get; set; }
[ScaffoldColumn(false)]
public int ImageID { get; set; } // Question Image
[DisplayName("Question: ")]
public string InformationIntakeGroupValue { get; set; } // Question: ie Did you graguate high school
public int ImageID_Topic { get; set; } // Topic Image
[DisplayName("Topic: ")]
public string InformationIntakeTopicValue { get; set; } // Topic: ie Education
[ScaffoldColumn(false)]
public string InformationIntakeTypeCode { get; set; } // Type of question (date, bool, text)
// below not filled by select;
// the key from the EntityAffilliateIntake record insert
public int PersonId { get; set; } // Person anwering question
// this is the user response area
[DisplayName("Answer: ")]
public string InformationIntakeValue { get; set; }
[DisplayName("Choice: ")]
public string InformationIntakeValueBool { get; set; }
[DisplayName("Date: ")]
public DateTime InformationIntakeValueDate { get; set; }
[ForeignKey("Survey")]
public int SurveyId { get; set; }
public virtual Survey Survey { get; set; }
}
(Note: fyi, I've tried the models without foreign keys as well - but perhaps it's not defined correctly)
The controller :
// GET: /Inquiry/Create
public ActionResult Create()
{
var geoIpData = Strings._download_serialized_json_data<GeoData>(StringConstants.UrlForGeoService);
SurveyModel = new Survey
{
Id=1,
Address = new AddressController().GetAddressModel(geoIpData.country, geoIpData.regionName, geoIpData.city, ""),
Questions = new QuestionController().GetQuestions(1).ToList()
};
return View(SurveyModel);
}
// POST: /Inquiry/Create
// 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]
public ActionResult Create([Bind(Include = "Id, Name, ContactMethodPhone, ContactMethodEmail, Address, Questions")] Survey survey)
{
if (ModelState.IsValid)
{
int i = 0;
}
if (survey.Address.Cities == null)
{
survey.Address.Cities = SurveyModel.Address.Cities;
survey.Address.Countries = SurveyModel.Address.Countries;
survey.Address.MailCodes = SurveyModel.Address.MailCodes;
survey.Address.Provinces = SurveyModel.Address.Provinces;
survey.Address.Tenements = SurveyModel.Address.Tenements;
}
if (survey.Questions == null)
{
survey.Questions = SurveyModel.Questions;
}
return View(survey);
}
The view:
#model BindCollection.Models.Survey
#{
ViewBag.Title = "Create";
}
<h2>Create</h2>
#using (Html.BeginForm())
{
#Html.AntiForgeryToken()
// I use <div class="container">, <fieldset> and <div class="row">
// instead of <div class="form-horizontal"> and <div class="form-group">
<div class="container">
<fieldset>
<legend></legend>
<h4>Survey</h4>
<hr />
#Html.ValidationSummary(true)
#Html.HiddenFor(model => model.Id)
<div class="row">
#Html.LabelFor(model => model.Name, new { #class = "control-label col-md-2 col-md-2 col-lg-2" })
<div class="col-md-10">
#Html.EditorFor(model => model.Name)
#Html.ValidationMessageFor(model => model.Name)
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-12">
Your address information:<br />
<br />
</div>
</div>
#* no problem here with address *#
#{ var vddAddress = new ViewDataDictionary(ViewData) { TemplateInfo = new TemplateInfo() { HtmlFieldPrefix = "Address" } };}
#Html.Partial("_AddressPartial", Model.Address, #vddAddress)
<hr />
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-12">
How we can contact you? :<br />
<br />
</div>
</div>
<div class="row">
#Html.LabelFor(model => model.ContactMethodPhone, new { #class = "control-label col-sm-2 col-md-2 col-lg-2" })
<div class="col-sm-10 col-md-10 col-lg-10">
#Html.EditorFor(model => model.ContactMethodPhone)
#Html.ValidationMessageFor(model => model.ContactMethodPhone)
</div>
</div>
<div class="row">
#Html.LabelFor(model => model.ContactMethodEmail, new { #class = "control-label col-sm-2 col-md-2 col-lg-2" })
<div class="col-sm-10 col-md-10 col-lg-10">
#Html.EditorFor(model => model.ContactMethodEmail)
#Html.ValidationMessageFor(model => model.ContactMethodEmail)
</div>
</div>
<hr />
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-12">
Some Questions<br />
<br />
</div>
</div>
#*Here is the evil one! Beware!*#
#{ var vddQuestions = new ViewDataDictionary(ViewData) { TemplateInfo = new TemplateInfo() { HtmlFieldPrefix = "Questions" } };}
#Html.Partial("_QuestionsPartial", Model.Questions, #vddQuestions)
<hr />
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</div>
</fieldset>
</div>
}
The address partial view is insignificant as there's no problem there.
Here's the partial view for the questions:
#model IEnumerable<BindCollection.Models.Question>
#using BindCollection
#using Kendo.Mvc.UI
#{
string CurrentTopic = string.Empty;
bool FirstTime = true;
}
#foreach (var item in Model)
{
if (CurrentTopic != item.InformationIntakeTopicValue)
{
CurrentTopic = item.InformationIntakeTopicValue;
if (!FirstTime)
{
FirstTime = false;
item.InformationIntakeTopicValue = string.Empty;
}
}
else
{
item.InformationIntakeTopicValue = string.Empty;
}
#Html.EditorFor(m=>item, "Question")
<br />
<br />
}
and, of course, I made an EditorTemplate for a question, as you can see a few lines above...
#model BindCollection.Models.Question
#Html.HiddenFor(m=>m.Id)
#{if (!string.IsNullOrWhiteSpace(Model.InformationIntakeTopicValue))
{
<hr />
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-12">
<br />
<br />
<h4>
#Html.DisplayFor(m => m.InformationIntakeTopicValue, new { #class = "control-label col-sm-10 col-md-10 col-lg-10" })
</h4>
<br />
</div>
</div>
}}
#*type of value to ask for is denoted by item.InformationIntakeTypeCode*#
<div class="row">
<div class="control-label col-sm-9 col-md-9 col-lg-9">
#Html.DisplayFor(m => m.InformationIntakeGroupValue, null)
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-12 col-lg-12">
#{
var outputBool = false;
var outputDate = false;
var outputText = false;
if(Model.InformationIntakeTypeCode.ToLower().Contains("date") )
{
outputDate = true;
}
else if (Model.InformationIntakeTypeCode.ToLower().Contains("bool"))
{
outputBool = true;
}
else if (Model.InformationIntakeTypeCode.ToLower().Contains("string"))
{
outputText = true;
}
}
#if(outputBool)
{
#(Html.Kendo().DropDownListFor(m => m.InformationIntakeValueBool)
.HtmlAttributes(new { style ="width: 100px;", id="InformationIntakeValueBool"+Model.Id.ToString() })
.DataTextField("Text")
.DataValueField("Value")
.BindTo(new List<SelectListItem>() {
new SelectListItem() {
Text = "Please Select...",
Value = "0"
},
new SelectListItem() {
Text = "Yes",
Value = "true"
},
new SelectListItem() {
Text = "No",
Value = "false"
}
}).Value("0")
)
}
#if(outputDate)
{
#(Html.Kendo().DatePickerFor(m => m.InformationIntakeValueDate)
.HtmlAttributes(new { style = "width: 100px;", id="InformationIntakeValueDate"+Model.Id.ToString() })
)
}
#if (outputText)
{
#Html.Kendo().AutoCompleteFor(m => m.InformationIntakeValue).HtmlAttributes(new { style = "width: 80%;", id="InformationIntakeValue"+Model.Id.ToString()})
}
</div>
</div>
So... When I POST, an odd thing occurs. All form values are passed, but the ones for Questions look strange:
Id 1
Name sally
Address.Street 123 Avenue Steet
Address.Street2 Building C
Address.Tenement Suite
Address.ApartmentNum 111
Address.City_input Sarnia
Address.City Sarnia
Address.State_input Ontario
Address.State Ontario
Address.Country_input Canada
Address.Country Canada
Address.MailCode_input N6B 2K0
Address.MailCode N6B 2K0
ContactMethodPhone 555-555-5555
ContactMethodEmail r#r.com
Questions.item.Id 1
Questions.item.InformationIntakeValueBool true
Questions.item.Id 2
Questions.item.InformationIntakeValueDate 2/4/2014
Questions.item.Id 3
Questions.item.InformationIntakeValue Speckled
Questions.item.Id 4
Questions.item.InformationIntakeValueBool true
Questions.item.Id 5
Questions.item.InformationIntakeValue Lightly Toasted
Questions.item.Id 7
Questions.item.InformationIntakeValueBool true
Questions.item.Id 8
Questions.item.InformationIntakeValue Nothing!
Questions.item.Id 6
Questions.item.InformationIntakeValueBool true
Questions.item.Id 9
Questions.item.InformationIntakeValueDate 6/29/2014
I thought, as I've seen in other posts, that the Question items should look like:
Questions[0].Id 1
Questions[0].InformationIntakeValueBool true
Questions[1].Id 2
Questions[1].InformationIntakeValueDate 2/4/2014
Questions[2].Id 3
Questions[2].InformationIntakeValue Speckled
So I'm not sure why mine looks like this.
On the server side, the Request only shows one variable for each:
Request.Form.Results View Expanding the Results View will enumerate the IEnumerable
[0] "__RequestVerificationToken" object {string}
[1] "Id" object {string}
[2] "Name" object {string}
[3] "Address.Street" object {string}
[4] "Address.Street2" object {string}
[5] "Address.Tenement" object {string}
[6] "Address.ApartmentNum" object {string}
[7] "Address.City_input" object {string}
[8] "Address.City" object {string}
[9] "Address.State_input" object {string}
[10] "Address.State" object {string}
[11] "Address.Country_input" object {string}
[12] "Address.Country" object {string}
[13] "Address.MailCode_input" object {string}
[14] "Address.MailCode" object {string}
[15] "ContactMethodPhone" object {string}
[16] "ContactMethodEmail" object {string}
[17] "Questions.item.Id" object {string}
[18] "Questions.item.InformationIntakeValueBool" object {string}
[19] "Questions.item.InformationIntakeValueDate" object {string}
[20] "Questions.item.InformationIntakeValue" object {string}
See the last 4 items? What about the other records?
I'm guessing that there's something strange with the ViewDataDictionary that I'm sending to the Question Partial View.
Any help would be appreciated. This should be simple...
Neither Partial Views or foreach statements contain the necessary information to properly bind collections. I always use EditorTemplates for everything. Especially since EditorTemplates will automatically iterate over a collection.
However, if you're bound and determined to use a loop, then use a for loop, and then index the model. In your case:
#for(int i = 0; i < Model.Count; i++)
{
if (CurrentTopic != Model[i].InformationIntakeTopicValue)
...
#Html.EditorFor(m => Model[i]) // don't have to specify the template name
// since it's the same name as the type
}
However, i'd just do this:
In your view, do this:
#*Here is the evil one! Beware!*#
#Html.EditorFor(m => m.Questions)
Then have your Question.cshtml as usual, which will automatically get iterated over by the EditorFor
However, at the top of the Question.cshtml (after the model declaration) add the following code, it's all that's necessary to achieve what you're trying to do. You don't need the partial view at all.
#{
if (ViewBag.CurrentTopic != Model.InformationIntakeTopicValue)
{
ViewBag.CurrentTopic = Model.InformationIntakeTopicValue;
}
else
{
Model.InformationIntakeTopicValue = string.Empty;
}
}

MVC 3 Dynamic Form Using a ViewModel

I have been trying for weeks to follow a couple tutorials on how to create a dynamic form giving the ability to add another "ingredient" to the form. Here is the article I tried to follow. http://www.joe-stevens.com/2011/07/24/asp-net-mvc-2-client-side-validation-for-dynamic-fields-added-with-ajax/
Right now Im just working on adding multiple recipeIngredients using the add link, but I will need to have both the "ingredientName", and "recipeIngredient" Amount able to be added when the link is clicked.
My problem is that when I run the app, the form for the recipeingredient has a 0 instead of an actual textbox. When I click add new ingredient, I am able to get a textbox to add, but when I type in an amount and click save, the model data isnt being passed to the controller..
I just dont even know where to begin with fixing this, I am not sure if I should be using a viewmodel or if Im going about this entirely wrong. Here is my database diagram http://i44.tinypic.com/xp1tog.jpg.
Here is my CreateView:
#model ViewModels.RecipeViewModel
#using Helpers;
<h2>CreateFullRecipe</h2>
<script src="#Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
<script type="text/javascript">
$().ready(function () {
$("#add-recipeingredient").click(function () {
$.ajax({
url: '#Url.Action("GetNewRecipeIngredient")',
success: function (data) {
$(".new-recipeingredients").append(data);
Sys.Mvc.FormContext._Application_Load();
}
});
});
});
</script>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Recipe</legend>
<div class="editor-label">
#Html.LabelFor(model => model.Recipe.RecipeName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Recipe.RecipeName)
#Html.ValidationMessageFor(model => model.Recipe.RecipeName)
</div>
</fieldset>
<fieldset>
<legend>RecipeIngredients</legend>
<div class="new-recipeingredients">
#Html.EditorFor(model => model.RecipeIngredients)
</div>
<div style="padding: 10px 0px 10px 0px">
<a id="add-recipeingredient" href="javascript:void(0);">Add another</a>
</div>
</fieldset>
<div>
<input type="submit" value="CreateFullRecipe" />
</div>
}
<div>
#Html.ActionLink("Back to List", "Index")
</div>
My editortemplateview for recipeingredient:
#model Models.RecipeIngredient
#using Helpers;
#using (Html.BeginAjaxContentValidation("form0"))
{
using (Html.BeginCollectionItem("RecipeIngedients"))
{
<div style="padding: 5px 0px 5px 0px">
#Html.LabelFor(model => model.Amount)
#Html.EditorFor(model => model.Amount)
#Html.ValidationMessageFor(model => Model.Amount)
</div>
}
}
MY Controller relating methods:
[HttpGet]
public ActionResult CreateFullRecipe()
{
var recipeViewModel = new RecipeViewModel();
return View(recipeViewModel);
}
//
// POST: /Recipe/Create
[HttpPost]
public ActionResult CreateFullRecipe(RecipeViewModel recipeViewModel)
{
if (ModelState.IsValid)
{
db.Recipes.Add(recipeViewModel.Recipe);
db.SaveChanges();
int recipeID = recipeViewModel.Recipe.RecipeID;
for (int n = 0; n < recipeViewModel.RecipeIngredients.Count(); n++)
{
db.Ingredients.Add(recipeViewModel.Ingredients[n]);
int ingredientID = recipeViewModel.Ingredients[n].IngredientID;
recipeViewModel.RecipeIngredients[n].RecipeID = recipeID;
recipeViewModel.RecipeIngredients[n].IngredientID = ingredientID;
db.RecipeIngredients.Add(recipeViewModel.RecipeIngredients[n]);
db.SaveChanges();
}
return RedirectToAction("Index");
}
return View(recipeViewModel);
}
public ActionResult GetNewIngredient()
{
return PartialView("~/Views/Shared/IngredientEditorRow.cshtml", new Ingredient());
}
public ActionResult GetNewRecipeIngredient()
{
return PartialView("~/Views/Shared/_RecipeIngredientEditRow.cshtml", new RecipeIngredient());
}
My View Model:
public class RecipeViewModel
{
public RecipeViewModel()
{
RecipeIngredients = new List<RecipeIngredient>() { new RecipeIngredient() };
Ingredients = new List<Ingredient>() { new Ingredient() };
Recipe = new Recipe();
}
public Recipe Recipe { get; set; }
public IList<Ingredient> Ingredients { get; set; }
public IList<RecipeIngredient> RecipeIngredients { get; set; }
}
}
If there is any other information needed to help my problem out please let me know. This is really driving me crazy so I look forward to any help I can get
Thank you!
I would also like to mention that the controller post method createfullrecipe is for a pre defined list and it worked when I wasnt worried about giving the user the ability to add another ingredient, rather I just defaulted the form to have 2 ingredients and my view had this commented out code to create them. All I really want to do is get the viewmodel to pass the form data to the controller and I can handle the data like my createfullrecipe controller method does now.
#* #for (int n = 0; n < Model.Ingredients.Count(); n++)
{
<div class="editor-label">
#Html.LabelFor(model => model.Ingredients[n].IngredientName)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.Ingredients[n].IngredientName)
#Html.ValidationMessageFor(model => model.Ingredients[n].IngredientName)
</div>
<div class="editor-label">
#Html.LabelFor(model => model.RecipeIngredients[n].Amount)
</div>
<div class="editor-field">
#Html.EditorFor(model => model.RecipeIngredients[n].Amount)
#Html.ValidationMessageFor(model => model.RecipeIngredients[n].Amount)
</div>
}*#
Here are my model classes:
public class Recipe
{
public int RecipeID { get; set; }
public string RecipeName { get; set; }
public string Description { get; set; }
public int? PrepTime { get; set; }
public int? CookTime { get; set; }
public string ImageURL { get; set; }
public virtual IList<RecipeTag> RecipeTags { get; set; }
public virtual IList<Rating> Ratings { get; set; }
public virtual IList<RecipeStep> RecipeSteps { get; set; }
public virtual IList<RecipeIngredient> RecipeIngredients { get; set; }
}
public class RecipeIngredient
{
public int RecipeIngredientID { get; set; }
public string IngredientDesc { get; set; }
public string Amount { get; set; }
public int RecipeID { get; set; }
public int? IngredientID { get; set; }
public virtual Recipe Recipe { get; set; }
public virtual Ingredient Ingredient { get; set; }
}
public class Ingredient
{
public int IngredientID { get; set; }
public string IngredientName { get; set; }
public virtual ICollection<RecipeIngredient> RecipeIngredients { get; set; }
}
There are lots of issues with your code. I prefer to go step by step in order to illustrate a simplified example that you could adapt to your needs.
Models:
public class RecipeViewModel
{
public Recipe Recipe { get; set; }
public IList<RecipeIngredient> RecipeIngredients { get; set; }
}
public class Recipe
{
public string RecipeName { get; set; }
}
public class RecipeIngredient
{
public int Amount { get; set; }
[Required]
public string IngredientDesc { get; set; }
}
Controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var recipeViewModel = new RecipeViewModel();
return View(recipeViewModel);
}
[HttpPost]
public ActionResult Index(RecipeViewModel recipeViewModel)
{
if (!ModelState.IsValid)
{
// there wre validation errors => redisplay the view
return View(recipeViewModel);
}
// TODO: the model is valid => you could pass it to your
// service layer for processing
return RedirectToAction("Index");
}
public ActionResult GetNewRecipeIngredient()
{
return PartialView("~/Views/Shared/EditorTemplates/RecipeIngredient.cshtml", new RecipeIngredient());
}
}
View (~/Views/Home/Index.cshtml):
#model RecipeViewModel
<script src="#Url.Content("~/Scripts/jquery.validate.js")" type="text/javascript"></script>
<script src="#Url.Content("~/Scripts/jquery.validate.unobtrusive.js")" type="text/javascript"></script>
<script type="text/javascript">
$(function() {
$('#add-recipeingredient').click(function () {
$.ajax({
url: '#Url.Action("GetNewRecipeIngredient")',
type: 'POST',
success: function (data) {
$('.new-recipeingredients').append(data);
}
});
return false;
});
});
</script>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<div>
#Html.LabelFor(model => model.Recipe.RecipeName)
#Html.EditorFor(model => model.Recipe.RecipeName)
#Html.ValidationMessageFor(model => model.Recipe.RecipeName)
</div>
<fieldset>
<legend>RecipeIngredients</legend>
<div class="new-recipeingredients">
#Html.EditorFor(model => model.RecipeIngredients)
</div>
<div style="padding: 10px 0px 10px 0px">
<a id="add-recipeingredient" href="javascript:void(0);">Add another</a>
</div>
</fieldset>
<div>
<input type="submit" value="CreateFullRecipe" />
</div>
}
Editor template (~/Views/Shared/EditorTemplates/RecipeIngredient.cshtml):
#model RecipeIngredient
#using (Html.BeginCollectionItem("RecipeIngredients"))
{
<div>
#Html.LabelFor(model => model.Amount)
#Html.EditorFor(model => model.Amount)
#Html.ValidationMessageFor(model => model.Amount)
</div>
<div>
#Html.LabelFor(model => model.IngredientDesc)
#Html.EditorFor(model => model.IngredientDesc)
#Html.ValidationMessageFor(model => model.IngredientDesc)
</div>
}

Resources