Hi there I have a table of jobs with a specific id for them
id title
1 Manager
2 Engineer
3 IT
I have created a way so that I am able to load these value from a database with this table and data.
Here is my model
[Display(Name = "Type: "),
Required(ErrorMessage = "Required")]
public IEnumerable<SelectListItem> jobTitle { get; set; }
Here is my controller
public void loadDropDown()
{
JobModel selectedJob = new JobModel();
reviewlogsEntities db = new reviewlogsEntities();
IEnumerable<SelectListItem> type = db.types.Select(m => new SelectListItem
{
Value = SqlFunctions.StringConvert((double)m.id),
Text = m.title
});
ViewBag.JobTitle = type;
}
[HttpGet]
public ActionResult DisplayJobTitle()
{
if (Request.IsAuthenticated)
{
loadDropDown();
return View();
}
else
{
return RedirectToAction("index", "home");
}
}
[HttpPost]
public ActionResult DisplayJobTitle(JobModel curJob)
{
loadDropDown();
if (ModelState.IsValid)
{
return View();
}
else
{
ModelState.AddModelError("", "Looks like there was an error with your job title, please make sure a job title is selected.");
}
return View();
}
And Lastly what my view looks like:
#Html.ValidationSummary(true, "Please try again.")
#using (Html.BeginForm())
{
#Html.LabelFor(model => model.type)
#Html.DropDownList("JobTitle")
<input class="submitButton" type="submit" value="Show Job Info" style="margin-left:126px;margin-bottom: 20px;" />
}
See the problem is that variable jobTitle in my model is null because I never give it a value and since I don't give it a value the form thinks it is not filled out since it must be required and will not submit properly. My question is how do I return the value of whatever the users selected as their job title back to jobTitle variable when the form is submitted so that I won't get a submission failure.
Model should accept selected value not collection of items:
[Display(Name = "Type: "),
Required(ErrorMessage = "Required")]
public int jobTitle { get; set; }
You need a property to map selected value in your model like:
[Display(Name = "Type: "),
Required(ErrorMessage = "Required")]
public int SelectedjobTitle { get; set; }
and then in your View:
#Html.DropDownList("SelectedJobTitle",ViewBag.JobTile as IEnumerable<SelectListItem>)
use the DropDownListFor like this:
#Html.DropDownListFor(model => model.JobTitle, Model.Jobs)
on your model:
[Display(Name = "Type: "),
Required(ErrorMessage = "Required")]
public int JobTitle { get; set; }
public IEnumerable<SelectListItem> Jobs { get; set; }
don't forget to fill the "Jobs" on your controller.
Related
Ok, I'm trying to do a proper dropdown in Core 3.1. In this example https://learn.microsoft.com/en-us/aspnet/core/mvc/views/working-with-forms?view=aspnetcore-3.1#the-select-tag-helper
Model has a new list with hardcoded values
public string Country { get; set; }
public List<SelectListItem> Countries { get; } = new List<SelectListItem>
{
new SelectListItem { Value = "MX", Text = "Mexico" },
new SelectListItem { Value = "CA", Text = "Canada" },
new SelectListItem { Value = "US", Text = "USA" },
};
I looked for examples where the list is coming from the database but they are very inconsistent. The only way I was able to do the dropdown list is with the ViewBag which is not advised.
I have two models. 1.
public partial class Glossary
{
public int UniqueId { get; set; }
public int Category { get; set; }
public string DisplayText { get; set; }
}
which is my view model
public partial class AdminUser
{
[Key]
public int Id { get; set; }
public string UserName { get; set; }
public string UserLocation { get; set; }
public string UserStatus { get; set; }
//public IEnumerable<Glossary> Glossary { get; set; } //I used this for ViewBag
public List<SelectListItem> UserLocations { get; } = new List<SelectListItem>
{
according to the example my query should go here
};
}
Here is my controller:
public IActionResult Create()
{
// This is the ViewBag that worked with HTML helpers, but I'm trying to use tag-helpers.
/*IEnumerable<SelectListItem> LocationsList = _context.Glossary.Where(x => x.Category == 1).Select(x => new SelectListItem
{
Value = x.UniqueId.ToString(),
Text = x.DisplayText
});
ViewBag.LocationsList = LocationsList;
*/
return View();
}
All examples that found were hardcoded lists and nothing with getting it from the database. What is the proper way to get the data from the Glossary table through the view model with ViewBag? Any pointers are appreciated.
ALSO:
When using this example: Select Tag Helper in ASP.NET Core MVC
When I used
public SelectList Employees { set; get; }
I got error: InvalidOperationException: The entity type 'SelectListGroup' requires a primary key to be defined. If you intended to use a keyless entity type call 'HasNoKey()'.
Both of my tables have PK and adding [Key] to Glossary model didn't fix it.
If you'd like to retrieve data from db and populate a dropdown with retrieved data through a view model (or ViewBag), you can refer to following code snippet.
In AdminUser view model class, include these properties
public string Selected_Glossary { get; set; }
public List<SelectListItem> Glossary_List { get; set; }
In controller
public IActionResult Create(AdminUser model)
{
var adminuser_model = new AdminUser
{
UserName="test"
//for other properties
};
//retrieve data from Glossary table
var items = _context.Glossary.Where(x => x.Category == 1).Select(x => new SelectListItem
{
Value = x.UniqueId.ToString(),
Text = x.DisplayText
}).ToList();
//pass dropdown items through a view model
adminuser_model.Glossary_List = items;
////pass dropdown items through ViewBag
//ViewBag.Glossary_List = items;
return View(adminuser_model);
}
In view page
#model AdminUser
#{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<form asp-controller="Home" asp-action="Create" method="post">
<select asp-for="Selected_Glossary" asp-items="Model.Glossary_List"></select>
#*populate it through ViewBag*#
#*<select asp-for="Selected_Glossary" asp-items="ViewBag.Glossary_List"></select>*#
<input type="submit" value="Submit" />
</form>
Test Result
What I have is a form with multiple inputs that I want to use to query database for some results. Form has some default values and it all works, however I have problem submitting it to itself.
The returned error is "No paramaterless constructor defined for this object" and it is caused by SelectList object.
I have tried this solution and made psUserType private with getter and setter and intialized it as empty list, but then my dropDown menu had no values on start. Not sure why GetUserTypes hadn't filled them.
What am I doing wrong here? How does one have both preselected values and also send the same model with user-selected values, while also displaying results on the same page?
Does it make sense to use the same model for all 3 actions: 1. display form and inputs with default values 2. post selected values during submit 3. return results and selected values? I've read this solution also but not sure how to use 2 or 3 separate models here.
Any help is appreciated. Thanks in advance.
Model
public class SearchDownloadsModel
{
public SelectList psUserType { get; private set; } //causes problem on submit
public string psText { get; set; }
public MultiSelectList psColumns { get; private set; }
public IEnumerable<ResultsRowModel> psResults { get; set; }
public SearchDownloadsModel()
{
this.psUserType = GetUserTypes();
this.psColumns = GetColumns();
this.psResults = new List<ResultsRowModel>(); //empty by default
}
public SelectList GetUserTypes()
{
List<SelectListItem> items = new List<SelectListItem>()
{
new SelectListItem { Value="user", Text="Single User" },
new SelectListItem { Value="group", Text="User group" },
...
};
return new SelectList(items, "Value", "Text");
}
public MultiSelectList GetColumns()
{
List<SelectListItem> items = new List<SelectListItem>()
{
new SelectListItem { Value = "user", Text="Username" },
new SelectListItem { Value = "file", Text="Filename" },
new SelectListItem { Value = "titl", Text="Title" },
new SelectListItem { Value = "auth", Text="Author" },
...
};
return new MultiSelectList(items, "Value", "Text");
}
}
public class ResultsRowModel
{
public int ID { get; set; }
public string EventTime { get; set; }
public string FileName { get; set; }
public string FilePath { get; set; }
public string UserName { get; set; }
...
}
View
#model Proj.Models.SearchDownloadsModel
#using (Html.BeginForm("Downloads", "Home", FormMethod.Post))
{
#Html.DropDownListFor(x => x.psUserType, Model.psUserType)
#Html.TextBoxFor(x => x.psText)
#Html.ListBoxFor(x => x.psColumnsSelected, Model.psColumns, new { multiple = "multiple" })
<button type="submit" class="btn btn-primary">Search</button>
}
#if (Model.psResults != null && Model.psResults.Any())
{
<table>
<tr>
<th>User</th>
<th>File</th>
</tr>
#foreach (var row in Model.psResults)
{
<tr>
<td>#row.UserName</td>
<td>#row.FileName</td>
</tr>
}
</table>
}
Controller
[HttpGet]
public ActionResult Downloads()
{
SearchDownloadsModel model = new SearchDownloadsModel();
model.psColumnsSelected = new List<string>() { "user", "file" }; //preselected values
return View(model);
}
[HttpPost]
public ActionResult Downloads(SearchDownloadsModel model)
{
model.psResults = queryDatabase(model);
return View(model);
}
private List<ResultsRowModel> queryDatabase(SearchDownloadsModel model)
{
//...
}
EDIT: Added ResultsRowModel under SearchDownloadsModel
In ASP.NET MVC you should only put variables containing the posted or selected values in the ViewModel class. Select List items are considered extra info and are typically passed from the Action Method into the View (.cshtml) using ViewBag items.
Many of the rendering extension methods are even written specifically for such an approach, leading to code such as this:
Controller
ViewBag.PersonID = persons.ToSelectList(); // generate SelectList here
View
#Html.DropDownListFor(model => model.PersonID)
#* The above will look for ViewBag.PersonID, based on the name of the model item *#
The DropDownListFor generates a <select> element with the name of the property you bind it to. When you submit the form, that name will be included as one of the form fields and its value will be the option's value you select.
You're binding the DropDownList to a property of type SelectList (psUserType) and when your action is called, a new instance of SelectList must be created in order to bind the form field to it. First of all, the SelectList class does not have a parameterless constructor and, thus, your error. Secondly, even if a SelectList could be created as part of model binding, the <select> element is submitting a string value which wouldn't be convertible to SelectList anyways.
What you need to do is to add a string property to your SearchDownloadsModel, for example:
public string SelectedUserType { get; set; }
Then bind the dropdownlist to this property:
#Html.DropDownListFor(x => x.SelectedUserType, Model.psUserType)
When you submit the form, this new property will have the value you selected in the drop down.
Peter's answer and Stephen's comments helped me solve the problem.
Pehaps someone will find it useful.
Any further suggestions always welcome.
Model
public class PobraniaSzukajModel
{
public IEnumerable<SelectListItem> UserTypes { get; set; }
public string psSelectedUserType { get; set; }
public IEnumerable<SelectListItem> Columns { get; set; }
public IEnumerable<string> psSelectedColumns { get; set; }
public string psText { get; set; }
public ResultsModel psResults { get; set; }
}
View
#Html.ListBoxFor(x => x.psSelectedUserType, Model.Columns)
#Html.TextBoxFor(x => x.psText)
#Html.ListBoxFor(x => x.psSelectedColumns, Model.Columns)
Controller
[HttpGet]
public ActionResult Downloads()
{
SearchDownloadsModelmodel = new SearchDownloadsModel();
model.UserTypes = GetUserTypes();
model.Columns = GetColumns();
model.psColumnsSelected = new List<string>() { "user", "file" }; //preselected values
return View(model);
}
[HttpPost]
public ActionResult Downloads(SearchDownloadsModel model)
{
model.UserTypes = GetUserTypes();
model.Columns = GetColumns();
model.psResults = GetResults(model);
return View(model);
}
public SelectList GetUserTypes()
{
//...
}
public MultiSelectList GetColumns()
{
//...
}
public ResultsModel GetResults()
{
//...
}
I am learning LINQ and was wondering how can I INSERT into two different tables with no relationship on one click using LINQ. Is it even possible?
I am adding into one table like this. How can I add into the second table as well?
Second Table,
GenreId
Name
Description
Code,
[HttpPost]
public ActionResult Create(Artist artist)
{
if (ModelState.IsValid)
{
_db.Artists.Add(artist);
_db.Genres.Add(new Genre { GenreId = 1, Name = "Some Genre", Description = "Trying to add second table" }); // how can i add the genre object here
_db.SaveChanges();
return RedirectToAction("Index", new { id = artist.ArtistId });
}
return View(artist);
}
Note that my View is strongly typed to Artist class.
My View,
#model EntityFrameWorkDBFirst.Models.Artist
#Html.TextBoxFor(model => model.Name)
#Html.TextBoxFor(model =>model.Genres) // how to get this working
My Model Class,
public partial class Artist
{
public Artist()
{
this.Albums = new HashSet<Album>();
this.Genres = new HashSet<Genre>();
}
public int ArtistId { get; set; }
public string Name { get; set; }
public virtual ICollection<Album> Albums { get; set; }
public virtual ICollection<Genre> Genres { get; set; }
}
Tuple related suggestion didn't work. Maybe I am missing something.
#model Tuple<EntityFrameWorkDBFirst.Models.Artist, EntityFrameWorkDBFirst.Models.Genre>
<legend>Create a Artist</legend>
#Html.TextBoxFor(model => model.Item1.Name)
<h2>Genre</h2>
#Html.TextBoxFor(model => model.Item2.Name)
#Html.TextBoxFor(model => model.Item2.Name)
<p>
<input type="submit" value="Create" />
</p>
Controller Code:
[HttpPost]
public ActionResult Create(Artist artist, Genre genre)
{
if (ModelState.IsValid)
{
_db.Artists.Add(artist);
_db.Genres.Add(genre);
_db.SaveChanges();
return RedirectToAction("Index", new { id = artist.ArtistId });
}
return View(artist);
}
You mixed domain model and view model.
It's big mistake, you should work only with viewmodel on view.
You should create view model:
public class CreateArtistViewModel
{
public string ArtistName { get; set; }
public int? Genres { get; set; } // if you provide chooser for user
public string GenresName { get; set; } // if user can enter new genre
public string GenresDecription { get; set; } // if user can enter new genre
public IList<Genre> Genres { get; set; }
}
in the post action you should check view model and create artist and genre if user create new gener.
i recently had the dropdownlist working and everything was fine but for some reason it is now throwing value cannot be null exception. I have swapped variable names around and things trying to resolve the error but has had no effect. Could you please take a look at my code and point me in the right direction. I am a beginner with MVC4 and have been working on this problem for hours now, so any help would be appreciated. I have only posted relevant code thanks in advance.
EDIT: it is throwing the error at start of line #Html.DropDownListFor(....) in the view
Model:
public partial class SiteBookingsTable
{
public string departureAirport { get; set; }
public string arrivalAirport { get; set; }
[Required]
[Display(Name = "Flying From")]
public string chooseDepartureAirport { get; set; }
[Required]
[Display(Name = "Flying To")]
public string chooseArrivalAirport { get; set; }
}
View:
#model Project56.Models.SiteBookingsTable
#{
List<Project56.Models.SiteBookingsTable> selectDepFlight = ViewBag.depList;
}
<tr>
<td>#Html.LabelFor(model => model.chooseDepartureAirport)<br />
#Html.DropDownListFor(model => model.chooseDepartureAirport, new SelectList(selectDepFlight,"departureAirport","departureAirport"))</td>
</tr>
Controller:
public ActionResult Create()
{
List<SiteBookingsTable> selectDepFlight = new List<SiteBookingsTable>();
selectDepFlight.Add(new SiteBookingsTable() { listID = 0, departureAirport = "-Select-" });
selectDepFlight.Add(new SiteBookingsTable() { listID = 1, departureAirport = "London (LTN)" });
selectDepFlight.Add(new SiteBookingsTable() { listID = 2, departureAirport = "Manchester (MAN)" });
ViewBag.depList = selectDepFlight;
return View();
}
[HttpPost]
public ActionResult Create(SiteBookingsTable aBooking)
{
if (ModelState.IsValid == true)
{
newBooking.SiteBookingsTables.Add(aBooking);
newBooking.SaveChanges();
return RedirectToAction("Index");
}
return View(aBooking);
}
in your View you are using SelectList(selectDepFlight,
but you are sending a ViewBag variable ViewBag.depList = selectDepFlight;
you want SelectList(ViewBag.depList,
although honestly it should probably be in the Model, not the ViewBag
If I remember correctly the SelectList type must be enumerable. --Source
** Edit, its probably best to return the value as an enumerable from the controller side instead of in the View itself. Also consider utilizing ViewModels and not the Model itself.
List<Project56.Models.SiteBookingsTable> selectDepFlight = ViewBag.depList;
//Add the following line(s) to cast your list to an enumerable.
IEnumerable<Project56.Models.SiteBookingsTable> enumSelectDepFlight = selectDepFlight.AsEnumerable<Project56.Models.SiteBookingsTable>
//Alter the following code
#Html.DropDownListFor(model => model.chooseDepartureAirport, new SelectList(selectDepFlight,"departureAirport","departureAirport"))
//to match the enumerable above
#Html.DropDownListFor(model => model.chooseDepartureAirport, new SelectList(enumSelectDepFlight,"departureAirport","departureAirport"))
I have a PartialView that is a form used to create or modify a user and implements the ViewModel DynamicActionUserModel. The view that referrences this partialView shows a table of all MembershipUsers and gives the ability to create a new user Membership.CreateUser() or modify a user 'Membership.UpdateUser()`. The form in the partialView does an ajax post to my controller to submit data.
The issue I'm running into is that when a user is created their userName, email, password and role is serialized back to the controller as DynamicActionUserModel.RegisterModel and validated, but when a user is modified, password is not a property that is available (nor do I want to make it available to modify on the client side) so it isn't set in DynamicActionUserModel.RegisterModel and ModelState.IsValid is always false.
Perhaps the design of my model and view needs to change, or is there a way to validate the model but ignore password when a user is being modified? Not sure what the best practice is for this one.
I guess another option would be to create another ViewModel and another partialView specifically for modifying a user but that seems sloppy.
Model
public class DynamicActionUserModel {
public string Action { get; set; }
public RegisterModel RegisterModel { get; set; }
}
public class RegisterModel {
[Required]
[Display(Name = "User Name")]
public string UserName { get; set; }
[Required]
[DataType(DataType.EmailAddress)]
[Display(Name = "Email")]
public string Email { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
public string[] SelectedRoles { get; set; }
public MultiSelectList Roles { get; set; }
}
Controller
[HttpGet]
public ActionResult CreateUser() {
var model = new DynamicActionUserModel {
Action = "CreateUser",
RegisterModel = new RegisterModel {
Roles = new MultiSelectList(System.Web.Security.Roles.GetAllRoles())
}
};
return PartialView("_UserPartial", model);
}
[HttpGet]
public ActionResult ModifyUser() {
var model = new DynamicActionUserModel {
Action = "ModifyUser",
RegisterModel = new RegisterModel {
Roles = new MultiSelectList(System.Web.Security.Roles.GetAllRoles())
}
};
return PartialView("_UserPartial", model);
}
[HttpPost]
public ActionResult ModifyUser(DynamicActionUserModel model) {
bool isEqual = true;
if(!ModelState.IsValid) { // this is always false because password is empty
return PartialView("_UserPartial", model);
}
var user = Membership.GetUser(model.RegisterModel.UserName);
// do stuff
Membership.UpdateUser(user);
return Json(new {success = false});
}
View
#using RobotDog.Models
#model IEnumerable<RobotDog.Models.UserModel>
<!-- table of users -->
<div class="modify-form">
#Html.Action("ModifyUser")
</div>
<div class="create-user-form">
#Html.Action("CreateUser")
</div>
PartialView
#model RobotDog.Models.DynamicActionUserModel
#using(Html.BeginForm(Model.Action,"Admin", FormMethod.Post, new { #class = "ajax" })) {
<!-- Email -->
#Html.TextBoxFor(x => x.RegisterModel.Email, new { #class = inputSize, placeholder = "Email"})
<!-- UserName -->
#if(Model.Action == "ModifyUser") {
#Html.HiddenFor(x => x.RegisterModel.UserName)
<span class="input-xlarge uneditable-input">#Html.DisplayNameFor(x => x.RegisterModel.UserName)</span>
} else {
#Html.TextBoxFor(x => x.RegisterModel.UserName, new { #class = inputSize, placeholder = "User Name" })
}
<!-- Password -->
#if(Model.Action == "Createuser") {
#Html.PasswordFor(x => x.RegisterModel.Password, new { #class = inputSize, placeholder = "Password"})
}
<!-- Roles -->
#Html.ListBoxFor(x => x.RegisterModel.SelectedRoles, Model.RegisterModel.Roles)
<!-- Submit -->
<input type="submit" value="Submit" class="btn"/>
}
Try with ModelState.Remove("password") before calling ModelState.IsValid, but as suggested here if a property is not always required you should not mark it as required.
Have you looked at ModelState.IsValidField ?
Note: even as of MVC4 the documentation is backwards and should say :
Determines whether there are any ModelError objects that are associated with or prefixed with the specified key.
I've made a little extension method helper for this:
public static class ModelStateHelpers
{
public static bool IsValidFor<TModel, TProperty>(this TModel model,
System.Linq.Expressions.Expression<Func<TModel, TProperty>> expression,
ModelStateDictionary modelState)
{
string name = ExpressionHelper.GetExpressionText(expression);
return modelState.IsValidField(name);
}
}
Usage is simple:
bool billingValid = model.IsValidFor(x => x.CheckoutModel.BillingAddress, ModelState);