Create a checkbox for each object - c#

I am creating a small website which allows users to create questions and exams (and also take those exams). Facing a problem with the Create-examview. The user has to be allowed to check any Questions that should be added to the Exam.
Using the following action to create the view, which passes the Questions in a ViewBag:
public ActionResult Create()
{
QuestionaireDbContext db = new QuestionaireDbContext();
ViewBag.Questions = db.Questions;
return View();
}
In my view then, I can call ViewBag.Questions and (should be able to?) use those to create checkboxes for each Question.
I have tried using the extesionmethod for HtmlHelper, CheckBoxList, which I got through NuGet. But the Html.CheckBoxList doesn't seem to be picked up. I tried adding the using as suggested in their documents but that didn't work either.
How can I create a checkbox for each Question and allow the user to select a variaty of them?
My Exam and Question-models for reference:
public class Exam
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime CreationDate { get; set; }
public ICollection<Question> Questions { get; set; }
}
public class Question
{
public enum Answers
{
A,
B,
C,
D
}
public int Id { get; set; }
public string Name { get; set; }
public string AnswerA { get; set; }
public string AnswerB { get; set; }
public string AnswerC { get; set; }
public string AnswerD { get; set; }
public Answers Correct { get; set; }
}

You will need to create view models to represent what you want to bind to. One possible solution might be
public class ExamVM
{
public int ID { get; set; }
public string Name { get; set; }
public List<QuestionVM> Questions { get; set; }
}
public class QuestionVM
{
public int ID { get; set; }
public string Name { get; set; }
public bool IsSelected { get; set; }
}
In you Create method, initialize and populate the ExamVM details including the collection of possible questions, then in the view
#model YourAssembly.ExamVM
#using (Html.BeginForm())
{
#Html.HiddenFor(m => m.ID)
#Html.DisplayFor(m => m.Name)
for (int i = 0; i < Model.Questions; i++)
{
#Html.HiddenFor(m => m.Questions[i].ID)
#Html.CheckBoxFor(m => m.Questions[i].IsSelected)
#Html.DisplayFor(m => m.Questions[i].Name)
}
<input type="submit" value="Save" />
}
Post method
[HttpPost]
public ActionResult Create(ExamVM model)
{
foreach(QuestionVM q in model.Questions)
{
if (q.IsSelected)
{
// Save the value of exam.ID and question ID to the database

Create a folder called EditorTemplates in your Views/Shared folder.
create a new empty view called _QuestionEditor and add the following code.
#model Question
#Html.HiddenFor(model => model.Id)
#Html.DisplayFor(model => model.Name)
//use DisplayFor or LabelFor accordingly
#Html.CheckboxFor(model => true, Model.AnswerA)
#Html.CheckboxFor(model => true, Model.AnswerB)
#Html.CheckboxFor(model => true, Model.AnswerC)
#Html.CheckboxFor(model => true, Model.AnswerD)
now in your main view use it as follows
#foreach(var question in ViewBag.Questions){
#Html.EditorFor(item => question)
//you can specify the template name i.e `"_QuestionEditor"` as the second parameter
//if you have more than one editor template for the same type
}
This doesn't take into account how you submit your data as you haven't provided any code for that or the model that you use to get the data back into your post action

Related

Difficulties with DropDownlist in ASP.Net MVC5

I'm struggling with dropdownlist tried several methods online and all failed will show the methods that I tried.
Objective
Create a Reciept with date,reference... & country. Country is Required and should be a dropdownlist.
So the Table for Reciept("ID, Name, Date, Address, City, CountryList).
RecieptModel
public class TransactionModel
{
public int ID { get; set; }
public int Name{ get; set; }
public DateTime Date { get; set; }
public string Address { get; set;}
public string City { get; set; }
public CountryList CountryList { get; set; }
}
public class ApplicationDbContext : DbContext
{
public DbSet<RecieptModel> Reciepts { get; set;}
public DbSet<CountryList> coutryList { get; set; }
}
CountryList
public class CountryList
{
public byte Id { get; set; }
public enum Country
{
Germany,
US,
UK
}
}
Controller
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "ID,Name,Date,City,CountryList")] Reciepts reciepts)
{
if (ModelState.IsValid)
{
db.Reciepts.Add(reciepts);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(reciepts);
}
View
<div class="form-group">
#Html.LabelFor(model => model.CountryList, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.EnumDropDownListFor(model => model.CountryList)
#Html.ValidationMessageFor(model => model.CountryList)
</div>
</div>
This failed I looked for several examples I'm trying to do it without the use of javascript. In the End I just want to learn how to implement a Dropdownlist & save it to the database allot of the methods that I tried to implement failed in MVC5.
I would sugest you to add a html extension method that implement dropdownlist for a generic enumarator. Take a look at this answer.
There is slightly change in your view where you bind the Country
this is actually you write
#Html.EnumDropDownListFor(model => model.CountryList)
Change is need to add name property in your country list (it must be ID that is your taken in your model) , so it must be like this to maintain country list value
#Html.DropDownListFor(model => model.CountryList.Id, new SelectList(model => model.CountryList)
Becouse every HTML control need unique indentifier

MVC model binding to list - only works on first item in list

I have a page that contains multiple forms to edit questions for a single quiz, each question has its own list of answers. So for each question inside this quiz there is a form for which a user can edit the question (and answers), See below:
#model OLTINT.Areas.admin.ViewModels.OldQuizQAViewModel
<h1>Edit #Model.QuizTitle quiz</h1>
<hr />
<p class="breadcrumb">
#Html.ActionLink(HttpUtility.HtmlDecode("◄") + " Back to List", "Quizzes", new { id = Model.CourseID }, new { #class = "" })
</p>
#for (int j = 0; j < Model.OldQuizQuestions.Count(); j++)
{
using (Ajax.BeginForm("EditQuiz", "Course", null, new AjaxOptions
{
HttpMethod = "POST",
InsertionMode = InsertionMode.Replace,
UpdateTargetId = "button"
}))
{
#Html.AntiForgeryToken()
#Html.HiddenFor(model => model.QuizID)
#Html.HiddenFor(model => model.OldQuizQuestions[j].QuizQuestionID)
<p class="form_title">Question number #Model.OldQuizQuestions[j].Order</p>
<div class="resize_input">#Html.EditorFor(model => model.OldQuizQuestions[j].Question)</div>
<p class="form_title">#Html.LabelFor(model => model.OldQuizQuestions[j].Type)</p>
<div class="resize_input">#Html.DropDownListFor(model => model.OldQuizQuestions[j].Type, ViewBag.types, "Please choose...", new { #class = "chosen-select" })</div>
<p class="form_title">Choose correct answers</p>
Char x = 'a';
for (int i = 0; i < Model.OldQuizQuestions[j].OldQuizAnswers.Count(); i++)
{
x++;
if (i == 0)
{
x = 'a';
}
<div style="display:table; width:100%;">
<div class="divTableCell" style="padding:0 10px 10px 0; vertical-align:middle; min-width:6%;">
#Html.CheckBoxFor(model => model.OldQuizQuestions[j].OldQuizAnswers[i].Correct, new { style = "" })
#Html.LabelFor(model => model.OldQuizQuestions[j].OldQuizAnswers[i].Correct, "["+ x +"]")
</div>
<div class="divTableCell quiz_input">
#Html.HiddenFor(model => model.OldQuizQuestions[j].OldQuizAnswers[i].QuizAnsID)
#Html.EditorFor(model => model.OldQuizQuestions[j].OldQuizAnswers[i].Answer)
</div>
</div>
}
<div class="button_container">
<p id="button"></p>
#Html.ActionLink("Delete this question", "DeleteQuestion", new { id = Model.OldQuizQuestions[j].QuizQuestionID }, new { #class = "button button_red button_not_full_width" })
<input type="submit" value="Save" class="button button_orange button_not_full_width" />
</div>
<hr />
}
}
OldQuizQAViewModel:
public class OldQuizQAViewModel
{
public int CourseID { get; set; }
public int? QuizID { get; set; }
public string QuizTitle { get; set; }
public IList<OldQuizQuestions> OldQuizQuestions { get; set; }
}
OldQuizQuestions:
public class OldQuizQuestions
{
[Key]
public int QuizQuestionID { get; set; }
public int OldQuizID { get; set; }
[Required]
public string Question { get; set; }
[Required]
public int Order { get; set; }
[Required]
public int Type { get; set; }
public virtual IList<OldQuizAnswers> OldQuizAnswers { get; set; }
public virtual OldQuiz OldQuiz { get; set; }
}
OldQuizAnswers:
public class OldQuizAnswers
{
[Key]
public int QuizAnsID { get; set; }
public int QuizQuestionID { get; set; }
public string Answer { get; set; }
public int Order { get; set; }
public bool Correct { get; set; }
public bool Chosen { get; set; }
public virtual OldQuizQuestions OldQuizQuestions { get; set; }
}
Controller:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditQuiz(OldQuizQAViewModel model)
{
var questiondata = model.OldQuizQuestions.Single();
if (ModelState.IsValid)
{
OldQuizQuestions updatequestion = db.OldQuizQuestions
.SingleOrDefault(x => x.QuizQuestionID == questiondata.QuizQuestionID);
updatequestion.Question = questiondata.Question;
updatequestion.Type = questiondata.Type;
db.Entry(updatequestion).State = EntityState.Modified;
db.SaveChanges();
foreach (var answer in questiondata.OldQuizAnswers)
{
var updateanswer = updatequestion.OldQuizAnswers
.First(x => x.QuizAnsID == answer.QuizAnsID);
updateanswer.Answer = answer.Answer;
updateanswer.Correct = answer.Correct;
db.Entry(updateanswer).State = EntityState.Modified;
db.SaveChanges();
}
return Content("<span style='font-weight:300; font-size:1.2em; color: green; '>Saved!</span>");
}
return Content("<span class='errortext'>Please correct the marked fields!</span>");
}
Now this works fine if I want to edit the first question but when I edit anything else my controller just says null but when I check the data that's being posted everything is there (for example when i try to edit question 2):
I've had a look around on here at the many queries about model binding to a list but none have helped. Can anyone see where i'm going wrong with this?
The issue you are facing is caused by a misunderstanding of how asp.net model binding works in relation to lists. For example looking at the view model used in your controller action EditQuiz.
public class OldQuizQAViewModel
{
public int CourseID { get; set; }
public int? QuizID { get; set; }
public string QuizTitle { get; set; }
public IList<OldQuizQuestions> OldQuizQuestions { get; set; }
}
In order for the model binding to work with a IList or any other collection, asp-net expects the form data you post to have sequential indexes. The form data you are sending over POST already has a working example of model binding with collections implemented. Looking at the form data:
The highlighted properties show how to correctly bind to a collection, in that you set the values for the property Correct in the OldQuizAnswers model for each index of IList in OldQuizQAViewModel and pass these all at once in a single request.
Whereas in the same request you only pass the data for OldQuizQuestions of specific index you wish these values to be bound to in the IList collection.
This is why the fist time you post, the model binding works successfully as you are referencing the first index ([0]), whereas on the second POST you reference the second index (1) but not the first, causing the model binding to fail.
See herefor more information on how model binding works.
Have you tried adding a #Html.HiddenFor(model => model.OldQuizQuestions[j]) ?
I read this "It creates a hidden input on the form for the field (from your model) that you pass it.
It is useful for fields in your Model/ViewModel that you need to persist on the page and have passed back when another call is made but shouldn't be seen by the user."
Answer comes from https://stackoverflow.com/a/3866720/8404545
Might be the problem here

Orchard - save list of elements on form submit

Basically i have a editor view where i have a few fields and a list, located in admin -> teacher -> edit
File.cshtml
<fieldset>
<legend>#T("Details")</legend>
#Html.LabelFor(m => m.FirstName, T("First name"))
#Html.TextBoxFor(m => m.FirstName)
#Html.LabelFor(m => m.LastName, T("Last name"))
#Html.TextBoxFor(m => m.LastName)
#Html.LabelFor(m => m.Gender, T("Gender"))
#Html.DropDownListFor(m => m.Gender, genderSelectListItems, "Select gender...")
</fieldset>
<fieldset>
#{
var i = 0;
#Html.LabelFor(m => m.Availability)
#Html.DropDownListFor(m => m.Availability, daysSelectList, "Select day to add...")
<input type="button" id="addDay" value="Add" />
<ul id="daysList">
#foreach (var day in Model.Availability)
{
<li>
#Html.TextBoxFor(m => m.Availability[i].Interval)
</li>
i++;
}
</ul>
}
</fieldset>
Whenever i submit the data, only the fields get saved. The list is never saved, even though fiddler shows that the data is sent back to the server
My models are:
TeacherPartRecord
public class TeacherPartRecord : ContentPartRecord
{
public TeacherPartRecord()
{
TeacherData = new List<TeacherDataRecord>();
}
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual Gender Gender { get; set; }
public virtual IList<TeacherDataRecord> TeacherData { get; set; }
}
TeacherPart - this file is almost the same as TeacherPartRecord so i truncated it, showing only the difference
public class TeacherPart : ContentPart<TeacherPartRecord>
{
................
public IList<AvailabilityRecord> Availability
{
get { return Record.TeacherData.Select(r => r.AvailabilityRecord).ToList(); }
}
}
TeacherDataRecord
public class TeacherDataRecord
{
public virtual int Id { get; set; }
public virtual TeacherPartRecord TeacherPartRecord { get; set; }
public virtual AvailabilityRecord AvailabilityRecord { get; set; }
}
AvailabilityRecord
public class AvailabilityRecord
{
public virtual int Id { get; set; }
public virtual Day Day { get; set; }
public virtual string Interval { get; set; }
}
and TeacherPartDriver
public class TeacherPartDriver : ContentPartDriver<TeacherPart>
{
//GET
protected override DriverResult Editor(TeacherPart part, dynamic shapeHelper)
{
return ContentShape("Parts_Teacher_Edit", () =>
shapeHelper.EditorTemplate(TemplateName: "Parts/Teacher", Model: BuildEditorViewModel(part), Prefix: Prefix));
}
//POST
protected override DriverResult Editor(TeacherPart part, IUpdateModel updater, dynamic shapeHelper)
{
var viewModel = new TeacherEditViewModel();
updater.TryUpdateModel(viewModel, Prefix, null, null);
_teacherService.UpdateTeacher(viewModel, part);
return Editor(part, shapeHelper);
}
}
BuildEditorViewModel only maps properties from TeacherPart to TeacherEditViewModel, which is identical with TeacherPart
The POST Editor method never receives the data from Availability (part.Availability is the same as previously, new data is not received)
I inserted a few entries in the database manually because i can't add a list of type Availability from the edit page, even though the other content (first name, last name, gender) is created
I would like to know how/when does the binding occur, or maybe some suggestions on how i could solve this issue.
I should mention that my c#/MVC experience is 6 months and my orchard experience is about 2 weeks so i'm still learning.
All i hope is that i stated my problem clearly and there is someone that can help.
Thank you for your time
EDIT
As i can see, the values are bound
<input id="Teacher_Availability_0__Interval" name="Teacher.Availability[0].Interval" type="text" value="14-20">
<input id="Teacher_Availability_1__Interval" name="Teacher.Availability[1].Interval" type="text" value="12-18">
and fiddler shows the data is getting sent
Fiddler
so i guess that something wrong happens right after the data is sent and before being received by the server or maybe my models are wrong

