Trying to create a dynamic enough model to handle dynamic questions - c#

I am trying to create a dynamic model for a set of questions that are pulled dynamically into my app.
I want to set my input types equal to a question id (which is unique). Then I want to associate those answers with a value field in my model.
I have this model:
public class QuestionViewModel
{
public int? Id { get; set; }
public string QuestionType { get; set; }
public string Text { get; set; }
public List<QuestionOptionViewModel> Options { get; set; }
}
public class QuestionOptionViewModel
{
public int? Id { get; set; }
public string Text { get; set; }
public string StringValue { get; set; }
public bool OptionValue {get; set;}
}
So I tried to do a texboxfor like below. I was hoping that this would set the form name = "id" and then set the value to my model property StringValue. But it doesn't work.
#for (int i = 0; i < #Model.Options.Count; i++)
{
<div class="col-lg-4">
<label for="#Model.Options[i].Text">#Model.Options[i].Text</label>
#Html.TextBoxFor(o => Model.Options[i].StringValue, new { name = Model.Options[i].Id, #class = "form-control", placeholder = "" })
</div>
}
Let me walk you through the model being given to me pseudo code style. It should explain what I want to do.
QuestionViewModel
int = 1
questionType = Text
Text = What is your name?
QuestionOptionViewModel
int = 1
Text = FirstName
StringValue = *I want to set the response here*
OptionValue = *If it was a radio I want to store bool here*
int = 2
Text = LastName
StringValue = *I want to set the response here*
OptionValue = *If it was a radio I want to store bool here*
So I added into the model the StringValue part. I want to catch the responses there so I can dynamically have a trace that this questions input box would have the name of QuestionOption int. And store the value in StringValue (since text in this example).
I am confused on the appropriate way to do this in C# MVC? What would be the correct way to handle dynamically created questions that are pulled into a web page. I can easily pull them out using my model. But I am unsure the proper way to store them back into the model.
As requested here is a screen grab of a single question.

Related

Why SelectList returning Null value in model when submitting post?

I'm trying to submit a form using Html helper like this:
#using (Html.BeginForm("Create", "Certificate", FormMethod.Post, new { enctype = "multipart/form-data", id = "complianceCertificate" }))
Onload of page, I'm binding the dropdown using HTML helper like this
#Html.DropDownListFor(model => model._DetailsOfOwner.Id, Model._DetailsOfOwner.Practices, "Select licensee name...", new { #class = "form-control tx-13 pd-2 has-check-valid", #name = "LicenseeId" })
#Html.ValidationMessageFor(model => model._DetailsOfOwner.Practices, "", new { #class = "text-danger has-error-valid" })
It loaded correctly
The problem is when submitting the form. It passes null value in model of SelectList.
Here is my class where select list located
public class _DetailsOfOwnerViewModel : BaseAddress
{
public int Id { get; set; }
public SelectList Practices { get; set; }
public string LicenseeName { get; set; }
public string RMLNo { get; set; }
public string ContactPerson { get; set; }
public string Telephone { get; set; }
public string Email { get; set; }
}
Thank you.
You cant return complete SelectList you need to select a single value from dropdown that will be returned to server. You should change your model.
Switch this
public SelectList Practices { get; set; }
to this
public string Practices { get; set; }
A dropdown list when an item has been chosen maps to a single value rather than a list of values so the model isn't being bound properly on submission. Try changing the type of Practicies to an int or a string.
Here, You are binding a dropdown from the model object and may b that's why you take SelectList for practices property. But when you receive form data using the post method at that time you have only a single value selected in the dropdown so you need int or string for that.
So there is two way to fix it:
Bind dropdown by using ViewBag, in which you will assign your list and replace
public SelectList Practices {get;set;}
to
public string Practices {get;set;}
and bind dropdown by using model._DetailsOfOwner.Practices property instead of model._DetailsOfOwner.Id.
OR another way is
You are binding the model._DetailsOfOwner.Id in the dropdown so simply you can get selected value from that property.

Why won't my Edit screen validation allow null?

I added a field to a view model for a Document that should allow the user to associate it with a Tenant. It works fine if the user does assign a tenant, but if they select the null option from the dropdown, then the validation tells me that "The ItemID field is required.", where ItemID is a field on TenantViewModel.
It occurs to me that perhaps I'm using editor templates wrong - I'm trying to select from a list of tenants, not edit a tenant. If that's wrong, let me know, and maybe suggest a better way to get the dropdown.
namespace TenantPortal.Models
{
public class DocumentViewModel
{
...
[UIHint("SelectTenant")]
public TenantViewModel Tenant { get; set; }
}
public class TenantViewModel
{
private Tenant _ten = null;
public int ItemID { get; set; }
public string Display_Name { get; set; }
public string Legal_Name { get; set; }
...
}
}
Editor Template: SelectTenant.cshtml
#using CMS.DocumentEngine.Types.Tenantportal
#using TenantPortal.Models
#model TenantViewModel
#{
Layout = null;
var opts = new SelectList(TenantProvider.GetTenants(), "ItemID", "Display_Name");
}
#Html.DropDownListFor(model => model.ItemID, opts, "(none)")
If you use data annotations you can add validation to your model.
See my example below:
public class TenantViewModel
{
private Tenant _ten = null;
[Required]
public int ItemID { get; set; }
[Required]
[MaxLength(30)]
public string Display_Name { get; set; }
public string Legal_Name { get; set; }
...
}
For further information about data annotations check this
Also, on your code/controller-action side, you need to use ModelState.IsValid check in order to verify whether your model is valid or not
Your ItemID field is an int so it does not allow null values so the model validation fails. Try changing it to int? (a nullable int). If a value is not set in the form, then the value will be null, but if a value is selected, the ItemID will be the selected value.
I ended up adding another property to my document view model named TenantID, having it communicate with the Tenant property behind-the-scenes, and creating SelectLists for TenantID dropdowns on both Create and Edit views. It's less elegant than I would like, but it works.

