我在TagHelper类的处理方法中生成脚本如下
[TargetElement("MyTag")]
public Class MYClass: TagHelper{
public override void Process(TagHelperContext context, TagHelperOutput output)
{
StringBuilder builder = new StringBuilder();
builder.Append("<script>");
builder.Append("//some javascript codes here);
builder.Append("</script>");
output.Content.Append(builder.ToString());
}
}
现在它将脚本放在标签元素的旁边,作为它的兄弟。
我需要将脚本放在正文部分的末尾。
答案 0 :(得分:4)
我创建了一对能够解决问题的自定义标记帮助程序。
第一个是<storecontent>
,它只是将包含在其中的html内容存储在TempData字典中。它不提供直接输出。内容可以是内联脚本或任何其他html。许多这种标签帮助器可以放置在不同的位置,例如在部分视图中。
第二个标记助手是<renderstoredcontent>
,它将所有先前存储的内容呈现在所需位置,例如在body元素的末尾。
StoreContentTagHelper.cs
的代码:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
namespace YourProjectHere.TagHelpers
{
[TargetElement("storecontent", Attributes = KeyAttributeName)]
public class StoreContentTagHelper : TagHelper
{
private const string KeyAttributeName = "asp-key";
private const string _storageKey = "storecontent";
private const string _defaultListKey = "DefaultKey";
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
[HtmlAttributeName(KeyAttributeName)]
public string Key { get; set; }
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
output.SuppressOutput();
TagHelperContent childContent = await context.GetChildContentAsync();
var storageProvider = ViewContext.TempData;
Dictionary<string, List<HtmlString>> storage;
List<HtmlString> defaultList;
if (!storageProvider.ContainsKey(_storageKey) || !(storageProvider[_storageKey] is Dictionary<string,List<HtmlString>>))
{
storage = new Dictionary<string, List<HtmlString>>();
storageProvider[_storageKey] = storage;
defaultList = new List<HtmlString>();
storage.Add(_defaultListKey, defaultList);
}
else
{
storage = ViewContext.TempData[_storageKey] as Dictionary<string, List<HtmlString>>;
if (storage.ContainsKey(_defaultListKey))
{
defaultList = storage[_defaultListKey];
}
else
{
defaultList = new List<HtmlString>();
storage.Add(_defaultListKey, defaultList);
}
}
if (String.IsNullOrEmpty(Key))
{
defaultList.Add(new HtmlString(childContent.GetContent()));
}
else
{
if(storage.ContainsKey(Key))
{
storage[Key].Add(new HtmlString(childContent.GetContent()));
}
else
{
storage.Add(Key, new List<HtmlString>() { new HtmlString(childContent.GetContent()) });
}
}
}
}
}
RenderStoredContentTagHelper.cs
的代码:
using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Mvc.Rendering;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
namespace YourProjectHere.TagHelpers
{
[TargetElement("renderstoredcontent", Attributes = KeyAttributeName)]
public class RenderStoredContentTagHelper : TagHelper
{
private const string KeyAttributeName = "asp-key";
private const string _storageKey = "storecontent";
[HtmlAttributeNotBound]
[ViewContext]
public ViewContext ViewContext { get; set; }
[HtmlAttributeName(KeyAttributeName)]
public string Key { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output)
{
output.TagName = String.Empty;
var storageProvider = ViewContext.TempData;
Dictionary<string, List<HtmlString>> storage;
if (!storageProvider.ContainsKey(_storageKey) || !(storageProvider[_storageKey] is Dictionary<string, List<HtmlString>>))
{
return;
}
storage = storageProvider[_storageKey] as Dictionary<string, List<HtmlString>>;
string html = "";
if (String.IsNullOrEmpty(Key))
{
html = String.Join("", storage.Values.SelectMany(x => x).ToList());
}
else
{
if (!storage.ContainsKey(Key)) return;
html = String.Join("", storage[Key]);
}
TagBuilder tagBuilder = new TagBuilder("dummy");
tagBuilder.InnerHtml = html;
output.Content.SetContent(tagBuilder.InnerHtml);
}
}
}
基本用法:
在某些视图或部分视图中:
<storecontent asp-key="">
<script>
your inline script...
</script>
</storecontent>
在另一个地方:
<storecontent asp-key="">
<script src="..."></script>
</storecontent>
最后在应该呈现两个脚本的所需位置:
<renderstoredcontent asp-key=""></renderstoredcontent>
就是这样。
一些注意事项:
<storecontent>
代码。 asp-key
属性是必需的,至少为空“”。如果为此属性指定特定值,则可以对存储的内容进行分组,并在不同位置呈现特定组。例如。如果您使用asp-key="scripts"
指定某些内容,而使用asp-key="footnotes"
指定其他内容,则可以使用以下方式仅将第一个群组渲染为某个位置: <renderstoredcontent asp-key="scripts"></renderstoredcontent>
另一组“脚注”可以在其他位置呈现。
必须在应用<storecontent>
之前定义<renderstoredcontent>
。在ASP.NET中,响应以反向层次顺序生成,首先生成最内部的部分视图,然后是父部分视图,然后是主视图,最后是布局页面。因此,您可以轻松地使用这些标记帮助程序在局部视图中定义脚本,然后在布局页面的正文部分的末尾渲染脚本。
不要忘记使用@addTagHelper "*, YourProjectHere"
对不起,很长的帖子,我希望它有所帮助!
答案 1 :(得分:3)
创建BodyTagHelper
,将值插入TagHelperContext.Items
,然后在自定义TagHelper
中设置。
完整的代码:
public class BodyContext
{
public bool AddCustomScriptTag { get; set; }
}
public class BodyTagHelper : TagHelper
{
public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
{
var builder = new StringBuilder();
var bodyContext = new BodyContext();
context.Items["BodyContext"] = bodyContext;
// Execute children, they can read the BodyContext
await context.GetChildContentAsync();
if (bodyContext.AddCustomScriptTag)
{
// Add script tags after the body content but before end tag.
output
.PostContent
.Append("<script>")
.Append("//some javascript codes here")
.Append("</script>");
}
}
}
[TargetElement("MyTag")]
public class MYClass : TagHelper
{
public override void Process(TagHelperContext context, TagHelperOutput output)
{
// Do whatever you want
object bodyContextObj;
if (context.Items.TryGetValue("BodyContext", out bodyContextObj))
{
// Notify parent that we need the script tag
((BodyContext)bodyContextObj).AddCustomScriptTag = true;
}
}
}
希望这有帮助!
答案 2 :(得分:1)
使用@section scripts {}
在布局上呈现@RenderSection("scripts")
,并将标记帮助器放在脚本部分中。渲染时,它将放置在Layout(在html底部)上定义的位置。
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div>
<p>some html ... bla bla bla</p>
@RenderBody()
</div>
@RenderSection("scripts", required: false)
</body>
</html>
然后在任何其他cshtml文件上,
<p>Some page</p>
@section scripts {
<mytaghelper>foo</mytaghelper>
}
答案 3 :(得分:0)
我不相信tagHelper内部可以在底部或其他任何地方添加脚本,但taghelper正在呈现的标记位置。我认为如果taghelper依赖于某些外部js文件,那么添加脚本不应该是taghelper本身的责任。例如,内置验证标记符如:
<span asp-validation-for="Email" class="text-danger"></span>
所有验证taghelper都是用数据属性装饰span,它不会向页面添加任何脚本,如果脚本丢失,数据属性将被忽略。
考虑一个视图可能有多个验证标记使用,我们不希望每个添加另一个脚本。
在VS starter web app模板中,您可以看到验证脚本是由视图底部的部分视图添加的(例如Login.cshtml)
@{await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
自动化脚本包含的一种可能策略是你的tagHelper可以在其构造函数中使用IHttpContextAccessor,因此它将由DI注入,然后你可以访问HttpContext.Items集合并添加一个变量来指示需要一个脚本,然后在添加脚本的局部视图,您可以检测添加的变量,以决定要包含哪些脚本。
但是我自己认为只需在需要的地方添加脚本来支持taghelper的使用,而不是试图自动添加并自动添加内容。
这个想法只适用于外部js文件而不适用于在taghelper中动态编写的js,但最好不要使用这样的脚本,并且只在可能的情况下才使用外部脚本文件。如果你真的需要在taghelper中生成脚本,我想你只能在taghelper正在处理的元素的位置呈现它。
答案 4 :(得分:0)
不要将javascript放在页面底部,而是可以更进一步,从你的javascript中完全分离你的html(taghelper)。编写您的Javascript,以便找到您的taghelper并初始化自己。
这里的示例是我使用的Taghelper / Javascript,它采用UTC日期时间并在用户本地时间显示,格式化为日期时间,时间或日期。
标签助手
[HtmlTargetElement("datetime", Attributes = "format,value")]
public class DateTimeTagHelper : TagHelper {
[HtmlAttributeName("format")]
public DateTimeFormat Format { get; set; }
[HtmlAttributeName("value")]
public DateTime Value { get; set; }
public override void Process(TagHelperContext context, TagHelperOutput output) {
output.TagName = "span";
output.TagMode = TagMode.StartTagAndEndTag;
output.Attributes.Add("class", "datetime_init");
output.Attributes.Add("format", Format);
output.Attributes.Add("value", Value.ToString("u"));
}
}
Javascript(需要moment.js但与概念无关)
$(document).ready(function () {
DateTime_Init();
}
function DateTime_Init() {
$(".datetime_init").each(function () {
var utctime = $(this).attr("value");
var localTime = moment.utc(utctime).toDate();
switch($(this).attr("format")) {
case "Date":
$(this).html(moment(localTime).format('DD/MM/YYYY'));
break;
case "Time":
$(this).html(moment(localTime).format('HH:mm'));
break;
default:
$(this).html(moment(localTime).format('DD/MM/YYYY HH:mm'));
break;
}
//Ensure this code only runs once
$(this).removeClass("datetime_init");
});
}
答案 5 :(得分:0)
我知道这个帖子已经过时了,但如果有人正在寻找一个简单的方法来运行一些javascript,这是一种方式。
首先,ViewComponents渲染服务器端,所以当然客户端脚本不会准备就绪。正如其他人指出的那样,你可以在需要的地方渲染一些部分脚本来解释你的标签帮助器,这对于解耦非常有用,你只需要在需要的地方包含脚本。
但是,您的标记帮助程序通常将数据作为与客户端脚本相关的输入。为了能够通过js函数运行这些数据,你可以做这样的事情。
<强> TagHelper.cs 强>
var data= $@"
'{Id}',
'{Title}',
{JsonConvert.SerializeObject(MyList)}";
output.Attributes.SetAttribute("data-eval", data);
<强> site.js 强>
$(".tag-helper-class").each((i, e) => {
const jq = $(e);
const data= jq.data("eval");
if (!data) {
return;
}
jq.attr("data-eval", "");
eval(`myJsFunction(${data})`);
});
现在,当脚本准备就绪时,他们可以查找你的标签助手并使用相关数据执行正确的功能。
答案 6 :(得分:0)
也许不是最优雅的解决方案,但仍然可以使用:
将要生成的标签包装在span
内部,然后将一些HTML附加到此InnerHtml
的{{1}}中:
span