Reusing Razor view component in list context - asp.net-core

Say, on a page I have a list of items and a Delete button next to each. Upon clicking, I want to show a pop-up with a confirmation message.
The confirmation dialog and the deletion functionality are put into a view component.
I know I can do like this:
foreach (var item in Model.List)
{
<tr class="row">
<td class="col-12">
#item.Name
<button class="btn btn-danger ml-auto" data-toggle="modal"
data-target="#delete-item-#item.Id">×</button>
<vc:delete-item-dialog id="delete-item-#item.Id" item-id="#item.Id"></vc:delete-item-dialog>
</td>
</tr>
}
But then each delete-item-dialog view component is rendered separately, bloating the size of the generated HTML.
Is it possible to place that view component only in one place, after the end of the list, and provide the item-id parameter more dynamically?

Is it possible to place that view component only in one place, after the end of the list, and provide the item-id parameter more dynamically?
Yeah, you can use ajax to load the view component dynamically. Below is a working demo.
View:
#model List<User>
<table>
#foreach (var item in Model)
{
<tr class="row">
<td class="col-12">
#item.Name
<button class="btn btn-danger ml-auto" onclick="deleteuser(#item.Id)">
×
</button>
</td>
</tr>
}
</table>
<div id="container">
</div>
#section scripts{
<script>
function deleteuser(id){
$.ajax({
type: 'get',
url: 'GetMyViewComponent?id=' + id,
success: function (result) {
$("#container").html(result);
$('.modal').modal('show');
}
})
}
</script>
}
Controller:
public IActionResult UserList()
{
var users = new List<User>
{
new User{ Id = 1, Name = "Mike"},
new User{ Id = 2, Name = "John"},
new User{ Id = 3, Name = "David"},
};
return View(users);
}
public IActionResult GetMyViewComponent(int id)
{
return ViewComponent("PopUp", id);
}
PopUpViewComponent class:
public class PopUpViewComponent : ViewComponent
{
public IViewComponentResult Invoke(int id)
{
return View(id);
}
}
PopUpViewComponent Razor View:
#model int
<div class="modal fade" id="delete-#Model" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLongTitle">Modal title</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
Are you sure to delete #Model?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary">Ok</button>
</div>
</div>
</div>
</div>
Result:

Related

Bootstrap Modal Popup background view

