Маршрутизация в Razor Pages пользовательский Tag Helper с JavaScript - PullRequest
0 голосов
/ 01 мая 2019

Я работаю над Tag Helper, чтобы создавать модалы.До сих пор я прошел большой путь, но теперь я застрял при передаче информации, которая требуется обработчику.

У меня есть три файла: ModalTagHelper.cs, _ModalPartial.cshtml и _ModalPartialScripts.cshtml.

Класс ModalTagHelper

Класс ModalTagHelper отвечает за создание Tag Helper.Он использует _ModalPartial.cshtml для рендеринга модальной модели и вызывает ModalTagHelperComponent класс для рендеринга требуемых js в нижней части html <body>

[HtmlTargetElement("modal", TagStructure = TagStructure.NormalOrSelfClosing)]
    public class ModalTagHelper : TagHelper
    {
        private const string ButtonTextAttributeName = "button-text";
        private const string ModalIdAttributeName = "modal-id";
        private const string ModalTitleAttributeName = "modal-title";
        private const string AcceptButtonTextAttributeName = "accept-button-text";
        private const string DeclineButtonTextAttributeName = "decline-button-text";
        private const string PageAttributeName = "page";
        private const string HandlerAttributeName = "handler";
        private const string RouteValuesDictionaryName = "all-route-data";
        private const string RouteValuesPrefix = "route-";

        private readonly IHtmlHelper _htmlHelper;
        private readonly ITagHelperComponentManager _tagHelperComponentManager;
        private IDictionary<string, string> _routeValues;

        public ModalTagHelper(IHtmlHelper htmlHelper, ITagHelperComponentManager tagHelperComponentManager)
        {
            this._htmlHelper = htmlHelper;
            this._tagHelperComponentManager = tagHelperComponentManager;
        }

        [HtmlAttributeNotBound]
        [ViewContext]
        public ViewContext ViewContext { get; set; }

        [HtmlAttributeName(ButtonTextAttributeName)]
        public string ButtonText { get; set; }

        [HtmlAttributeName(ModalIdAttributeName)]
        public string ModalId { get; set; }

        [HtmlAttributeName(ModalTitleAttributeName)]
        public string ModalTitle { get; set; }

        [HtmlAttributeName(AcceptButtonTextAttributeName)]
        public string AcceptButtonText { get; set; }

        [HtmlAttributeName(DeclineButtonTextAttributeName)]
        public string DeclineButtonText { get; set; }

        [HtmlAttributeName(PageAttributeName)]
        public string Page { get; set; }

        [HtmlAttributeName(HandlerAttributeName)]
        public string Handler { get; set; }

        [HtmlAttributeName(RouteValuesDictionaryName, DictionaryAttributePrefix = RouteValuesPrefix)]
        public IDictionary<string, string> RouteValues
        {
            get
            {
                if (this._routeValues == null)
                {
                    _routeValues = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
                }
                return this._routeValues;
            }
            set => this._routeValues = value;
        }

        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            var model = await this._createModalModel(output);


            this._tagHelperComponentManager.Components.Add(new ModalTagHelperComponent(this._htmlHelper, model));

            var content = await this._getContent(model);

            output.TagName = "";
            output.Content.SetHtmlContent(content);
        }

        private async Task<ModalModel> _createModalModel(TagHelperOutput output)
        {
            var jsBaseName = this._cleanTextForJavaScript(this.ButtonText);

            return new ModalModel
            {
                ButtonText = this.ButtonText,
                ModalId = string.IsNullOrWhiteSpace(this.ModalId) ? $"{jsBaseName}Id" : this.ModalId,
                ModalTitle = string.IsNullOrWhiteSpace(this.ModalTitle) ? this.ButtonText : this.ModalTitle,
                ModalBody = await output.GetChildContentAsync(),
                AcceptButtonText = string.IsNullOrWhiteSpace(this.AcceptButtonText)
                    ? "Accept"
                    : this.AcceptButtonText,
                DeclineButtonText = string.IsNullOrWhiteSpace(this.DeclineButtonText)
                    ? "Decline"
                    : this.DeclineButtonText,
                Page = string.IsNullOrWhiteSpace(this.Page)
                    ? "/Index"
                    : this.Page,
                Handler = string.IsNullOrWhiteSpace(this.Handler)
                    ? ""
                    : this.Handler,
                AcceptFuntionName = $"accept{jsBaseName}()",
                OpenModalFunctionName = $"open{jsBaseName}()",
                DeclineFunctionName = $"close{jsBaseName}()",
                RouteValues = this._getRouteValues()
            };
        }

        private RouteValueDictionary _getRouteValues()
        {
            RouteValueDictionary routeValues = null;
            if (this._routeValues != null && this._routeValues.Count > 0)
            {
                routeValues = new RouteValueDictionary(this._routeValues);
            }
            return routeValues;
        }

        private string _cleanTextForJavaScript(string input)
        {
            var output = input.ToLower();
            var regex = new Regex("[^a-z]");
            return regex.Replace(output, "");
        }

        private async Task<IHtmlContent> _getContent(ModalModel model)
        {
            (this._htmlHelper as IViewContextAware).Contextualize(this.ViewContext);
            return await this._htmlHelper.PartialAsync("~/Areas/ModalTagHelper/Pages/Shared/_ModalPartial.cshtml", model);
        }
    }

