将视图组件与表单一起使用
Use of View Components with forms
我正在尝试学习 ASP.NET 核心 MVC 中视图组件的正确用法,所以我有以下示例:我的想法是渲染一个包含电影细节的视图,在其中 ReviewViewComponent
持有 10 星电影评级小部件。
视图组件的视图内部是一个带有单选按钮的表单。表单操作名称被传递给视图组件(Create
或 Edit
,取决于用户是否已经给出评级)。根据收到的动作名称,视图组件内的表单将调用 ReviewsController
.
内的 Create
或 Edit
方法
这一切都有效,直到我到达 ReviewsController
中的 return 调用。我希望能够 return 那里的视图组件,并简单地在 div 中渲染 return 结果,使用 AJAX 在详细信息中使用 id="now-showing-details-rating-div"
。这适用于部分视图(代码在 ReviewsController
的 Edit
方法中被注释掉),但它似乎不适用于视图组件(它只是将视图组件的视图呈现为一个全新的视图,即使我在表单上调用 AJAX 的方式与在局部视图中调用时的方式相同)。
我实际上是在滥用视图组件的概念吗?在表单提交后呈现视图的一部分的意义上,仅使用部分视图实际上更好吗?
Ajax 片段
$.ajax({
type: "POST",
url: requestUrl,
data: form.serialize(),
success: function (data) {
$("#" + divZaRezultat).html(data);
}
});
视图模型:
public class MovieDetailsVM
{
public string Title { get; set; }
public int Id { get; set; }
public int Year { get; set; }
public string Actors { get; set; }
public string Country { get; set; }
public string Directors { get; set; }
public int Duration { get; set; }
public string VideoLink { get; set; }
public string AverageRating { get; set; }
public string NumberOfReviews { get; set; }
public ReviewIndexVM CurrentUserReview { get; set; }
}
public class ReviewIndexVM
{
public int ReviewId { get; set; }
public int Rating { get; set; }
public MasterModel User { get; set; }
public MasterModel Movie { get; set; }
}
ViewComponent
public class ReviewViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(string methodName, ReviewIndexVM review)
{
ViewBag.Method = methodName;
return View(review);
}
}
ViewComponent 默认视图
@model Cinema.DTO.ViewModels.Reviews.ReviewIndexVM
@{
ViewData["Title"] = "Default";
}
<form asp-controller="Reviews" asp-action="@ViewBag.Method">
<input asp-for="ReviewId" hidden />
<input asp-for="Movie.Id" hidden />
<input asp-for="User.Id" hidden />
<div class="rating form-group">
@for (int i = 10; i > 0; i--)
{
<input asp-for="Rating" type="radio" value="@i" id="@($"rating-star-{i}")" onclick="this.form.submit();" class="form-control rating-star"><label class="rating-star-label" for="@($"rating-star-{i}")"></label>
}
</div>
</form>
浏览量:
@model Cinema.DTO.ViewModels.Movies.MovieDetailsVM
@using Microsoft.AspNetCore.Identity
@using Cinema.Domain.Entities.Identity
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager
@{
ViewData["Title"] = "Details";
Layout = "~/Views/Shared/_Layout.cshtml";
bool first = true;
DateTime currentDate = DateTime.Now;
}
<section>
<div class="container">
<div class="content-wrap">
<div class="row">
<h1 class="h2">@Html.DisplayFor(model => model.Title)</h1>
</div>
<div class="row">
<div class="col-md-4">
<img id="movie-poster" class="pull-left" src="~/img/movie-poster.png" />
</div>
<div class="col-md-8">
<ul class="list-unstyled movie-info">
<li>
<span>@Html.DisplayNameFor(model => model.Title)</span>
@Html.DisplayFor(model => model.Title)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Year)</span>
@Html.DisplayFor(model => model.Year)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Actors)</span>
@Html.DisplayFor(model => model.Actors)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Country)</span>
@Html.DisplayFor(model => model.Country)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Directors)</span>
@Html.DisplayFor(model => model.Directors)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Duration)</span>
@Html.DisplayFor(model => model.Duration)
</li>
@*<li>
<span>@Html.DisplayNameFor(model => model.GenreMovies)</span>
@Html.DisplayFor(model => model.GenreMovies)
</li>*@
<li>
<span>@Html.DisplayNameFor(model => model.VideoLink)</span>
@Html.DisplayFor(model => model.VideoLink)
</li>
</ul>
Average rating <span class="badge">@Model.AverageRating</span>
<hr />
<div asp-authorize asp-roles="@Roles.User">
Your rating:
<div id="now-showing-details-rating-div">
@if (@Model.CurrentUserReview.ReviewId == 0)
{
@await Component.InvokeAsync("Review", new { methodName = "Create", review = @Model.CurrentUserReview })
}
else
{
@await Component.InvokeAsync("Review", new { methodName = "Edit", review = @Model.CurrentUserReview })
}
</div>
</div>
</div>
</div>
</div>
</section>
@section Scripts {
$(document).ready(function () {
$('.rating-star-label').mouseover(function () {
$('.rating-star').prop('checked', false);
});
});
</script>
}
ReviewsController
[HttpGet]
[Authorize(Roles = Roles.User)]
public async Task<IActionResult> Edit(int reviewId)
{
Review review = await _unit.Reviews.GetAsync(reviewId);
var authorizationResult = await _authorizationService.AuthorizeAsync(User, review, OperationRequirements.Update);
if (authorizationResult.Succeeded)
{
ReviewUpdateVM model = review.ToUpdateVM();
return PartialView(model);
}
else if (User.Identity.IsAuthenticated)
{
return new ForbidResult();
}
else
{
return new ChallengeResult();
}
}
[Authorize(Roles = Roles.User)]
public async Task<IActionResult> Edit(ReviewIndexVM model)
{
Review review = model.Create();
var authorizationResult = await _authorizationService.AuthorizeAsync(User, review, OperationRequirements.Update);
if (authorizationResult.Succeeded)
{
await _unit.Reviews.UpdateAsync(review, model.ReviewId);
await _unit.SaveAsync();
return ViewComponent("Review");
//return Redirect("/Reviews/Details?reviewId=" + review.Id);
}
else if (User.Identity.IsAuthenticated)
{
return new ForbidResult();
}
else
{
return new ChallengeResult();
}
}
}
else if (User.Identity.IsAuthenticated)
{
return new ForbidResult();
}
else
{
return new ChallengeResult();
}
}
这是一个工作演示:
Details.cshtml:
@model MovieDetailsVM
@{
ViewData["Title"] = "Details";
Layout = "~/Views/Shared/_Layout.cshtml";
bool first = true;
DateTime currentDate = DateTime.Now;
}
<section>
<div class="container">
<div class="content-wrap">
<div class="row">
<h1 class="h2">@Html.DisplayFor(model => model.Title)</h1>
</div>
<div class="row">
<div class="col-md-8">
<ul class="list-unstyled movie-info">
<li>
<span>@Html.DisplayNameFor(model => model.Title)</span>
@Html.DisplayFor(model => model.Title)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Year)</span>
@Html.DisplayFor(model => model.Year)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Actors)</span>
@Html.DisplayFor(model => model.Actors)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Country)</span>
@Html.DisplayFor(model => model.Country)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Directors)</span>
@Html.DisplayFor(model => model.Directors)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Duration)</span>
@Html.DisplayFor(model => model.Duration)
</li>
@*<li>
<span>@Html.DisplayNameFor(model => model.GenreMovies)</span>
@Html.DisplayFor(model => model.GenreMovies)
</li>*@
<li>
<span>@Html.DisplayNameFor(model => model.VideoLink)</span>
@Html.DisplayFor(model => model.VideoLink)
</li>
</ul>
Average rating <span class="badge">@Model.AverageRating</span>
<hr />
<div>
Your rating:
<div id="now-showing-details-rating-div">
@if (@Model.CurrentUserReview == null)
{
@await Component.InvokeAsync("Review", new { methodName = "Create", review = @Model.CurrentUserReview })
}
else
{
@await Component.InvokeAsync("Review", new { methodName = "Edit", review = @Model.CurrentUserReview })
}
</div>
</div>
</div>
</div>
</div>
</div>
</section>
Ajax 在 Details.cshtml:
@section Scripts {
<script>
$(document).ready(function () {
$('.rating-star-label').mouseover(function () {
$('.rating-star').prop('checked', false);
});
});
function Update() {
$.ajax({
type: "POST",
url: "/Reviews/Edit/@Model.CurrentUserReview.ReviewId",
data: $("form").serialize(),
success: function (data) {
$("#now-showing-details-rating-div").html(data);
}
});
}
</script>
}
Components/Review/Default.cshtml:
@model ReviewIndexVM
@{
ViewData["Title"] = "Default";
}
<form asp-controller="Reviews" asp-action="@ViewBag.Method">
<input asp-for="ReviewId" hidden />
<input asp-for="Movie.Id" hidden />
<input asp-for="User.Id" hidden />
<div class="rating form-group">
@for (int i = 10; i > 0; i--)
{
<input asp-for="Rating" type="radio" value="@i" id="@($"rating-star-{i}")" onclick="Update();" class="form-control rating-star"><label class="rating-star-label" for="@($"rating-star-{i}")"></label>
}
</div>
</form>
控制器:
public class ReviewsController : Controller
{
private readonly Component2_2Context _context;
public ReviewsController(Component2_2Context context)
{
_context = context;
}
// GET: Reviews/Details/5
public async Task<IActionResult> Details(int? id)
{
var reviewIndexVM = await _context.MovieDetailsVM
.Include(m => m.CurrentUserReview)
.FirstOrDefaultAsync(m => m.Id == id);
return View(reviewIndexVM);
}
// GET: Reviews/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var reviewIndexVM = await _context.ReviewIndexVM.FindAsync(id);
if (reviewIndexVM == null)
{
return NotFound();
}
return View(reviewIndexVM);
}
// POST: Reviews/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ReviewId,Rating")] ReviewIndexVM reviewIndexVM)
{
if (id != reviewIndexVM.ReviewId)
{
return NotFound();
}
if (ModelState.IsValid)
{
_context.Update(reviewIndexVM);
await _context.SaveChangesAsync();
return ViewComponent("Review");
}
return new ChallengeResult();
}
视图组件:
public class ReviewViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(string methodName, ReviewIndexVM review)
{
ViewBag.Method = methodName;
return View(review);
}
}
结果:
我正在尝试学习 ASP.NET 核心 MVC 中视图组件的正确用法,所以我有以下示例:我的想法是渲染一个包含电影细节的视图,在其中 ReviewViewComponent
持有 10 星电影评级小部件。
视图组件的视图内部是一个带有单选按钮的表单。表单操作名称被传递给视图组件(Create
或 Edit
,取决于用户是否已经给出评级)。根据收到的动作名称,视图组件内的表单将调用 ReviewsController
.
Create
或 Edit
方法
这一切都有效,直到我到达 ReviewsController
中的 return 调用。我希望能够 return 那里的视图组件,并简单地在 div 中渲染 return 结果,使用 AJAX 在详细信息中使用 id="now-showing-details-rating-div"
。这适用于部分视图(代码在 ReviewsController
的 Edit
方法中被注释掉),但它似乎不适用于视图组件(它只是将视图组件的视图呈现为一个全新的视图,即使我在表单上调用 AJAX 的方式与在局部视图中调用时的方式相同)。
我实际上是在滥用视图组件的概念吗?在表单提交后呈现视图的一部分的意义上,仅使用部分视图实际上更好吗?
Ajax 片段
$.ajax({
type: "POST",
url: requestUrl,
data: form.serialize(),
success: function (data) {
$("#" + divZaRezultat).html(data);
}
});
视图模型:
public class MovieDetailsVM
{
public string Title { get; set; }
public int Id { get; set; }
public int Year { get; set; }
public string Actors { get; set; }
public string Country { get; set; }
public string Directors { get; set; }
public int Duration { get; set; }
public string VideoLink { get; set; }
public string AverageRating { get; set; }
public string NumberOfReviews { get; set; }
public ReviewIndexVM CurrentUserReview { get; set; }
}
public class ReviewIndexVM
{
public int ReviewId { get; set; }
public int Rating { get; set; }
public MasterModel User { get; set; }
public MasterModel Movie { get; set; }
}
ViewComponent
public class ReviewViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(string methodName, ReviewIndexVM review)
{
ViewBag.Method = methodName;
return View(review);
}
}
ViewComponent 默认视图
@model Cinema.DTO.ViewModels.Reviews.ReviewIndexVM
@{
ViewData["Title"] = "Default";
}
<form asp-controller="Reviews" asp-action="@ViewBag.Method">
<input asp-for="ReviewId" hidden />
<input asp-for="Movie.Id" hidden />
<input asp-for="User.Id" hidden />
<div class="rating form-group">
@for (int i = 10; i > 0; i--)
{
<input asp-for="Rating" type="radio" value="@i" id="@($"rating-star-{i}")" onclick="this.form.submit();" class="form-control rating-star"><label class="rating-star-label" for="@($"rating-star-{i}")"></label>
}
</div>
</form>
浏览量:
@model Cinema.DTO.ViewModels.Movies.MovieDetailsVM
@using Microsoft.AspNetCore.Identity
@using Cinema.Domain.Entities.Identity
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager
@{
ViewData["Title"] = "Details";
Layout = "~/Views/Shared/_Layout.cshtml";
bool first = true;
DateTime currentDate = DateTime.Now;
}
<section>
<div class="container">
<div class="content-wrap">
<div class="row">
<h1 class="h2">@Html.DisplayFor(model => model.Title)</h1>
</div>
<div class="row">
<div class="col-md-4">
<img id="movie-poster" class="pull-left" src="~/img/movie-poster.png" />
</div>
<div class="col-md-8">
<ul class="list-unstyled movie-info">
<li>
<span>@Html.DisplayNameFor(model => model.Title)</span>
@Html.DisplayFor(model => model.Title)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Year)</span>
@Html.DisplayFor(model => model.Year)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Actors)</span>
@Html.DisplayFor(model => model.Actors)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Country)</span>
@Html.DisplayFor(model => model.Country)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Directors)</span>
@Html.DisplayFor(model => model.Directors)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Duration)</span>
@Html.DisplayFor(model => model.Duration)
</li>
@*<li>
<span>@Html.DisplayNameFor(model => model.GenreMovies)</span>
@Html.DisplayFor(model => model.GenreMovies)
</li>*@
<li>
<span>@Html.DisplayNameFor(model => model.VideoLink)</span>
@Html.DisplayFor(model => model.VideoLink)
</li>
</ul>
Average rating <span class="badge">@Model.AverageRating</span>
<hr />
<div asp-authorize asp-roles="@Roles.User">
Your rating:
<div id="now-showing-details-rating-div">
@if (@Model.CurrentUserReview.ReviewId == 0)
{
@await Component.InvokeAsync("Review", new { methodName = "Create", review = @Model.CurrentUserReview })
}
else
{
@await Component.InvokeAsync("Review", new { methodName = "Edit", review = @Model.CurrentUserReview })
}
</div>
</div>
</div>
</div>
</div>
</section>
@section Scripts {
$(document).ready(function () {
$('.rating-star-label').mouseover(function () {
$('.rating-star').prop('checked', false);
});
});
</script>
}
ReviewsController
[HttpGet]
[Authorize(Roles = Roles.User)]
public async Task<IActionResult> Edit(int reviewId)
{
Review review = await _unit.Reviews.GetAsync(reviewId);
var authorizationResult = await _authorizationService.AuthorizeAsync(User, review, OperationRequirements.Update);
if (authorizationResult.Succeeded)
{
ReviewUpdateVM model = review.ToUpdateVM();
return PartialView(model);
}
else if (User.Identity.IsAuthenticated)
{
return new ForbidResult();
}
else
{
return new ChallengeResult();
}
}
[Authorize(Roles = Roles.User)]
public async Task<IActionResult> Edit(ReviewIndexVM model)
{
Review review = model.Create();
var authorizationResult = await _authorizationService.AuthorizeAsync(User, review, OperationRequirements.Update);
if (authorizationResult.Succeeded)
{
await _unit.Reviews.UpdateAsync(review, model.ReviewId);
await _unit.SaveAsync();
return ViewComponent("Review");
//return Redirect("/Reviews/Details?reviewId=" + review.Id);
}
else if (User.Identity.IsAuthenticated)
{
return new ForbidResult();
}
else
{
return new ChallengeResult();
}
}
}
else if (User.Identity.IsAuthenticated)
{
return new ForbidResult();
}
else
{
return new ChallengeResult();
}
}
这是一个工作演示:
Details.cshtml:
@model MovieDetailsVM
@{
ViewData["Title"] = "Details";
Layout = "~/Views/Shared/_Layout.cshtml";
bool first = true;
DateTime currentDate = DateTime.Now;
}
<section>
<div class="container">
<div class="content-wrap">
<div class="row">
<h1 class="h2">@Html.DisplayFor(model => model.Title)</h1>
</div>
<div class="row">
<div class="col-md-8">
<ul class="list-unstyled movie-info">
<li>
<span>@Html.DisplayNameFor(model => model.Title)</span>
@Html.DisplayFor(model => model.Title)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Year)</span>
@Html.DisplayFor(model => model.Year)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Actors)</span>
@Html.DisplayFor(model => model.Actors)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Country)</span>
@Html.DisplayFor(model => model.Country)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Directors)</span>
@Html.DisplayFor(model => model.Directors)
</li>
<li>
<span>@Html.DisplayNameFor(model => model.Duration)</span>
@Html.DisplayFor(model => model.Duration)
</li>
@*<li>
<span>@Html.DisplayNameFor(model => model.GenreMovies)</span>
@Html.DisplayFor(model => model.GenreMovies)
</li>*@
<li>
<span>@Html.DisplayNameFor(model => model.VideoLink)</span>
@Html.DisplayFor(model => model.VideoLink)
</li>
</ul>
Average rating <span class="badge">@Model.AverageRating</span>
<hr />
<div>
Your rating:
<div id="now-showing-details-rating-div">
@if (@Model.CurrentUserReview == null)
{
@await Component.InvokeAsync("Review", new { methodName = "Create", review = @Model.CurrentUserReview })
}
else
{
@await Component.InvokeAsync("Review", new { methodName = "Edit", review = @Model.CurrentUserReview })
}
</div>
</div>
</div>
</div>
</div>
</div>
</section>
Ajax 在 Details.cshtml:
@section Scripts {
<script>
$(document).ready(function () {
$('.rating-star-label').mouseover(function () {
$('.rating-star').prop('checked', false);
});
});
function Update() {
$.ajax({
type: "POST",
url: "/Reviews/Edit/@Model.CurrentUserReview.ReviewId",
data: $("form").serialize(),
success: function (data) {
$("#now-showing-details-rating-div").html(data);
}
});
}
</script>
}
Components/Review/Default.cshtml:
@model ReviewIndexVM
@{
ViewData["Title"] = "Default";
}
<form asp-controller="Reviews" asp-action="@ViewBag.Method">
<input asp-for="ReviewId" hidden />
<input asp-for="Movie.Id" hidden />
<input asp-for="User.Id" hidden />
<div class="rating form-group">
@for (int i = 10; i > 0; i--)
{
<input asp-for="Rating" type="radio" value="@i" id="@($"rating-star-{i}")" onclick="Update();" class="form-control rating-star"><label class="rating-star-label" for="@($"rating-star-{i}")"></label>
}
</div>
</form>
控制器:
public class ReviewsController : Controller
{
private readonly Component2_2Context _context;
public ReviewsController(Component2_2Context context)
{
_context = context;
}
// GET: Reviews/Details/5
public async Task<IActionResult> Details(int? id)
{
var reviewIndexVM = await _context.MovieDetailsVM
.Include(m => m.CurrentUserReview)
.FirstOrDefaultAsync(m => m.Id == id);
return View(reviewIndexVM);
}
// GET: Reviews/Edit/5
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var reviewIndexVM = await _context.ReviewIndexVM.FindAsync(id);
if (reviewIndexVM == null)
{
return NotFound();
}
return View(reviewIndexVM);
}
// POST: Reviews/Edit/5
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(int id, [Bind("ReviewId,Rating")] ReviewIndexVM reviewIndexVM)
{
if (id != reviewIndexVM.ReviewId)
{
return NotFound();
}
if (ModelState.IsValid)
{
_context.Update(reviewIndexVM);
await _context.SaveChangesAsync();
return ViewComponent("Review");
}
return new ChallengeResult();
}
视图组件:
public class ReviewViewComponent : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync(string methodName, ReviewIndexVM review)
{
ViewBag.Method = methodName;
return View(review);
}
}
结果: