I'm trying to populate new HTML5 input attribute values into a Razor partial view. My model looks like this:
public class Answer
{
public int AnswerId { get; set; }
public string AnswerText { get; set; }
public int? Columns { get; set; }
public int? Maximum { get; set; }
public string Placeholder { get; set; }
}
My partial view looks like this:
#model Answer
#{Layout = null;}
#Html.TextBoxFor(x => x.AnswerText, new { #class="textbox", cols="#Model.Columns", max="#Model.Maximum", placeholder="#Model.Placeholder" })
Probably unsurprisingly this generates html that looks like this (I've left out some irrelevant attributes):
<input type="text" class="textbox" cols="#Model.Columns", max="#Model.Maximum", placeholder="#Model.Placeholder">
Whereas what I'm after is html that looks like this, but with whatever value happpens to be in the model:
<input type="text" class="textbox" cols="50", max="30", placeholder="Answer here">
I'm sure this is me having a Homer Simpson moment but I just can't get it to work.
Take out the quotes and use GetValueOrDefault on the nullable fields:
#Html.TextBoxFor(x => x.AnswerText, new { #class="textbox", cols=Model.Columns.GetValueOrDefault(), max=Model.Maximum.GetValueOrDefault(), placeholder=Model.Placeholder })
Related
I am developing a page for rating questions.
In the view, I have a list of questions and 5 radio buttons in front of each one of them.
<input name="evalId" type="hidden" value="#Model.Evaluation.EvalId" />
foreach (var question in questionList)
{
<input name="questionId" type="hidden" value="#question.QuestionId" />
<div class="row_star" style="border-bottom : 0 none; background: none;">
#if (!String.IsNullOrEmpty(question.QuestionTitre))
{
<p>#question.QuestionTitre.TrimEnd()</p>
}
#* here goes the code for 5 radio buttons*#
}
Now, in my controller I want to be able to know which radio button was checked for each question.
How can I do that ?
Here is my ViewModel
public class EvaluationViewModel
{
/// <summary>
///
/// </summary>
public EvalEvaluation Evaluation
{
get;
set;
}
/// <summary>
///
/// </summary>
public Dictionary<EvalQuizz, List<EvalQuestion>> EvalQuizzQuestionList
{
get;
set;
}
}
Assuming your ViewModel is like this
public class Question
{
public int ID { set; get; }
public string QuestionText { set; get; }
public List<Answer> Answers { set; get; }
public int SelectedAnswer { set; get; }
public Question()
{
Answers = new List<Answer>();
}
}
public class Answer
{
public int ID { set; get; }
public string AnswerText { set; get; }
}
public class Evaluation
{
public List<Question> Questions { set; get; }
public Evaluation()
{
Questions = new List<Question>();
}
}
And in your GET action method, you will return the viewmodel back to the view with some questions and answers filled in it. In the code below I've hardcoded the questions and answers. You may get it from your Repositary/Service layer.
public ActionResult Index()
{
var evalVM = new Evaluation();
//the below is hardcoded for DEMO. you may get the data from some
//other place and set the questions and answers
var q1=new Question { ID=1, QuestionText="What is your favourite language"};
q1.Answers.Add(new Answer{ ID=12, AnswerText="PHP"});
q1.Answers.Add(new Answer{ ID=13, AnswerText="ASP.NET"});
q1.Answers.Add(new Answer { ID = 14, AnswerText = "Java" });
evalVM.Questions.Add(q1);
var q2=new Question { ID=2, QuestionText="What is your favourite DB"};
q2.Answers.Add(new Answer{ ID=16, AnswerText="SQL Server"});
q2.Answers.Add(new Answer{ ID=17, AnswerText="MySQL"});
q2.Answers.Add(new Answer { ID=18, AnswerText = "Oracle" });
evalVM.Questions.Add(q2);
return View(evalVM);
}
Now we will create an Editor Template to render our Question. so go to your View Folder and create a folder called EditorTemplates under the folder with your current controller name.
Add a view to the EditorTemplates folder and give the same name as the class name we want to represent. ie : Question.cshtml
Now put this code in the editor tempalte
#model YourNameSpace.Question
<div>
#Html.HiddenFor(x=>x.ID)
#Model.QuestionText
#foreach (var a in Model.Answers)
{
<p>
#Html.RadioButtonFor(b=>b.SelectedAnswer,a.ID) #a.AnswerText
</p>
}
</div>
Now go to our main view and use EditorTemplate html helper method to bring the EditorTemplate we created to the main view.
#model YourNameSpace.Evaluation
<h2>Index</h2>
#using (Html.BeginForm())
{
#Html.EditorFor(x=>x.Questions)
<input type="submit" />
}
Now in your HttpPost you can check the posted model and get the selected radio button (SelectedAnswer) value there
[HttpPost]
public ActionResult Index(Evaluation model)
{
if (ModelState.IsValid)
{
foreach (var q in model.Questions)
{
var qId = q.ID;
var selectedAnswer = q.SelectedAnswer;
//Save
}
return RedirectToAction("ThankYou"); //PRG Pattern
}
//reload questions
return View(model);
}
If you use visual studio breakpoints, you can see the values posted. Thanks to MVC Model binding :)
You can read about it and download a working sample here.
I have two classes as follows
public class ODCTE_Major
{
public int ODCTE_MajorId { get; set; }
public string OfficialMajorName { get; set; }
public string MajorCode { get; set; }
... More unrelated code ....
}
AND
public class CareerMajor
{
...lots of unrealted code to this question left out
public int ODCTE_MajorId { get; set; }
public virtual ODCTE_Major ODCTE_Major { get; set; }
}
I added a controller with CRUD methods and in the create.cshtml there is this line
<div class="editor-field">
#Html.DropDownList("ODCTE_MajorId", String.Empty)
#Html.ValidationMessageFor(model => model.ODCTE_MajorId)
</div>
The select list populates it with the OfficialMajorName from ODCTE_Major. I need the select list to populate with the MajorCode or a value that looks like MajorCode - OfficialMajorName.
Could someone provide assistance for how this is done, please?
Thanks.
Add this to ODCTE_Major:
public string MajorDisplayName
{
get { return string.Format("{0} - {1}", MajorCode, OfficialMajorName); }
}
This is just a read only property used to create the display text in the format you want the menu to use.
Then in CareerMajor, add:
public IEnumerable<ODCTE_Major> Majors{ set; get; } // Thank you Shyju!
This will give you a place in your view model to pass the list of Majors you want in your menu to the view.
Then in your action method when you're creating a CareerMajor view model to send to the view, populate the new IEnumberable with the ODCTE_Major entities you'd like displayed in your menu.
On the view page:
#Html.DropDownListFor(m => m.ODCTE_MajorId, new SelectList(Model.Majors, "ODCTE_MajorId", "MajorDisplayName", Model.ODCTE_MajorId), "Select One")
This creates a SelectList to populate the drop down with. The SelectList constructor is saying use ODCTE_MajorId as the value for a SelectListItem in the menu, and to use MajorDisplayName as the text to actually display in the menu. It sets the selected value, if there is one, and adds a null item with the text "Select One" to the top of the menu. Feel free to take that final argument out if you don't want the null text.
Have your ViewModel hold a Collection property to represent all available Majors (for poulating the Dropdown)
public class CareerMajor
{
//other proiperties
public int ODCTE_MajorId { get; set; }
public IEnumerable<ODCTE_Major> Majors{ set; get; }
}
And in your GET Action, fill it and send it to your strongly typed view
pubilc ACtionResult Create()
{
var viewModel=new CareerMajor();
viewModel.Majors=db.GetllAllMajors(); // getting a list of ODCTE_Major objects
return View(viewModel);
}
and in the View, use the DropDownListFor HTML Helper method.
#model CareerMajor
#Html.BeginForm())
{
#Html.DropDownListFor(m=>m.ODCTE_MajorId,
new SelectList(Model.Majors,"ODCTE_MajorId ","MajorCode"),"select one..")
//other elements
}
In your controller action:
ViewBag.ODCTE_MajorId = new SelectList(availableMajors, "ODCTE_MajorId", "MajorCode");
*second and third parameters are the names of the value and text fields respectively
Then in your view:
#Html.DropDownList("ODCTE_MajorId", String.Empty)
where availableMajors is an IEnumerable that contains the majors you want to list.
I am trying to extend ProDinner by adding phone numbers to Chef.
ChefInput view model:
public class ChefInput :Input
{
public string Name { get; set; }
public ChefInput()
{
PhoneNumberInputs = new List<PhoneNumberInput>(){
new PhoneNumberInput()
};}
public IList<PhoneNumberInput> PhoneNumberInputs { get; set; }
}
PhoneInput view model:
public class PhoneNumberInput :Input
{
public string Number { get; set; }
public PhoneType PhoneType { get; set; } <-- an enum in Core project
}
Chef Create.cshtml file:
#using (Html.BeginForm())
{
#Html.TextBoxFor(o => o.Name)
#Html.EditorFor(o => o.PhoneNumberInputs)
}
PhoneNumberInput.cshtml in EditorTemplate folder:
#using (Html.BeginCollectionItem("PhoneNumberInputs"))
{
#Html.DropDownListFor(m => m, new SelectList(Enum.GetNames(typeof(PreDefPhoneType))))
#Html.TextBoxFor(m => m.Number)
}
When debugging and I stop it at Create in Crudere file, the Phone collection is null.
Anyone have any ideas?
Thanks in Advance.
Joe,
You don't show your controller logic but I've got a feeling you're getting null because you're not populating the PhoneNumberInputs ViewModel. From what I can see, all you're doing is newing up the list in the model. Ensure that you fill this 'list' in your controller from the database (with the appropriate values) and i'm certain all will work as planned.
[edit] - in answer to comment. don't know what the prodinner controllers etc look like but something alsong these lines:
public ActionResult Edit(int id)
{
var viewModel = new ChefInput();
viewModel.ChefInput = _context.GetById<ChefModel>(id);
viewModel.PhoneNumberInputs = _context.All<PhoneNumberInput>();
return View(viewModel);
}
as i said, not sure of the prodinner setup, but this is what i meant.
I have the following code that allows a teacher to see a drop-down list of available courses to teach, listed by name. When a teacher selects a dropdown option I would like a form on the view to auto-populate with default values representing the selected course. What is the most efficient way to populate the fields?
note: When "Custom" is selected in the drop-down, I want the form that is displayed below the dropdown to have nothing but blank spaces.
CourseController
// GET: /Course/ApplyToTeach
public ActionResult ApplyToTeach()
{
var course = db.CourseTemplates;
var model = new ApplyCourseViewModel
{
Courses = course.Select(x => new SelectListItem
{
Value = x.Title,
Text = x.Title,
})
};
return View(model);
}
ApplyToTeachViewModel
public class ApplyToTeachViewModel
{
[Display(Name = "selected course")]
public string SelectedCourse { get; set; }
public IEnumerable<SelectListItem> Courses { get; set; }
}
ApplyToTeach (view) - note that all I have here currently is the drop-down menu, I am looking for the most efficient way to add the auto-populating form below this drop-down.
<h2>ApplyToTeach</h2>
#using (Html.BeginForm())
{
#Html.ValidationSummary(true)
<fieldset>
<legend>Apply To Teach</legend>
<div class="editor-label">
Which class would you like to teach? (select "Custom" if you would like to submit a customized class)
</div>
<div class="editor-field">
#Html.DropDownListFor(x => x.SelectedCourse, Model.Courses, "Custom")
#Html.ValidationMessageFor(model => model.Courses)
</div>
<p>
<input type="submit" value="Submit" />
</p>
</fieldset>
}
The data for the drop-down fields comes from the following model -
CourseTemplates
public class CourseTemplates
{
public int CourseTemplatesID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public int AttendingDays { get; set; } // 10, 8, 3, or custom
public int AttendanceCap { get; set; } // default of 30
public string Location { get; set; }
public string Description { get; set; }
}
The form is actually going to be submitted as a "Course" model, not the "CourseTemplates" model, the "Course" model has more data fields than the "CourseTemplates" model - such as the following:
public DateTime StartDate { get; set; }
public bool Approved { get; set; }
etc. . .
What I have in mind as far as user-experience is that an administrator will go through beforehand and add in a number of possible course options, simply to ease the application process for most teachers (so they don't have to type every detail for every class they apply to teach), but I want the teacher to be able to edit any information before submitting the course for review by an administrator.
Any tips?
I am building a system which asks questions and receives answers to them. Each question can have an aswer of its own type. Let's limit it to String and DateTime for now. In Domain, question is represented the following way:
public class Question
{
public int Id
{
get;
set;
}
public string Caption
{
get;
set;
}
public AnswerType
{
get;
set;
}
}
, where AnswerType is
enum AnswerType
{
String,
DateTime
}
Please note that actually I have much more answer types.
I came up with an idea of creating a MVC model, deriving from Question and adding Answer property to it. So it has to be something like this:
public class QuestionWithAnswer<TAnswer> : Question
{
public TAnswer Answer
{
get;
set;
}
}
And here start the problems. I want to have a generic view to draw any question, so it needs to be something like that:
#model QuestionWithAnswer<dynamic>
<span>#Model.Caption</span>
#Html.EditorFor(m => m.Answer)
For String I want to have simple input here, for DateTime I am going to define my own view. I can pass the concrete model from the controller. But the problem is that on the rendering stage, naturally, it cannot determine the type of Answer, especially if it is initially null (default for String), so EditorFor draws nothing for String and inputs for all properties in DateTime.
I do understand the nature of the problem, but is there any elegant workaround? Or I have to implement my own logic for selecting editor view name basing on control type (big ugly switch)?
Personally I don't like this:
enum AnswerType
{
String,
DateTime
}
I prefer using .NET type system. Let me suggest you an alternative design. As always we start by defining out view models:
public abstract class AnswerViewModel
{
public string Type
{
get { return GetType().FullName; }
}
}
public class StringAnswer : AnswerViewModel
{
[Required]
public string Value { get; set; }
}
public class DateAnswer : AnswerViewModel
{
[Required]
public DateTime? Value { get; set; }
}
public class QuestionViewModel
{
public int Id { get; set; }
public string Caption { get; set; }
public AnswerViewModel Answer { get; set; }
}
then a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
var model = new[]
{
new QuestionViewModel
{
Id = 1,
Caption = "What is your favorite color?",
Answer = new StringAnswer()
},
new QuestionViewModel
{
Id = 1,
Caption = "What is your birth date?",
Answer = new DateAnswer()
},
};
return View(model);
}
[HttpPost]
public ActionResult Index(IEnumerable<QuestionViewModel> questions)
{
// process the answers. Thanks to our custom model binder
// (see below) here you will get the model properly populated
...
}
}
then the main Index.cshtml view:
#model QuestionViewModel[]
#using (Html.BeginForm())
{
<ul>
#for (int i = 0; i < Model.Length; i++)
{
#Html.HiddenFor(x => x[i].Answer.Type)
#Html.HiddenFor(x => x[i].Id)
<li>
#Html.DisplayFor(x => x[i].Caption)
#Html.EditorFor(x => x[i].Answer)
</li>
}
</ul>
<input type="submit" value="OK" />
}
and now we can have editor templates for our answers:
~/Views/Home/EditorTemplates/StringAnswer.cshtml:
#model StringAnswer
<div>It's a string answer</div>
#Html.EditorFor(x => x.Value)
#Html.ValidationMessageFor(x => x.Value)
~/Views/Home/EditorTemplates/DateAnswer.cshtml:
#model DateAnswer
<div>It's a date answer</div>
#Html.EditorFor(x => x.Value)
#Html.ValidationMessageFor(x => x.Value)
and the last piece is a custom model binder for our answers:
public class AnswerModelBinder : DefaultModelBinder
{
protected override object CreateModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Type modelType)
{
var typeValue = bindingContext.ValueProvider.GetValue(bindingContext.ModelName + ".Type");
var type = Type.GetType(typeValue.AttemptedValue, true);
var model = Activator.CreateInstance(type);
bindingContext.ModelMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, type);
return model;
}
}
which will be registered in Application_Start:
ModelBinders.Binders.Add(typeof(AnswerViewModel), new AnswerModelBinder());
You can still use the Html.EditorFor(..), but specify a second parameter which is the name of the editor template. You have a property on the Question object that is the AnswerType, so you could do something like...
#Html.EditorFor(m => m.Answer, #Model.AnswerType)
The in your EditorTemplates folder just define a view for each of the AnswerTypes. ie "String", "DateTime", etc.
EDIT: As far as the Answer object being null for String, i would put a placeholder object there just so the model in you "String" editor template is not null.