ModalModel class

ModalModelкласс используется для передачи информации в частичные представления

    public class ModalModel
    {
        public string ButtonText { get; set; }
        public string ModalId { get; set; }
        public string ModalTitle { get; set; }
        public TagHelperContent ModalBody { get; set; }
        public string AcceptButtonText { get; set; }
        public string DeclineButtonText { get; set; }
        public string Page { get; set; }
        public string Handler { get; set; }
        public string OpenModalFunctionName { get; set; }
        public string AcceptFuntionName { get; set; }
        public string DeclineFunctionName { get; set; }
        public RouteValueDictionary RouteValues { get; set; }
    }

ModalTagHelperComponent class

Помещает необходимые js в нижнюю часть HTML <body>

    public class ModalTagHelperComponent : TagHelperComponent
    {
        private readonly IHtmlHelper _htmlHelper;
        private readonly ModalModel _model;

        [ViewContext]
        public ViewContext ViewContext { get; set; }

        public ModalTagHelperComponent(IHtmlHelper htmlHelper, ModalModel model)
        {
            this._htmlHelper = htmlHelper;
            this._model = model;
        }

        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            if (string.Equals(context.TagName, "body", StringComparison.OrdinalIgnoreCase))
            {
                var script = await this._getContent(this._model);
                output.PostContent.AppendHtml(script);
            }
        }

        private async Task<IHtmlContent> _getContent(ModalModel model)
        {
            (this._htmlHelper as IViewContextAware).Contextualize(this.ViewContext);
            return await this._htmlHelper.PartialAsync("~/Areas/ModalTagHelper/Pages/Shared/_ModalPartialScript.cshtml", model);
        }
    }

_ModalPartial.cshtml

Вызывается ModalTagHelper, чтобы отобразить кнопку для открытия модала и самого модала

@model AspNetCore.Razor.Areas.ModalTagHelper.TagHelpers.ModalModel

<a href="#" class="btn btn-primary" onclick="@Model.OpenModalFunctionName">@Model.ButtonText</a>
<div id="@Model.ModalId" class="modal fade">
    <div class="modal-dialog modal-lg">
        <div class="modal-content">
            <div class="modal-header">
                <h4 class="modal-title">@Model.ModalTitle</h4>
                <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                    <span aria-hidden="true">&times;</span>
                </button>
            </div>
            <div class="modal-body">
                @Model.ModalBody
            </div>
            <div class="modal-footer">
                <a href="#" onclick="@Model.AcceptFuntionName" class="btn btn-danger">
                    @Model.AcceptButtonText
                </a>
                <button type="button" class="btn btn-primary" data-dismiss="modal">
                    @Model.DeclineButtonText
                </button>
            </div>
        </div>
    </div>
