ASP.MVC5 - Foreach with property array - c#

Well! I'm using ASP.MVC5. What I'm trying to do is to loop an array that contains objects and each object contains array properties:
I have this class:
public class MylistModels
{
public string Subtitle { get; set; }
public string[] Question { get; set; }
}
The logic is:
I have a POST form in HTML (I can add more fields if I want):
<input type="text" placeholder="Subtitle here" name="lists[0].Subtitle" />
<input type="text" placeholder="Subtitle here" name="lists[0].Question" />
<input type="text" placeholder="Subtitle here" name="lists[0].Question" />
<input type="text" placeholder="Subtitle here" name="lists[1].Subtitle" />
<input type="text" placeholder="Subtitle here" name="lists[1].Question" />
<input type="text" placeholder="Subtitle here" name="lists[1].Question" />
<input type="text" placeholder="Subtitle here" name="lists[2].Subtitle" />
<input type="text" placeholder="Subtitle here" name="lists[2].Question" />
<input type="text" placeholder="Subtitle here" name="lists[2].Question" />
When I click in save I send it to an action:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create(List<MylistModels> lists)
{
if (ModelState.IsValid)
{
//how to loop the arrays that contains a subtitle with questions array?
return RedirectToAction("Index");
}
return View();
}
When I receive it in the action, it looks like this:
I receive an array of objects and each one has your subtitle related to your questions
Obs.: the "subtitle" is always one in each object and "questions" I can have one or more.
PROBLEM:
How can I loop inside each object array and loop again in each question of this object?
I'm trying to do like this but it's not working:
SubtitleChecklist subtitleCheckList = new SubtitleChecklist();
QuestionChecklist questionChecklist = new QuestionChecklist();
foreach (var list in lists)
{
subtitleCheckList.IdChecklist = idChecklist;
subtitleCheckList.Subtitle = list.Subtitle;
db.subtitleCheckList.Add(subtitleCheckList);
db.SaveChanges();
int idSubtitleChecklist = subtitleCheckList.Id;
for (int i = 0; i < list.Question.Length; i++)
{
questionChecklist.Question = list.Question[i];
questionChecklist.IdSubtitle = idSubtitleChecklist;
db.QuestionChecklist.Add(questionChecklist);
db.SaveChanges();
}
}

Where are you instantiating questionChecklist and subtitleCheckList?
It looks like you are modifying the same objects over and over in your loops.
UPDATE 1:
Move: SubtitleChecklist subtitleCheckList = new SubtitleChecklist();
inside the outer loop and the QuestionChecklist questionChecklist = new QuestionChecklist(); into the inner loop.
Also, what result are you getting now?

You loop through lists object but you assign values and do all other stuff to one and the some object that you defined outside the loop and has nothing to do with your loop.

Related

Bind <string, string> Dictionary to Checkbox MVC (Make checkbox return srtring value)

I got this structure in my ViewModel
public Dictionary<string, string> Answers { get; set; }
public List<KeyValuePair<Question, List<string>>> QuestionsAndOptions { get; set; }
public TestVM()
{
Answers = new Dictionary<string, string>();
QuestionsAndOptions = new List<KeyValuePair<Question, List<string>>>();
}
QuestionsAndOptions contains Questions and its options for my Test Page im passing to VIew.
I got different question types in my project and questions with multiple answer options is one of them.
Answers contains <string,string> pairs of question IDs and user Answer for it
I got this code in my View:
#for (int i = 0; i < Model.QuestionsAndOptions.Count; i++)
{
var question = Model.QuestionsAndOptions[i];
<tr>
<td>
<b>#question.Key.Text</b>
#if (question.Key.TypeId == 1) /*single, radiobuttons*/
{
foreach (var answer in question.Value)
{
<input type="hidden" name="testVM.Answers[#question.Key.Id].Key" value="#question.Key.Id" />
<input type="hidden" name="testVM.Answers.Index" value="#question.Key.Id" />
<input type="hidden" name="testId" value="#Model.Test.Id" />
#Html.RadioButton("testVM.Answers[" + question.Key.Id + "].Value", answer, false) #answer
}
}
#if (question.Key.TypeId == 2) /*multiple, checkboxes*/
{
foreach (var answer in question.Value)
{
<input type="hidden" name="testVM.Answers[#question.Key.Id].Key" value="#question.Key.Id" />
<input type="hidden" name="testVM.Answers.Index" value="#question.Key.Id" />
<input type="hidden" name="testId" value="#Model.Test.Id" />
#Html.CheckBox("testVM.Answers[" + question.Key.Id + "].Value", answer) #answer
}
}
#if (question.Key.TypeId == 3) /*string text, textbox*/
{
foreach (var answer in question.Value)
{
<input type="hidden" name="testVM.Answers[#question.Key.Id].Key" value="#question.Key.Id" />
<input type="hidden" name="testVM.Answers.Index" value="#question.Key.Id" />
<input type="hidden" name="testId" value="#Model.Test.Id" />
#Html.TextBox("testVM.Answers[" + question.Key.Id + "].Value")
}
}
First and third If(){...} works fine and i getting what i need after submit, but for checkboxes i'm obviously geting true/false
I want checkboxes to pass strings (checked values) to Answer dictionary
If you want to bind value to checkbox,you can try to set value in checkbox,try to use the following code:
<input type="checkbox" value=#answer name="testVM.Answers[" + question.Key.Id + "].Value" />#answer
When the checkbox is checked the value of the checkbox(#answer) will be passed to action.

List not beign assigned as a parameter Url.Action

I'm using Url.Action to pass some paramenters to a method, like this
//Sending data
Sí
//Method
public ActionResult BorrarMensaje(int idMsg, int idMsgUsr, int idImg, List<int> idsArchs)
{
tblMensaje tblMensaje = db.tblMensaje.Find(idMsg);
tblMensajesUsuario tblMensajesUsuario = db.tblMensajesUsuario.Find(idMsgUsr);
tblImagenAdj tblImagenAdj = db.tblImagenAdj.Find(idImg);
foreach (var id in idsArchs)
{
tblArchivoAdj tblArchivoAdj = db.tblArchivoAdj.Find(id);
db.tblArchivoAdj.Remove(tblArchivoAdj);
}
//Some more code...
The values of idMsg, idMsgUsr, idImg are sent as arguments, but idsArch just doesn´t get the value of IDArchivos, when I view the count property it shows 0, (both idsArch and IDArchivos are of type List), I don't know why it isn't working, is there any alternative to do this? Thanks beforehand
For alternatives; you could write it as a Form and fields are hidden.
<form method="GET" action="#Url.Action("BorrarMensaje", "Supplier")">
<input name="idMsg" value="#item.IDMensage" hidden />
<input name="idMsgUsr" value="#item.IDMensajeUsuario" hidden />
<input name="idImg" value="#item.IDImagen" hidden />
<!-- for type list, you need to loop and specify an index -->
#for(int i = 0; i < item.IDArchivos.Count(); i++){
<!--replace PropertyName with the property name of IDArchivos that you want to submit -->
<input name="idsArchs[#i].PropertyName" hidden />
<input class="btn btn-danger" type="Submit" value="Si">
}
</form>
No changes in controller.

My ID's are all the same using a foreach and #Html.DropDownListFor

I'm struggling with my MVC 5 app. I'm trying to create a form, based upon my Model.
The Model is fairly simple and for this, I've totally reduced it to bare
Survey.cs has only 1 property
public Dictionary<Answer, AnswerTemplate> QuestionAndAnswerTemplates { get; set; }
Answer.cs has only 1 property
public string AnswerValue {get;set;}
AnswerTemplate.cs has 2 properties
public string ConstraintsCsv { get; set; }
public IEnumerable<SelectListItem> ConstraintsFroDropDownList
{
get
{
var result = new List<SelectListItem>();
var split = ConstraintsCsv?.Split(',');
foreach (var s in split)
{
result.Add(new SelectListItem() { Text = s });
}
return result;
}
}
And finally the CSHTML (view)
#model Survey
#using (Html.BeginForm())
{
foreach (var item in Model.QuestionAndAnswerTemplates)
{
#Html.DisplayFor(a => item.Key.Question)<br />
#Html.DropDownListFor(a => item.Key.AnswerValue, item.Value.ConstraintsFroDropDownList)
<br /><br />
}
<input type="submit" value="Save" />
}
The Dictionary is populated with 2 items, and as such, I see 2 DropDownLists on the rendered HTML in the browser. Great!
The problem is, when I inspect the source code of the rendered HTML, I get the following
<select id="item_Key_AnswerValue" name="item.Key.AnswerValue">
<option>1</option>
<option>2</option>
<option>3</option>
</select> <br /><br />
<select id="item_Key_AnswerValue" name="item.Key.AnswerValue">
<option>Yes</option>
<option>No</option>
</select> <br /><br />
As you can see, each ID is the same, as is the name
Why is this? I'd like to be able to click the Save button and have a fully populated model.
The controller is
[HttpPost]
public ActionResult Survey(Survey model)
{
return View();//need to do something here
}
But, model is always unpopulated (empty values).
What am I doing wrong?
When editing a collection of item in your razor view don't use foreach but a simple for loop.
So instead of:
foreach (var item in Model.QuestionAndAnswerTemplates)
{
#Html.DisplayFor(a => item.Key.Question)<br />
#Html.DropDownListFor(a => item.Key.AnswerValue, item.Value.ConstraintsFroDropDownList)
<br /><br />
}
Use this:
for(var i = 0; i < Model.QuestionAndAnswerTemplates.Count(); i++)
{
#Html.DisplayFor(a => Model.QuestionAndAnswerTemplates[i].Key.Question)<br />
#Html.DropDownListFor(a => Model.QuestionAndAnswerTemplates[i].Key.AnswerValue, item.Value.ConstraintsFroDropDownList)
<br /><br />
}
Doing it like above then name attribute of each of your select element will be bind to the position of your template in Model.QuestionAndAnswerTemplates.

How to bind OrderedDictionary?

I try to bind an OrderedDictionary to a view but when the post method gets invoked the Dictionary is always empty.
Here is my code:
[HttpGet]
public ViewResult Edit(string username, string password)
{
Xml test = new Xml(#"c:\Users\pc\Desktop\xml - Copy.xml");
XmlNode userNode = test.GetUserNodeByUsernameAndPassword(username, password);
User user = new User();
user.BindData(userNode);
return View(user.user);
}
[HttpPost]
public ViewResult Edit(OrderedDictionary attributes)
{
return View(attributes);
}
And here is the view:
#using (Html.BeginForm("Edit", "Users")) {
#Html.ValidationSummary(true)
<fieldset>
<legend>User</legend>
<p>
<input type="submit" value="Save" />
</p>
#{int counter = 0;}
#{string name = "";}
#foreach (DictionaryEntry attribute in Model)
{
{ name = "[" + counter + "].key"; }
<input type="hidden" name=#name value=#attribute.Key />
#attribute.Key #Html.TextBoxFor(m => attribute.Value)
counter++;
<br />
}
</fieldset>
}
And the result Html looks like this is:
<input type="hidden" value="Username" name="[0].key">
Username
<input id="attribute_Value" type="text" value="Anamana" name="attribute.Value">
So the content of the OrderedDictionary appears fine in the view but when I make a post back the binding isn't working and the directory remains empty.
Concept
To bind a dictionary you have to change the name attribute in the html input tag. Something like this:
In your controller:
[HttpPost]
public ActionResult Edit(IDictionary<string, string> attributes)
{
}
In your HTML:
<input type="text" name="attributes[0].Key" value="A Key" />
<input type="text" name="attributes[0].Value" value="A Value" />
<input type="text" name="attributes[1].Key" value="B Key" />
<input type="text" name="attributes[1].Value" value="B Value" />
The attributes name should be before the index [0] on ther name attribute, because your action expect it.
Tips
I would use the HiddenFor and TextBoxFor HTML Helper of the Asp.Net MVC.
#Html.HiddenFor(model => model[i].Key)
#Html.TextBoxFor(model => model[i].Value)
And it will render in the format that the asp.net mvc will understand and get it working.
For more samples about databind take a look at this link.
Meantime I have found the solution.
I can pass an OrderedDictionary to the view page.
It process it by the following Razor code:
#model System.Collections.Specialized.OrderedDictionary
(...)
#{int counter = 0;}
#{string name = "";}
#foreach (DictionaryEntry attribute in Model)
{
{ name = "[" + counter + "].key"; }
#Html.Hidden(name, attribute.Key)
{name = "[" + counter + "].value";}
#attribute.Key #Html.TextBox(name, attribute.Value)
counter++;
<br />
}
The result HTML's structure fits to the samples which is found in a book, the values from the dictionary appears fine on the page.
After POST was invoked the POST handler function gets the modified values in a Dictionary.
[HttpPost]
public ViewResult Edit(Dictionary<string, string> attributes)
{}
I don't know why but I can't use OrderedDictionary here.

Get html items from view to control

Still kind of new to MVC, so please bear with me. I'm trying to grab some dynamically generated HTML. In this case, list items in my notifyList. I plan on looping through each one in the controller and adding them as database entries. Any help is appreciated, thanks.
View
#model _BaseViewModel
// The form it's within...
#using (Html.BeginForm("Create", "Leaf", FormMethod.Post, new { id = "createForm" }))
<div class="editor-label bottom-area bottom-header">
Notification List:
</div>
<div class="editor-field bottom-area">
<ul id="notifyList"></ul>
</div>
Controller:
[HttpPost]
public ActionResult Create(_BaseViewModel model)
{
// Some loop here
// get html here
db.UserItems.AddObject(model.user);
db.SaveChanges();
//
return RedirectToAction("Index");
}
As far as I understood, you use jQuery to fetch <li/> elements into notifyList. What you need to do here is to generate a hidden input as well. Sample:
$("#btnAppend").click(function() {
for(var i = 0; i < 4; i++) {
var _val = "Foo " + i;
var $li = $("<li/>").text(_val);
var $hidden = #("<input/>").
attr("type", "hidden")
attr("name", "foo").
val(_val);
$hidden.appendTo($li);
$li.appendTo("#notifyList");
}
});
This code will generate following output inside your DOM:
<ul id="notifyList">
<li>Foo 0<input type="hidden" value="Foo 0" name="foo" /></li>
<li>Foo 1<input type="hidden" value="Foo 1" name="foo" /></li>
<li>Foo 2<input type="hidden" value="Foo 2" name="foo" /></li>
<li>Foo 3<input type="hidden" value="Foo 3" name="foo" /></li>
</ul>
When you make a http form post, you can grab the values by the below controller action implementation:
public ActionResult Index(string[] foo) {
foreach(var item in foo) {
//Work with each individual item
}
//continue your code
}
it doesn't work this way. html only exists in the view. the controller has no concept of html (not should it). data sent to the controller comes in 1 of types (GET, POST). there are others, but these are the main to.
get is typically associated with the querystring www.domain.com/mypage.aspx?key=value
where post is the input values from form
<form action="mypage.aspx" method="post">
<input name="key" value="value"/>
<input type="submit" value="click me"/>
</form>
So adding items to a html list won't provide any meaning to the controller. javascript and ajax provide more options on how the data gets sent to the server, but the data is sent, not the markup. and the data is sent as key value pairs.

Categories

Resources