How can I choose which property is displayed in Html.DropDownListFor for a some type?
For example I have the following class from which I want to select values
public partial class Artysta
{
public Artysci()
{
this.SpektaklArtysta = new HashSet<SpektaklArtysta>();
}
[Key]
public int ArtystaID { get; set; }
public string Imie { get; set; }
public string Nazwisko { get; set; }
}
Here is a generated code for edit View which sometimes displays Imie and from time to time Nazwisko.
#using (Html.BeginForm())
{
#model Teatr.Models.SpektaklArtysta
<div class="form-group">
#Html.LabelFor(model => model.ArtystaID, "Artysta", htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("ArtystaID", null, htmlAttributes: new { #class = "form-control" })
#Html.ValidationMessageFor(model => model.ArtystaID, "", new { #class = "text-danger" })
</div>
</div>
}
I would like to set displayed property to Nazwisko, how can I achieve that?
UPDATE:
Here is actual model that this View was generated.
public partial class SpektaklArtysta
{
public int SpektaklID { get; set; }
public int ArtystaID { get; set; }
public int RolaArtystyID { get; set; }
[Key]
public int SpektaklArtystaID { get; set; }
public virtual Artysci Artysci { get; set; }
public virtual RolaArtysty RolaArtysty { get; set; }
public virtual Spektakle Spektakle { get; set; }
}
Ok, you need to actually pass a list of possible values to the dropdown list, for example like this:
#Html.DropDownListFor(model => model.ArtystaID, new SelectList(Model.Artysci, "ArtystaID", "Nazwisko", 0))
It says: DropDownList setting models ArtystaID field, which is populated by models Artysci field, which is supposed to contain items that have key under ArtystaID field and display text under Nazwisko field (so, your Artysta class).
Now, your class doesn't have Artysci field, so you have to create it:
public partial class Artysta
{
public Artysci()
{
this.SpektaklArtysta = new HashSet<SpektaklArtysta>();
}
[Key]
public int ArtystaID { get; set; }
public string Imie { get; set; }
public string Nazwisko { get; set; }
public List<Artysta> Artysci = ArtistRepository.GetAllArtists();
}
Or you can pass it directly in DropDownList:
#Html.DropDownListFor(model => model.ArtystaID, new SelectList(ArtistRepository.GetAllArtists(), "ArtystaID", "Nazwisko", 0))
Oh, and just a personal note: I know it's probably not your choice, unless you are a lead/sole developer, but please use English names for your classes, variables etc., it will be much easier if in the future someone else will have to work on your code, and they might not speak Polish ;)
One good option for you is to use the DropDownList as following
#Html.DropDownList("DropDownId", Model.Select(item => new SelectListItem
{
Value = item.ArtystaID.ToString(),
Text = item.Nazwisko.ToString(),
Selected = "select" == item.ArtystaID.ToString() ? true : false
}))
Hope that answers your request!
Related
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
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
I have found examples of filtering results from a drop down... but in all the examples I've found the drop downs use the same model as the results. I've attempted to throw the drop down in a partial view with one model and the results in another view with a different model... but it didn't work. This would be very simple in PHP, or WinForms, or MS Access but in MVC it seems very complex to do something so simple.
My models are simple. I have Contacts which can be assigned to Groups with a bridge table/ object ContactGroups:
public class Contacts
{
public int ContactsID { get; set; }
[Display(Name ="First Name")]
[Required]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
[Required]
public string LastName { get; set; }
[Display(Name = "Primary Phone")]
[Phone]
public string PrimaryPhone { get; set; }
[Display(Name = "Phone Type")]
public PhoneType PhoneType { get; set; }
[Display(Name = "PrimaryEmail")]
[EmailAddress]
public string PrimaryEmail { get; set; }
public virtual ICollection<MessageContacts> MessageContacts { get; set; }
public virtual ICollection<ContactGroups> ContactGroups { get; set; }
}
public class Groups
{
public int GroupsID { get; set; }
[Required]
public string GroupName { get; set; }
public virtual ICollection<ContactGroups> ContactGroups { get; set; }
}
public class ContactGroups
{
public int ContactGroupsID { get; set; }
[Required]
public int ContactID { get; set; }
[Required]
public int GroupID { get; set; }
}
I'm trying to populate the drop down with available groups and filter all contacts who are in the groups from the drop down selection. I imagine this is probably simple for you who are experienced with EF so I am very appreciative of your help!
Notes that POHeaderId drop down choices are populated in the documenmt ready...
So, once they select a POheaderID it populates the OldLocation wit the relevent choices as per returned by the action.
Here's an outline - maybe you can do a similar thing or may be you find a more suitable solution.
Part of view...
<div class="form-group form-group-no-bott-marg">
#Html.LabelFor(model => model.PoHeaderId, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("PoHeaderId", String.Empty)
#Html.Label("NB: XXXXXX moved by specific Purchase Order", new { style = "color: red" })
</div>
</div>
<div class="form-group form-group-no-bott-marg">
#Html.LabelFor(model => model.OldLocationId, new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownList("OldLocationId", String.Empty)
#Html.ValidationMessageFor(model => model.OldLocationId)
</div>
</div>
Part of javascript......for when the POHader is changed.....
$('#PoHeaderId').change(function() {
$('#confirm').text('');
var poId = $("#PoHeaderId :selected").val();
var items = [];
//Call the action to get the list in JSON format
var url = "#Url.Action("Locations", "Utilities")";
//alert(url);
window.ShowOrHideProgress(true);
$.ajax({
url: url,
type: 'GET',
cache: false,
data: { locationId: 0, poHeaderId: poId },
success: function (resultantJsonData) {
for (var i = 0; i < resultantJsonData.length; i++) {
items.push("<option value=" + resultantJsonData[i].Value + ">" + resultantJsonData[i].Text + "</option>");
}
$('#OldLocationId').html(items.join(' '));
}
});
});
The action it calls.....
public ActionResult Locations(int locationId, int poHeaderId = 0)
{
var locList = StockMovesHeaderViewModelHelper.GetLocations(db, locationId, poHeaderId);
var tmpList = new SelectListWithSelect(true);
foreach (var l in locList)
{
tmpList.Add(new SelectListItem { Text = l.Description, Value = l.FulfilmentLocationID.ToString() });
}
return Json(tmpList, JsonRequestBehavior.AllowGet);
}
I’m trying to create a team of players, there are 4 players per team so I have 4 select lists that contain and populate the players in the league. When I go to save the team the error:
"There is no ViewData item of type 'IEnumerable' that has the key 'Skip'`
The CreateTeam controller:
public ActionResult CreateLeagueTeam(int? id)
{
int leagueId = id.Value;
League league = db.Leagues.Include(x=>x.Members).First(l=> l.Id == leagueId);
var leagueMemberList = league.Members.ToList();
var leagueMemberSelectListItems = new List<SelectListItem>();
foreach(Member m in leagueMemberList)
{
leagueMemberSelectListItems.Add(new SelectListItem() { Text = m.FirstName + " " + m.LastName, Value = m.Id.ToString() });
}
ViewData["leagueMemberList"] = leagueMemberSelectListItems;
return View();
}
the HTTP POST ActionResult:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult CreateLeagueTeam([Bind(Include = "skip")] Team team)
{
team.IsBonspiel = false;
team.IsLeague = true;
// team.Name = team.Skip.LastName;
team.YearActive = games.getSeasonYear();
//finds and sets the league ID for the team
int leagueId = (int)Session["leagueId"];
League league = db.Leagues.Find(leagueId);
team.League = league;
if (ModelState.IsValid)
{
db.Teams.Add(team);
db.SaveChanges();
return RedirectToAction("Index");
}
return View(team);
}
Finally the view where the Error is getting thrown:
<div class="form-group">
#Html.Label("Skip", null, htmlAttributes: new { #class = "control-label col-md-2" })
<div class="col-md-10">
#Html.DropDownListFor(model => model.Skip, (IEnumerable<SelectListItem>)ViewBag.leagueMemberList, htmlAttributes: new { #class = "control-label col-md-2" })
</div>
</div>
Also the Team Model:
public class Team: Entity
{
[Required]
public bool IsBonspiel { get; set; }
[Required]
public bool IsLeague { get; set; }
[Required]
public string Name { get; set; }
[Required]
public int YearActive { get; set; }
public int? SkipId { get; set; }
public Member Skip { get; set; }
public string BonspielSkip { get; set; }
public List<BonspielTeamMember> BonspielMembers { get; set; }
public List<Member> Members { get; set; }
public List<Game> Games { get; set; }
public Bonspiel Bonspiel { get; set; }
public League League { get; set; }
}
The are multiple errors with your code.
Skip is a complex object and you cannot bind a <select> to a
complex object. Based on your view, it should be
#Html.DropDownListFor(m => m.SkipId, ...)
You do not have a ViewBag property named leagueMemberList
(change the controller code to ViewBag.leagueMemberList =
leagueMemberSelectListItems; or the view code to
#Html.DropDownListFor(m => m.SkipId,
(IEnumerable<SelectListItem>)ViewData["leagueMemberList"], ...)
You do not reassign the value of ViewData["leagueMemberList"] (or
ViewBag.leagueMemberList) in the POST method if ModelState is
invalid and you return the view, which will result in a different
exception because it will be null (Refer this
question/answer
Your [Bind(Include = "skip")] code in the POST method means that
ModelState will always be invalid because you exclude every
property of the model from binding (except Skip and you dont post
any values for Skip)
You added data to "ViewData" but you try to access from ViewBag.
You should know ViewData is different with ViewBag.
Therefore you try to change this line:
ViewData["leagueMemberList"] = leagueMemberSelectListItems;
to => ViewBag.leagueMemberList = leagueMemberSelectListItems;
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