Reputation: 3
Using the standard scaffolding only displays the indexes in the drop down of related tables. How to display multiple fields so the drop down list is meaningful?
I have attempted to use a solution outlined by Shyju in the section 'Getting data from your database table using entity framework'. (Select Tag Helper in ASP.NET Core MVC).
I have 2 classes in the model (Books with related Author):
namespace SimpleAppwithTwoTables.Data
{
public class Book
{
public int BookID { get; set; }
[StringLength(255)]
[Display(Name ="Book Title")]
public string Title { get; set; }
public int AuthorID { get; set; }
public Author Author { get; set; }
}
}
and
namespace SimpleAppwithTwoTables.Data
{
public class Author
{
public int AuthorID { get; set; }
[Display(Name = "First Name")]
[StringLength(50)]
public string FirstName { get; set; }
[Display(Name = "Last Name")]
[StringLength(50)]
public string LastName { get; set; }
public ICollection<Book> Books { get; set; } = new List<Book>();
}
}
The BooksController has a Create() method similar to what was described in the post from Shyju.
public IActionResult Create()
Author vm = new Author();
vm.Books = _context.Book.Select(a => new SelectListItem()
{ Value = a.AuthorID.ToString(), Text = a.Author.LastName }).ToList();
return View(vm);
}
This line in the controller
vm.Books = _context.Book.Select(a => new SelectListItem()
{ Value = a.AuthorID.ToString(), Text = a.Author.LastName }).ToList();
Generates this error:
"Cannot implicitly convert type 'System.Collections.Generic.List<Microsoft.AspNetCore.Mvc.Rendering.SelectListItem>' to 'System.Collections.Generic.ICollection<SimpleAppwithTwoTables.Data.Book>'. An explicit conversion exists (are you missing a cast?).
Note that I am new to C# and understand that this is a type conversion issue and that the line with the error is expecting that the Author model contains an ICollection<>.
I am looking for the code needed in the vm.books line that does not do a type conversion and works with the ICollection.
Upvotes: 0
Views: 1934
Reputation: 3
I followed the advice Chris Pratt provided above. It took a while to figure out but almost having working. See details below.
See the model for Book and Author above.
Created a new ViewModel:
namespace SimpleDropDownList.Models
{
[NotMapped]
public class AuthorViewModel
{
//Property to hold the list of authors in the GET
public IEnumerable<SelectListItem> AuthorOptions { get; set; }
//Property to bind the selected author used in the POST
public List<int> SelectedAuthorIds { get; set; }
}
}
Changed the Create() method on the BooksController to:
public IActionResult Create()
{
//ViewData["AuthorID"] = new SelectList(_context.Set<Author>(), "AuthorID", "AuthorID");
AuthorViewModel vm = new AuthorViewModel();
vm.AuthorOptions = _context.Book.Select(x => new SelectListItem()
{ Value = x.AuthorID.ToString(), Text = x.Author.LastName }).ToList();
return View(vm);
}
Change the Create view to:
@model SimpleDropDownList.Models.Book
@{
ViewData["Title"] = "Create";
}
<h1>Create</h1>
<h4>Book</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
<span asp-validation-for="Title" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="AuthorID" class="control-label"></label>
@*<select asp-for="AuthorID" class ="form-control" asp-items="ViewBag.AuthorID"></select>*@
<select asp-for="@Model.AuthorViewModel.SelectedAuthorIds" asp-items="@Model.AuthorViewModel.AuthorOptions"></select>
</div>
<div class="form-group">
<input type="submit" value="Create" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Model: AuthorViewModel
When I run this and click the Create button the following error is generated:
An unhandled exception occurred while processing the request. InvalidOperationException: The model item passed into the ViewDataDictionary is of type 'SimpleDropDownList.Models.AuthorViewModel', but this ViewDataDictionary instance requires a model item of type 'SimpleDropDownList.Models.Book'.
I'm flagging this question as completed and have created a new question for the issue describe just above. See here: Error when Razor page opens and have a related drop down list
Upvotes: 0
Reputation: 239380
You're trying to stuff a list of SelectListItem
into an property defined as a list of Book
. SelectListItem
quite obviously is not the same things as Book
, so it fails.
The simple solution is that you need a property on your view model specifically for holding your select list options:
public IEnumerable<SelectListItem> AuthorOptions { get; set; }
You will also need something to bind the select items to, which will be primitive types, not actual Author
instances:
public List<int> SelectedAuthorIds { get; set; }
In your view, then:
<select asp-for="SelectedAuthorIds" asp-items="AuthorOptions">
On post, you will then need to use that collection of ids to query the actual author objects, if you need to associate them with a book or something else:
var selectAuthors = await _context.Authors.Where(x => vm.SelectedAuthorIds.Contains(x.Id)).ToListAsync();
Upvotes: 1