I am working on a ASP.NET Core CRUD applciation using modal popup i have a master detail models Stock and Article. i used this code to display the modal popup:
StockController:
public IActionResult Index()
{
List<Category> categories = _dbcontext.Category.ToList();
ViewBag.ListCategories = new SelectList(categories, "CategoryId", "CategoryName");
List<Stock> AllStocks = _dbcontext.Stock.ToList();
return View(AllStocks);
}
[HttpGet]
public IActionResult Create()
{
Stock stock = new Stock();
stock.Articles.Add(new Article() { ArticleId = 1 });
return View("_AddStockPartialView", stock);
}
[HttpPost]
public IActionResult Create(Stock stock)
{
if (stock != null)
{
_dbcontext.Stock.Add(stock);
_dbcontext.SaveChanges();
return RedirectToAction("Index");
}
return View();
}
Index.cshtml:
#model IEnumerable<Stock>
#{
ViewData["Title"] = "Index";
Layout = "~/Views/Shared/_Theme.cshtml";
}
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">Stock</h3>
<div class="card-tools">
<button type="button" class="btn btn-info" data-toggle="modal" data-target="#addStock" onclick="GetDetails()">
<i class="fa fa-plus"></i>
Ajouter
</button>
</div>
</div>
<div class="card-body" id="display">
<table class="table table-bordered table-hover">
.....
</table>
</div>
</div>
</div>
</div>
<script>
function GetDetails() {
$.ajax({
type: "Get",
url: "/Stock/Create",
success: function (res) {
$("#display").html(res);
$("#addStock").modal('show');
}
});
}
</script>
_AddStockPartialView.cshtml:
#model GestionStock.Models.Stock
#{
ViewData["Title"] = "_AddStockPartialView";
}
<div class="modal fade " role="dialog" tabindex="-1" id="addStock" aria-labelledby="addStockLabel" aria-hidden="true" data-backdrop="static" data-keyboard="false">
<div class="modal-dialog modal-dialog-scrollable" role="document">
<div class="modal-content">
<div class="modal-header">
<h5>Stock</h5>
</div>
<div class="modal-body">
<form asp-action="Create" method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="CategoryId" class="control-label"></label>
<select asp-for="CategoryId" class="form-control" asp-items="ViewBag.ListCategories"></select>
<span asp-validation-for="CategoryId" class="text-danger"></span>
</div>
.......
<table class="table table-striped" id="articleTable">
<thead>
<tr>
<th>Numero serie</th>
<th>Marque</th>
<th>etat</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.Articles.Count; i++)
{
<tr>
<td>
#Html.EditorFor(x => x.Articles[i].NumeroSerie, new { htmlAttributes = new { #class = "form-control" } })
</td>
<td>
#Html.EditorFor(x => x.Articles[i].Marque, new { htmlAttributes = new { #class = "form-control" } })
</td>
<td>
#Html.EditorFor(x => x.Articles[i].Etat, new { htmlAttributes = new { #class = "form-control" } })
</td>
</tr>
}
</tbody>
</table>
<input type="hidden" id="hdnLastIndex" value="0" />
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal" #*onclick="javascript:window.location.reload()"*#>Annuler</button>
<button type="submit" class="btn btn-primary">Sauvegarder</button>
</div>
</form>
</div>
</div>
</div>
</div>
Everything works fine and the modal popup is displaying with master detail models. But when i click te button to display the modal popup, the background view (Index.cshtml) is changed like the picture below and the CategoryId SelectList isn't populating:
Although this is the index view which is supposed to display in the background of the modal popup:
So why is Index view chaning when displaying the modal popup?
Below is a work demo, you can refer to it.
In controller, make some change in create action.
[HttpGet]
public IActionResult Create()
{
List<Category> categories = _dbcontext.Category.ToList();
ViewBag.ListCategories = new SelectList(categories, "CategoryId", "CategoryName");
Stock stock = new Stock();
stock.Articles.Add(new Article() { ArticleId = 1 });
return PartialView("_AddStockPartialView", stock);
}
2.In the Index view:
remove table (<table>) before <div class="card-body" id="display">
3.In the _Theme.cshtml, check and make sure you already have below code:
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/GestionStock.styles.css" asp-append-version="true" />
...
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
4.result(I use my model data to show the table) :

ASP.NET Core MVC Master Detail in partial view

I am trying to do CRUD operations on a Master Detail model Department and Employee using modal popup. This is the code I used:
DepartmentController:
[HttpGet]
public IActionResult Create()
{
Department department= new Department();
department.Employees.Add(new Employee() { EmployeeId = 1 });
department.Employees.Add(new Employee() { EmployeeId = 2 });
department.Employees.Add(new Article() { EmployeeId = 3 });
return PartialView("_AddDepartmentPartialView",department);
}
[HttpPost]
public IActionResult Create(Department department)
{
if (department != null)
{
_dbcontext.Department.Add(department);
_dbcontext.SaveChanges();
return RedirectToAction("Index");
}
return View();
}
Index.cshtml:
#model IEnumerable<Department>
#{
ViewData["Title"] = "Index";
Layout = "~/Views/Shared/_Theme.cshtml";
}
<div>
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header">
<h3 class="card-title">Department</h3>
<div class="card-tools">
<button type="button" class="btn btn-info" data-toggle="modal" data-target="#addDepartment">
<i class="fa fa-plus"></i>
Ajouter
</button>
</div>
</div>
<div class="card-body">
.....
</div>
</div>
</div>
</div>
</div>
#await Html.PartialAsync("_AddDepartmentPartialView", new Department())
_AddDepartmentPartialView.cshtml:
#model Department
#{
ViewData["Title"] = "_AddDepartmentPartialView";
}
<div class="modal fade " role="dialog" tabindex="-1" id="addDepartment" aria-labelledby="addDepartmentLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-scrollable" role="document">
<div class="modal-content">
<div class="modal-header">
.....
</div>
<div class="modal-body" >
.......
<form asp-action="Create" method="post">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<table class="table">
<thead>
<tr>
<th>Employee name</th>
<th>Profession</th>
<th>Email</th>
</tr>
</thead>
<tbody>
#for (int i = 0; i < Model.Employees.Count; i++)
{
<tr>
<td>
#Html.EditorFor(x => x.Employees[i].EmployeeName, new { htmlAttributes = new { #class = "form-control" } })
</td>
<td>
#Html.EditorFor(x => x.Employees[i].Profession, new { htmlAttributes = new { #class = "form-control" } })
</td>
<td>
#Html.EditorFor(x => x.Employees[i].Email, new { htmlAttributes = new { #class = "form-control" } })
</td>
</tr>
}
</tbody>
</table>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Annuler</button>
<button type="submit" class="btn btn-primary" >Sauvegarder</button>
</div>
</form>
</div>
</div>
</div>
</div>
But this PartialView displays only the inputs of the Department model and it doesn't displays the rows of the table to insert employees records (it displays only the head of the table).
So, how to pass both Department and Employee to the partial view?
UPDATE
I tried the solution of #Xinran Shen, the modal popup finally appears with both models Department and Employee. But on Ajouter click, the Index view behind the modal popup changed (a table of all departments is supposed to appear but the nothing is displayed), also i have a Dropdownlist in the modal popup it appears empty and it doesn't populated. I think because i am using a custom Layout page but i couldn't find where is the problem exactly. Any help??
In Index.cshtml, You use:
#await Html.PartialAsync("_AddDepartmentPartialView", new Department())
to load the partial View, It will not create partial view by Create get method. And you just pass new Department() into partial View, The Department passed into view is null, So it doesn't display the rows of table.
You can set an onclick event on Ajouter button, When user click the button, It will access Create get method and Initialize Department with the initial value, Then return partial view to the index view.
.............
<button type="button" class="btn btn-info" data-bs-toggle="modal" data-bs-target="#addDepartment" onclick="GetDetails()">
<i class="fa fa-plus"></i>
Ajouter
</button>
..........
<div class="card-body" id="Body">
</div>
...............
<script>
function GetDetails() {
$.ajax({
type: "Get",
url: "/Home/Create",
success: function (res) {
$("#Body").html(res);
$("#addDepartment").modal('show');
}
});
}
</script>
Then when you click button, It will show the full table.

Modal pop up not showing in ASP.NET Core 5

I am working on an ASP.NET Core 5 MVC application, and i'm trying to display a bootstrap modal popup. I used the code below:
Index.cshtml:
<button type="button" class="btn btn-info" data-toggle="modal" data-target="#addEmp">
Ajouter
</button>
<table class="table table-bordered table-hover text-center">
...
</table>
_EmployeePartialView.cshtml:
#model Employee
<div class="modal fade" id="addEmp" aria-labelledby="addEmpLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="addEmpLabel">Employee</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form asp-action="Create">
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="control-label"></label>
<input asp-for="Name" class="form-control" />
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<div class="form-group">
<label asp-for="Profession" class="control-label"></label>
<input asp-for="Profession" class="form-control" />
<span asp-validation-for="Profession" class="text-danger"></span>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary">Save changes</button>
</div>
</div>
</div>
</div>
EmployeeController:
public IActionResult Index()
{
List<Employee> emp = _dbcontext.Employees.ToList();
return View(emp);
}
[HttpGet]
public IActionResult Create()
{
Employee emp = new Employee();
return PartialView("_EmployeePartialView", emp);
}
But when i click on the button the modal popup is not showing without any errors. any suggestions??
1.Please firstly check if the partial view correctly load to your html page. You can F12 and check the Elements panel in browser.
2.Then please check your bootstrap version,because if you use Bootstrap v 5.0, it used data-bs-target instead of data-target.
3.Be sure the partial view locates in Views/Shared/ or Views/Employee/ folder.
Not sure how do you render the partial view, below I share two ways to render the partial view.
Use html helper to display the partial view:
#model List<Employee>
<button type="button" class="btn btn-info" data-toggle="modal" data-target="#addEmp">
Ajouter
</button>
<table class="table table-bordered table-hover text-center">
//...
</table>
#await Html.PartialAsync("_EmployeePartialView", new Employee())
Use ajax to call Create action to display the partial view:
#model List<Employee>
<button type="button" class="btn btn-info" data-toggle="modal" data-target="#addEmp">
Ajouter
</button>
<table class="table table-bordered table-hover text-center">
</table>
<div id="display">
</div>
#section Scripts
{
<script>
$(function(){
$.ajax({
type: "get",
url: "/Employee/Create",
success: function (data) {
$("#display").html(data);
}
})
})
</script>
}
Result:

DataTarget to 'editPage' pass to edit page 'edit.cshtml'

I recently started learning Razor pages so please bear with me....I have an index.cshtml razor page that displays a database data into a table. each row has an edit button that targets a model page 'editpage' . how will i pass or call the target-page 'editpage' from my index.cshtml to my edit.cshtml page that has a modal view.
<---Index.cshtml--->>
#foreach ( var item in Model.Ceding ) {
<tr>
<td>#item.FirstName</td>
<td>#item.MiddleInitial</td>
<td>#item.LastName</td>
<td>#item.Gender</td>
<td>#item.DateofBirth</td>
<td>#item.CedingCompany</td>
<td>#item.PlanEffectiveDate</td>
<td>#item.SumAssured</td>
<td>#item.ReinsuredNetAmount</td>
<td>#item.Status</td>
<td>
<div class="form-button-action">
<button type="button" data-toggle="modal" title="" class="btn btn-link btn-simple-primary btn-lg" data-target="#editPage"><a class="fa fa-edit" asp-route-id="#item.Id"></a>
</button>
<button type="button" data-toggle="tooltip" title="" class="btn btn-link btn-simple-danger" data- original-title="Delete"><i class="fa fa-times"></i>
</button>
</div>
</td>
</tr>
}
<---Edit.cshtml--->>
<div class="modal fade bd-example-modal-lg" id="editPage" tabindex="-1" role="dialog" aria- labelledby="exampleModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h2 class="display-5" id="exampleModalLabel">EDIT DETAILS</h2>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
</div>
</div>
</div>
Controller:
public IEnumerable<Cedants> Ceding { get; set; }
public IndexModel( AppDbContext db )
{
_db = db;
}
public void OnGet(int id)
{
Ceding = _db.dbo_life.ToList(); //retrieves data in database
}

Problem with pass data to controller - update data

I have a problem with passing data from view to controller... When I was clicked button which sending POST - I have got only data for Category.Name. No subcategories, no ids etc. I am thinking I do something wrong with my view but i am not sure...
I need really help. Thanks for everything comments.
That is my View:
#model AplikacjaFryzjer_v2.Models.Category
#{
ViewData["Title"] = "Edytuj kategorię";
Layout = "~/Views/Shared/_Layout.cshtml";
var SubcategoriesData = (IList<AplikacjaFryzjer_v2.Models.Subcategory>)ViewBag.Subcategories;
}
<h1>Edytuj kategorię</h1>
<form method="post">
<div class="form-group row">
<div class="col">
<div asp-validation-summary="All" class="text-danger"></div>
<div class="form-group">
<label asp-for="Name" class="col-sm-10 col-form-label"></label>
<div class="col-sm-10">
<input asp-for="Name" disabled class="form-control" />
</div>
<span asp-validation-for="Name" class="text-danger"></span>
</div>
<button class="btn btn-primary" type="submit" id="update">Aktualizuj kategorię</button>
</div>
<div class="col">
#if (SubcategoriesData != null)
{
<div class="col-sm-10 col-form-label">
<div id="subcatContainer">
#foreach (var subcategory in SubcategoriesData.ToList())
{
<div class="form-group col-sm-6">
<input asp-for="#subcategory.Name" />
<button class="btn btn-danger" type="button" id="remove">Usuń</button>
<span asp-validation-for="#subcategory.Name" class="text-danger"></span>
</div>
}
</div>
<button type="button" class="btn btn-secondary" id="add">Dodaj podkategorię</button>
</div>
}
else
{
<div id="container" class="col-md-6">
<label id="labelName">Nazwa podkategorii</label>
<input id="inputName" />
<button type="button" class="btn btn-secondary" id="addNew">Dodaj</button>
</div>
}
</div>
</div>
</form>
<script>
$(document).ready(function (e) {
// Variables
var i = #SubcategoriesData.Count()-1;
// Add rows to the form
$("#add").click(function (e) {
var html = '<p /><div class="form-group col-sm-6"><input asp-for="Subcategories[' + i + '].Name" /><button class="btn btn-danger" type="button" id="remove">Usuń</button></div>';
i++;
$("#subcatContainer").append(html).before();
});
// Remove rows from the form
$("#subcatContainer").on('click', '#remove', function (e) {
i--;
$(this).parent('div').remove();
});
// Populate values from the first row
});
</script>
My Actions EditCategory in controller:
[HttpGet]
public ViewResult EditCategory(int Id)
{
var category = _categoriesRepository.GetCategory(Id);
if (category == null)
{
ViewBag.ErrorMessage = $"Kategoria o id = {Id} nie została odnaleziona";
return View("NotFound");
}
ViewBag.Subcategories = category.Subcategories;
return View(category);
}
[HttpPost]
public IActionResult EditCategory(Category category)
{
if (!ModelState.IsValid)
{
// store Subcategories data which has been added
ViewBag.Subcategories = category.Subcategories == null ? new List<Subcategory>() { } : category.Subcategories;
return View("EditCategory");
}
_categoriesRepository.UpdateCategory(category);
return RedirectToAction("ManageCategories");
}
And my object (model):
public class Category
{
public int Id { get; set; }
public string Name { get; set; }
public List<Subcategory> Subcategories {get;set;}
}
I have got only data for Category.Name. No subcategories, no ids etc.
Based on your Category model class, the property Subcategories is defined as List<Subcategory>, to make form data values that browser user post could be automatically bound to this property, you can modify the field of Subcategories related to <input asp-for="#Subcategories[i].Name" /> as below.
#model AplikacjaFryzjer_v2.Models.Category
#{
ViewData["Title"] = "Edytuj kategorię";
Layout = "~/Views/Shared/_Layout.cshtml";
var Subcategories = (IList<AplikacjaFryzjer_v2.Models.Subcategory>)ViewBag.Subcategories;
}
<form method="post">
<div class="form-group row">
#*code for other fields*#
<div class="col">
#if (Subcategories != null)
{
<div class="col-sm-10 col-form-label">
<div id="subcatContainer">
#for (int i = 0; i < Subcategories.Count; i++)
{
<div class="form-group col-sm-6">
<input asp-for="#Subcategories[i].Name" />
<button class="btn btn-danger" type="button" id="remove">Usuń</button>
<span asp-validation-for="#Subcategories[i].Name" class="text-danger"></span>
</div>
}
</div>
<button type="button" class="btn btn-secondary" id="add">Dodaj podkategorię</button>
</div>
}
else
{
<div id="container" class="col-md-6">
<label id="labelName">Nazwa podkategorii</label>
<input id="inputName" />
<button type="button" class="btn btn-secondary" id="addNew">Dodaj</button>
</div>
}
</div>
</div>
</form>
Update:
why new inputs added by jquery not sending to my controller? I have got "" in my code jquery
As I mentioned in comment, please not use tag helper syntax on js client. You can try to manually set name and value attribute for new generated input field(s)
like below.
$(document).ready(function (e) {
// Variables
var i = #Subcategories.Count();
// Add rows to the form
$("#add").click(function (e) {
//var html = '<p /><div class="form-group col-sm-6"><input asp-for="Subcategories[' + i + '].Name" /><button class="btn btn-danger" type="button" id="remove">Usuń</button></div>';
var html = '<p /><div class="form-group col-sm-6"><input name="Subcategories[' + i + '].Name" value="" /><button class="btn btn-danger" type="button" id="remove">Usuń</button></div>';
i++;
$("#subcatContainer").append(html).before();
});
// Remove rows from the form
$("#subcatContainer").on('click', '#remove', function (e) {
i--;
$(this).parent('div').remove();
});
// Populate values from the first row
});
Test Result