Select Tag Helper to Display 2 Fields in the Drop Down List

asp.net-core asp.net-core-mvc c# entity-framework-core

Question

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.

1
0
4/11/2019 11:55:40 AM

Accepted Answer

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();
1
4/11/2019 5:29:44 PM

Popular Answer

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



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow