'MVC problem with view of Edit that contain [partial view from relation many to many]
I try make a library Sistem where in category to book has a relation many to many i need put in my book edit view a partial view frow the entity, could someone help-me?
my edit view:
@model MVC_Library.Models.Book
@{
ViewData["Title"] = "Edit";
BookCategory teste = ViewBag.testeview;
}
<h1>Edit</h1>
<h4>Book</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="BookId" />
<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="Author" class="control-label"></label>
<input asp-for="Author" class="form-control" />
<span asp-validation-for="Author" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PublishDate" class="control-label"></label>
<input asp-for="PublishDate" class="form-control" />
<span asp-validation-for="PublishDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="BasePrice" class="control-label"></label>
<input asp-for="BasePrice" class="form-control" />
<span asp-validation-for="BasePrice" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Quantity" class="control-label"></label>
<input asp-for="Quantity" class="form-control" />
<span asp-validation-for="Quantity" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PublishingCompanyId" class="control-label"></label>
<select asp-for="PublishingCompanyId" class="form-control" asp-items="ViewBag.PublishingCompanyId"></select>
<span asp-validation-for="PublishingCompanyId" class="text-danger"></span>
</div>
<div>
@Html.Partial("_EditBookCategory", teste)
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
my partial view that work (but in this way just pre-select one category item)
@model MVC_Library.Models.BookCategory
<div class="row">
<div class="col-md-12">
<label asp-for="CategoryId" class="control-label"></label>
<select multiple asp-for="CategoryId" class="form-control" name = "BookCategory[]" asp-items="ViewBag.SelectedCategory"></select>
</div>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
and my controller edit part
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var book = await _context.Books.FindAsync(id);
if (book == null)
{
return NotFound();
}
List<BookCategory> bookCategory = _context.BookCategories.Where(e => e.BookId == id).ToList();
ViewData["PublishingCompanyId"] = new SelectList(_context.PublishingCompanies, "PublishingCompanyId", "PublishingCompanyName", book.PublishingCompanyId);
//this is what i try for try return mutiple category select (don't work with this viewbag)
/* ViewBag.SelectedCategory = new MultiSelectList(_context.Categories.ToList(), "CategoryId", "CategoryName", bookCategory); */
//this viewbag return only one category selected but work
ViewBag.testeview = bookCategory.First();
return View(book);
}
Just once selected (the first), but a want to return all. Need help if this could possible.
Solution 1:[1]
If you want to display all the related categories selected:
- Be sure
asp-for="CategoryId"hereCategoryIdshould be type ofint[]orstring[]. Or just removeasp-fortag helper, because you have addedname="BookCategory[]"here, it will overrideasp-forgenerated name attribute. When you form submit, your action always need contain a parameter namedBookCategory[], no matter you addasp-foror not.
Besides BookCategory[] is not a correct name with no index in array, it should be BookCategory or any other name and the parameter name in action should be type of int[] or string[]. Guessing you may want to match the selected value to List model BookCategory's CategoryId. It is not acceptable for multiple select.
Be sure the last parameter in
MultiSelectListshould beint[]orstring[]. From your code, thedataValueFieldinMultiSelectListmatchesCategoryIdproperty, so your last parameter should be something likebookCategory.Select(a=>a.CategoryId).ToList().ViewBag.SelectedCategory = new MultiSelectList(_context.Categories.ToList(), "CategoryId", "CategoryName", bookCategory.Select(a=>a.CategoryId).ToList());No need use
ViewBag.testeview, just change like below:@Html.Partial("_EditBookCategory", new BookCategory())
Whole working demo should be like:
Model:
public class Book
{
public int BookId { get; set; }
public string Title { get; set; }
public string Author { get; set; }
public DateTime PublishDate { get; set; }
public int BasePrice { get; set; }
public int Quantity { get; set; }
public int PublishingCompanyId { get; set; }
public List<BookCategory> BookCategory { get; set; }
}
public class Category
{
public int CategoryId { get; set; }
public string CategoryName { get; set; }
public List<BookCategory> BookCategory { get; set; }
}
public class BookCategory
{
public int CategoryId { get; set; }
public int BookId { get; set; }
public Category Category { get; set; }
public Book Book { get; set; }
}
View:
@model Book
@{
ViewData["Title"] = "Edit";
}
<h1>Edit</h1>
<h4>Book</h4>
<hr />
<div class="row">
<div class="col-md-4">
<form asp-action="Edit">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<input type="hidden" asp-for="BookId" />
<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="Author" class="control-label"></label>
<input asp-for="Author" class="form-control" />
<span asp-validation-for="Author" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PublishDate" class="control-label"></label>
<input asp-for="PublishDate" class="form-control" />
<span asp-validation-for="PublishDate" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="BasePrice" class="control-label"></label>
<input asp-for="BasePrice" class="form-control" />
<span asp-validation-for="BasePrice" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Quantity" class="control-label"></label>
<input asp-for="Quantity" class="form-control" />
<span asp-validation-for="Quantity" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="PublishingCompanyId" class="control-label"></label>
<select asp-for="PublishingCompanyId" class="form-control" asp-items="ViewBag.PublishingCompanyId"></select>
<span asp-validation-for="PublishingCompanyId" class="text-danger"></span>
</div>
<div>
//change here....
@Html.Partial("_EditBookCategory", new BookCategory())
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-primary" />
</div>
</form>
</div>
</div>
<div>
<a asp-action="Index">Back to List</a>
</div>
@section Scripts {
@{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}
Partial View:
@model BookCategory
<div class="row">
<div class="col-md-12">
<label asp-for="CategoryId" class="control-label"></label>
<select multiple class="form-control" name="CategoryId" asp-items="ViewBag.SelectedCategory"></select>
</div>
</div>
Controller:
Note: I just hard-coded the data due to easy testing, it is the same with your get data from database. You just need change like what my comment said, other code is no need change like what I did.
public IActionResult Edit(int? id)
{
var book = new Book()
{
BookId = 1,
PublishDate = DateTime.Now,
Author = "a1",
BasePrice = 34,
PublishingCompanyId = 1,
Quantity = 23,
Title = "aa"
};
List<BookCategory> bookCategory = new List<BookCategory>()
{
new BookCategory(){BookId=1,CategoryId=1},
new BookCategory(){BookId=2,CategoryId=1},
new BookCategory(){BookId=2,CategoryId=3}
};
ViewData["PublishingCompanyId"] = new List<SelectListItem>() {
new SelectListItem() { Value = "1", Text = "Elementos1" },
new SelectListItem() { Value = "2", Text = "Elementos2" },
new SelectListItem() { Value = "3", Text = "Elementos3" }
};
var c = new List<Category>()
{
new Category(){CategoryId=1,Name="aa"},
new Category(){CategoryId=2,Name="bb"},
new Category(){CategoryId=3,Name="cc"}
};
//change here....
ViewBag.SelectedCategory = new MultiSelectList(c, "CategoryId", "Name", bookCategory.Select(a=>a.CategoryId).ToList());
//ViewBag.testeview = bookCategory.First();
return View(book);
}
[HttpPost]
public IActionResult Edit(int? id,Book book, int[] CategoryId)
{
//do your stuff......
return View();
}
Result:
For how to choose multiple option, you just need press Ctrl key meanwhile click the option.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Rena |