Creating an auto-generated DropDownList on View from ViewModel

Which I'd expect to be implemented in .NET MVC, but trying to figure out how to actually do it. Currently on my ViewModel, I have (for example):
public class GroupPolicyViewModel
{
public int PolicyId { get; set; }
public int HistoryId{ get; set; }
public SelectList ProductList { get; set; } // tried this
public List<Product> ProductList1 { get; set; } // tried this
}
Whenever I try and auto-generate my View from this ViewModel, the ProductList gets ignored. Is there any way to auto-generate a DropDownList at all from the ViewModel?
With model
public class GroupPolicyViewModel
{
public int PolicyId { get; set; }
public int HistoryId{ get; set; }
public int SelectedProductId{ get; set; }
public List<Product> ProductList { get; set; }
}
You can create DropDownList
#Html.DropDownListFor(m => m.SelectedProductId,
new SelectList(Model.ProductList, "ProductId", "ProductName"))
Or if you have SelectList of products in your model
#Html.DropDownListFor(m => m.SelectedProductId, Model.ProductSelectList)
If you want some generated code, you need to use scaffolding option with providing data context class. Here is nice tutorial MVC Music Store
You can (from VS2010) when creating a new Controller and using Entity Framework. Specify in the wizard to include Entity Framework and Read/Write ops and the wizard will create both the controller and the views.
It'll generate code like this [there is more] in the controller:
public ActionResult Create()
{
ViewBag.CostCentre_ID = new SelectList(db.CostCentres, "ID", "Name");
ViewBag.Location_ID = new SelectList(db.Locations, "ID", "Name");
ViewBag.User_ID = new SelectList(db.UCMUsers, "User_ID", "EmployeeNo");
return View();
}
and this in the view:
<div class="editor-field">
#Html.DropDownList("User_ID", String.Empty)
#Html.ValidationMessageFor(model => model.User_ID)
</div>

Mapping of ViewModel with SelectList in MVC3

Hi I'm struggling to find the correct approach on SO for what I am currently doing, so I thought I would ask.
Here is my simplified code:
The entities are nested types based on using them with EF CodeFirst and the ViewModel is being mapped with AutoMapper.
When posting the form the ModelState is not valid due to the dropdownlist being mapped to model.CourseId and displaying my Course data.. i.e. CourseId = 2, CourseList = Null, but also having the [Required] attribute, really only CourseId is required but I also needed a relevant error message.
I then thought that in my Create GET & POST actions the view should probably just have the CourseId but I still need to display it as a dropdown and populate it and I was unsure as how to do that correctly.
I may also not be understanding how this should be used correctly and if I even need CourseName, i.e. since the Course already exists in the database I just want a foreign key to it, which will still let me show the selected course.
I'm also planning to break out all this mapping and data setting in my controller actions into a separate service layer but at the moment its a small prototype.
// Entities
public class Recipe {
public int Id { get; set; }
public string Name { get; set; }
public Course Course { get; set; }
}
public class Course {
public int Id { get; set; }
public string Name { get; set; }
}
// View Model
public class RecipeCreateViewModel {
// Recipe properties
public int Id { get; set; }
public string Name { get; set; }
// Course properties, as primitives via AutoMapper
public int CourseId { get; set; }
public string CourseName { get; set; }
// For a drop down list of courses
[Required(ErrorMessage = "Please select a Course.")]
public SelectList CourseList { get; set; }
}
// Part of my View
#model EatRateShare.WebUI.ViewModels.RecipeCreateViewModel
...
<div class="editor-label">
Course
</div>
<div class="editor-field">
#* The first param for DropDownListFor will make sure the relevant property is selected *#
#Html.DropDownListFor(model => model.CourseId, Model.CourseList, "Choose...")
#Html.ValidationMessageFor(model => model.CourseId)
</div>
...
// Controller actions
public ActionResult Create() {
// map the Recipe to its View Model
var recipeCreateViewModel = Mapper.Map<Recipe, RecipeCreateViewModel>(new Recipe());
recipeCreateViewModel.CourseList = new SelectList(courseRepository.All, "Id", "Name");
return View(recipeCreateViewModel);
}
[HttpPost]
public ActionResult Create(RecipeCreateViewModel recipe) {
if (ModelState.IsValid) {
var recipeEntity = Mapper.Map<RecipeCreateViewModel, Recipe>(recipe);
recipeRepository.InsertOrUpdate(recipeEntity);
recipeRepository.Save();
return RedirectToAction("Index");
} else {
recipe.CourseList = new SelectList(courseRepository.All, "Id", "Name");
return View(recipe);
}
}
I fixed my particular problem just by doing the below.
[Required(ErrorMessage = "Please select a Course.")]
public int CourseId { get; set; }
// public string CourseName { get; set; }
public SelectList CourseList { get; set; }
The view will use the DropDownListFor helper to map the drop down to my CourseId and that's all I really needed.
On to another problem now with AutoMapper and why it is not mapping back to the Recipe entity in the POST Create action.
I probably first need to find a way to store the relevant Course name in the "CourseName" property.

Categories

Resources