Использование ViewComponent с формами - PullRequest
0 голосов
/ 25 марта 2020

Я пытаюсь научиться правильно использовать ViewComponent в ASP. NET Core Mvc, поэтому у меня есть следующий пример: Идея состоит в том, чтобы визуализировать представление с деталями Mov ie и внутри это, ReviewViewComponent, содержащий 10-звездочный виджет оценки mov ie.

Внутри ViewComponent View фактически есть форма с переключателями. Имя действия формы передается ViewComponent (либо «Создать», либо «Редактировать», в зависимости от того, дал ли пользователь оценку). В зависимости от полученного имени действия, форма внутри ViewComponent будет вызывать метод Create или Edit внутри ReviewsController.

Это все работает, пока я не достигну обратного вызова внутри ReviewsController. Я хотел бы иметь возможность вернуть ViewComponent там и просто отобразить возвращаемый результат внутри div с id = "now-показывая-details-rating-div" в Details, используя ajax. Это работает с PartialViews (код закомментирован в методе редактирования ReviewsController), но кажется, что он не работает с ViewComponents (он просто отображает ViewComponent View как совершенно новый View, хотя я вызываю ajax в той же форме как если бы это было в PartialView).

Действительно ли я здесь неправильно использую концепцию ViewComponent? В смысле визуализации части представления после отправки формы, лучше ли на самом деле просто использовать PartialViews?

Ajax фрагмент

        $.ajax({
            type: "POST",
            url: requestUrl,
            data: form.serialize(),
            success: function (data) {
                $("#" + divZaRezultat).html(data);
            }
        });

ViewModels:

    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>
}

ОтзывыКонтроллер

        [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();
            }
        }

1 Ответ

1 голос
/ 25 марта 2020

Вот рабочая демонстрация:

Details.cs html:

@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.cs html :

@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>
}

Компоненты / Обзор / Default.cs html:

@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();
    }

ViewComponent:

public class ReviewViewComponent : ViewComponent
{
    public async Task<IViewComponentResult> InvokeAsync(string methodName, ReviewIndexVM review)
    {
        ViewBag.Method = methodName;
        return View(review);
    }
}

Результат: enter image description here

...