</div>

_ModalPartialScript.cshtml

Вызывается ModalTagHelperComponent длярендеринга JS в нижней части HTML <body>.В этом коде я проверяю, есть ли какие-либо значения RouteValues, и когда они есть, он помещает их в URL в качестве параметров пути.

@model AspNetCore.Razor.Areas.ModalTagHelper.TagHelpers.ModalModel

@{
    var url = @Model.Page;

    if (@Model.RouteValues != null)
    {
        foreach (var routeValue in @Model.RouteValues)
        {
            url += $"/{routeValue.Value}";
        }
    }

    if (!string.IsNullOrWhiteSpace(Model.Handler))
    {
        url += $"?handler={Model.Handler}";
    }
}

<script>
    function @Model.OpenModalFunctionName {
        $("#@Model.ModalId").modal('show');
    };

    function @Model.AcceptFuntionName {
        var requestVerificationToken =
            document.getElementsByName("__RequestVerificationToken")[0].value;

        fetch("@url", {
            headers: {
                "Content-Type": "application/json",
                "RequestVerificationToken": requestVerificationToken
            },
            method: 'POST'
        });

        $("#@Model.ModalId").modal('hide');
    };
</script>

Пример использования

Использование свойств

В качестве примера у меня есть страница Index Razor, которая использует помощник по тегам modal.Когда вызывается метод OnGet(), устанавливаются свойства Name и Id.Но когда вызывается метод OnPostHello(), эти значения равны default, что, я думаю, имеет смысл, поскольку я не использую среду для вызова метода OnPostHello() и вызова его напрямую через JavaScript.

Index.cshtml.cs

public class IndexModel : PageModel
{
    public string Name { get; set; }
    public int Id { get; set; }

    public void OnGet()
    {
        this.Name = "Johan";
        this.Id = 124;
    }

    public void OnPost()
    {
        Console.WriteLine("Index POST method called");
    }

    public void OnPostHello()
    {
        Console.WriteLine($"Hello POST method called. Id = {this.Id}, Name = {this.Name}");
    }
}

Index.cshtml

@Html.AntiForgeryToken()
<div class="container">
    <div class="row">
        <modal button-text="Delete" handler="hello">
            <h1>Hello, @Model.Name</h1>
            <p>@Model.Name has id @Model.Id</p>
        </modal>
    </div>
</div>

Использование параметров URL-адреса

Следующее, что я попробовал, было использование параметров URL-адреса.Я уже построил это в классе ModalTagHelper и решил, что это должно работать.

Index.cshtml.cs

public class IndexModel : PageModel
{
    // Omitted for brevity

    public void OnPostHello(int id)
    {
        Console.WriteLine($"Hello POST method called. Id = {id}");
    }
}

Index.cshtml

@Html.AntiForgeryToken()
<div class="container">
    <div class="row">
        <modal button-text="Delete" handler="hello" route-id="@Model.Id">
            <h1>Hello, @Model.Name</h1>
            <p>@Model.Name has id @Model.Id</p>
        </modal>
    </div>
</div>

КогдаЯ смотрю на вывод javascript с помощью Chrpme inspect, вижу, что URL-адрес установлен.

<script>
    function opendelete() {
        $("#deleteId").modal('show');
    };

    function acceptdelete() {
        var requestVerificationToken =
            document.getElementsByName("__RequestVerificationToken")[0].value;

        fetch("/Index/124?handler=hello", {
            headers: {
                "Content-Type": "application/json",
                "RequestVerificationToken": requestVerificationToken
            },
            method: 'POST'
        });

        $("#deleteId").modal('hide');
    };
</script>

Но когда я нажимаю кнопку, которая должна вызвать обработчик, этого не происходит.

Итак, у меня два вопроса:

  1. Могу ли я использовать значения свойств, которые были установлены в методе OnGet(), так или иначе?
  2. Как можноЯ передаю в URL параметры, чтобы они были подобраны ASP.NET Core?
...