How do I distinguish RadioButtons?

I have a for-loop with a RadioButton at each iteration. My problem is I don't know how to distinguish every single RadioButton once I validate my form (how to retrieve their value?) . I have a model for printers :
public class Printer{
public int id{ get; get;}
public string printerName { get; set; }
}
Which itself is part of another model :
public class FinalSiteModel
{
public string name{ get; set; }
public List<Printer> printers{ get; set; }
public bool selected { get; set; }
}
Then as I pass it to the view :
for(var i = 0; i < Model.printers.Count() ; i++)
{
#Html.RadioButtonFor(m => m.selected, new { Name = "groupRadio"});
#Html.Label(Model.printers[i].printerName);
}
I think something is wrong with the way I wrote my RadioButton. I initially used the boolean selected inside of the PrinterModel but my problem then came from the fact that the radiobuttons didn't group since I used
#Html.RadioButtonFor(m => m.printers[i].selected, new { Name = "groupRadio"});
and this bit : m => m.printers[i].selected created a different name for each radiobutton, hence when I check several radiobuttons at the same time, which I don't want.
I'm guessing I should add a parameter to RadioButtonFor ?
Your radio buttons need a value to bind to your selected property. However that property needs to be int, not bool
public class FinalSiteModel
{
...
public List<Printer> Printers { get; set; }
public int SelectedPrinter { get; set; }
}
and then in the view
foreach(var printer in Model.Printers)
{
<label>
#Html.RadioButtonFor(m => m.SelectedPrinter, printer.Id, new { id = "" })
<span>#printer.Name</span>
</label>
}
Note the 2nd parameter of RadioButtonFor() adds the value attribute based on the Id property of Printer, and the new { id = "" } removes the id attribute which would otherwise be invalid because the method generates duplicates
The value of SelectedPrinter will now contain the Id of the selected Printer when you POST your form.
As a side note, never attempt to change the name attribute using new { Name = "groupRadio" } - it would not bind to your model.

What is available in the controller from a post action in a dropdownlist

Trying to unpack some inherited code and I am new to ASP.NET. My question:
What is available in the controller from a post action in a dropdownlist in C# (ASP.NET MVC5)?
Here is what I have in the view:
#using (Html.BeginForm("SignUp", "Location", FormMethod.Post))
....
#Html.DropDownListFor(
model => model.Member.PillarId, new SelectList (Model.Pillars, "Id", "Title"))
Here is the MemberViewModel:
public class MemberViewModel : IValidatableObject{
public int? LocationId { get; set; }
public int? PillarId { get; set; }
}
Here is the Member model:
public class Member
{
public int Id { get; set; }
public string Name { get; set; }
public int? LocationId { get; set; }
public int? PillarId { get; set; }
public String PillarTitle { get; set; }
Here is the Member model constructor(s):
public Member() { }
public Member(MemberViewModel member)
{
PillarId = member.PillarId;
///
}
Here is the Controller
public ActionResult SignUp(MemberViewModel member){///}
My form is correctly pulling the information from the DB to display, as well as posting correctly to the DB via the Controller. I do not want to change the visual options for the user to choose from (i.e. I think ListBox is out?).
Rather, I want to assign both Member.PillarId as well as the Member.Title based on their choice and have it available in the Controller for non-DB operations.
What is currently available in the SignUp method in the Controller? Can I call Model.Pillars.Title? Is it member.PillarId.Pillars.Id? If not, how can I assign it dynamically based on the user choice?
There are views and modelviews flying around in this code, and I am not sure what is available...
SO has a bunch of answers on the DropDownList, so here's a sampling of articles that are somewhat related to what I am getting at...
* This answer
* ListBox
* ListBoxFor: not MVC dynamic
* SelectList Constructor: Not MVC
With a dropdownlist the only value that will come back is the value of the selected item when posting back.
If you want something else to come back you would need a hidden field on the page and bind a change event listener to the dropdown to set the title in the hidden field
public class MemberViewModel : IValidatableObject{
public int? LocationId { get; set; }
public int? PillarId { get; set; }
public string Title { get; set;}
}
#using (Html.BeginForm("SignUp", "Location", FormMethod.Post))
....
#Html.DropDownListFor(
model => model.Member.PillarId, new SelectList (Model.Pillars, "Id", "Title"))
#Html.HiddenFor(model => model.Title);
javascript
$('#idofdropdown').on('change', function()
{
var selectedTitle = $(this).find(":selected").text();
$('#Title').val(selectedTitle);
});
How to get selected title from a drop down list: Get selected text from a drop-down list (select box) using jQuery
Then in your controller your viewmodel will have the title text inside the Title string :)

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