Я работаю над 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">×</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>
Но когда я нажимаю кнопку, которая должна вызвать обработчик, этого не происходит.
Итак, у меня два вопроса:
- Могу ли я использовать значения свойств, которые были установлены в методе
OnGet()
, так или иначе? - Как можноЯ передаю в URL параметры, чтобы они были подобраны ASP.NET Core?