3

我有一个视图,其中包含通过部分视图呈现的项目列表。

我希望能够在部分视图中只为整个页面添加一次 javascript 脚本。
我读到我可以用来在所有(部分)视图之间Context.Items共享数据(如 boolean )。ScriptAlreadyIncluded

但是我可以依靠这个吗?还是我应该使用其他东西?


我当前的代码(最相关的部分视图是 ITypedModel.cshtml 和 AFieldFormula_DirectFieldFormula.cshtm)

Index.cshtml

@model IEnumerable<MygLogWeb.Classes.MemberField>

@{
    Layout = "~/Views/Shared/_Layout.cshtml";

    var uid = Guid.NewGuid().ToString();
}

@using (Html.BeginForm("Index", "MemberField", FormMethod.Post, new { id = uid }))
{

    @Html.AntiForgeryToken()

    <table class="table none">
        <thead>
            <tr>
                <th>
                    @Html.DisplayNameFor(model => model.IsSystem)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.IsVirtual)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.Name)
                </th>
                <th>
                    @Html.DisplayNameFor(model => model.Formula)
                </th>
                <th></th>
            </tr>
        </thead>
        <tbody>
            @Html.EditorFor(model => model)
        </tbody>
        <tfoot>
            <tr>
                <td colspan="5">
                    <input type="button" value="@Resources.Common.Buttons.add" />
                </td>
            </tr>
        </tfoot>
    </table>

    <input type="submit" value="@Resources.Common.Buttons.save"/>
}

<script type="text/javascript">
    new TableEdit("@uid");
</script>

EditorTemplates/MemberField.cshtml

@model MygLogWeb.Classes.MemberField

<tr>
    <td>
        @Html.DisplayFor(model => model.IsSystem)
        @Html.HiddenFor(model => model.IsSystem)
    </td>
    <td>
        @Html.DisplayFor(model => model.IsVirtual)
        @Html.HiddenFor(model => model.IsVirtual)
    </td>
    <td>
        @if (Model.IsSystem)
        {
            @Html.DisplayFor(model => model.Name)
            @Html.HiddenFor(model => model.Name)
        }
        else
        {
            @Html.TextBoxFor(model => model.Name, new { data_focus = "true" })
            @Html.ValidationMessageFor(model => model.Name)
        }
    </td>
    <td>
        @Html.EditorFor(model => model.Formula, "ITypedModel")
    </td>
    <td>
        @if (!Model.IsSystem)
        {
            <input type="button" value="@Resources.Common.Buttons.delete" data-code="delete" />
        }
    </td>
</tr>

EditorTemplates/ITypedModel.cshtml

...
@foreach (var item in items)
{
    <span data-refered-type="@item.Item.Value">
        @if (item.Type.IsSubclassOf(typeof(AFieldFormula)))
        {
            var newModel = item.Item.Selected ? Model : Activator.CreateInstance(item.Type);

            @Html.Partial("~/Views/MemberField/EditorTemplates/AFieldFormula_" + item.Type.Name + ".cshtml", newModel)
        }
    </span>
}

EditorTemplates/AFieldFormula_ConstantFormula.cshtml

@model MygLogWeb.Classes.ConstantFormula

<b>ConstantFormula</b>
@Html.EditorFor(model => model.Constant)
@Html.ValidationMessageFor(model => model.Constant)

EditorTemplates/AFieldFormula_DirectFieldFormula.cshtml

@model MygLogWeb.Classes.DirectFieldFormula

...Build a JSON dictionnary...

<b>DirectFieldFormula</b>
@Html.EditorFor(model => model.Field)
@Html.ValidationMessageFor(model => model.Field)

...Some controls using the JSON dictionnary...

每个 AFieldFormula_DirectFieldFormula.cshtml 视图的字典都是相同的,所以我想每个网页只计算和显示一次。

4

4 回答 4

2

Context.Items(线程)安全吗?

不,因为HttpContext不是线程安全的,来自文档

此类型的任何公共静态(在 Visual Basic 中为 Shared)成员都是线程安全的。不保证任何实例成员都是线程安全的。

如果您希望在第一次渲染时将隐藏字段添加到页面中,并在后续渲染中检查该字段是否存在,就可以渲染它。


根据您的评论,您应该能够ViewBag在各个部分视图中使用 to mainain 状态,并确保脚本只呈现一次

ITypedModel.cshtml

...
@foreach (var item in items)
{
    <span data-refered-type="@item.Item.Value">
        @if (item.Type.IsSubclassOf(typeof(AFieldFormula)))
        {
            var newModel = item.Item.Selected ? Model : Activator.CreateInstance(item.Type);
            @Html.Partial("~/Views/MemberField/EditorTemplates/AFieldFormula_" + item.Type.Name + ".cshtml", newModel, new ViewDataDictionary { { "vb", ViewBag }})
        }
    </span>
}

AFieldFormula_DirectFieldFormula.cshtml

@model MygLogWeb.Classes.DirectFieldFormula

@{ var vb = ((dynamic)ViewData["vb"]); }

@if (!vb.ScriptAlreadyIncluded)
{
    ...Build a JSON dictionnary...   
}

<b>DirectFieldFormula</b>
@Html.EditorFor(model => model.Field)
@Html.ValidationMessageFor(model => model.Field)

@if (!vb.ScriptAlreadyIncluded)
{
    ...Some controls using the JSON dictionnary...
}

@if (!vb.ScriptAlreadyIncluded)
{
    @{ vb.ScriptAlreadyIncluded = true; } // set the flag
}

这将在第一次调用时呈现脚本RenderPartial("AFieldFormula_DirectFieldFormula"),然后将ViewBag.ScriptAlreadyIncluded属性设置为true. 然后在随后的渲染中(因为ViewBag正在传递),这个属性将跳过 JS 的东西,因为ScriptAlreadyIncluded已经设置了。

于 2013-11-05T10:41:55.763 回答
1

Context.Items(线程)安全吗?

不,正如詹姆斯在回答中所说的那样

但有必要吗?如果您不进行任何异步处理,您的请求将全部在同一个线程上执行,因此线程安全不是问题。

Context.Items 旨在在请求处理管道的不同部分之间共享对象。

于 2013-11-05T14:16:59.270 回答
0

基于@James 的想法和这篇文章

我可以只使用 :ViewContext.Controller.ViewBag因为我只有一个控制器用于整个页面(我不在那里使用 ChildActions)。

它给:

@model MygLogWeb.Classes.DirectFieldFormula

@{
    var GlobalViewBag = ViewContext.Controller.ViewBag;
    if (GlobalViewBag.DirectFieldFormula_ScriptAlreadyIncluded as bool? != true)
    {
        <script type="text/javascript">
            // ...
        </script>
    }

    GlobalViewBag.DirectFieldFormula_ScriptAlreadyIncluded = true;
}

...
于 2013-11-06T09:11:07.307 回答
0

所以,这是一个老问题,但我在尝试解决最近的问题时发现了它。

根据Microsoft的说法,HttpContext 不是线程安全的。好消息是有一种方法可以实现一个可重用的解决方案,该解决方案仅在由 ASP.NET 控制的线程中使用 HttpContext,正如 Microsoft 所推荐的那样。主要限制是此方法只能跟踪使用它添加的脚本,但是专门为向局部视图添加功能而创建的脚本无论如何都只能与局部视图一起使用。见下文。

是我留下这个答案的原始位置。它HttpContext.Current.Items用于维护一个单例请求静态类,其中包含已经包含的脚本字典(使用此方法)。

实现一个 Html 辅助扩展函数,它只会加载一次脚本。(这也适用于 CSS 和其他包含的文件)。

实现 HtmlHelper 类的扩展和私有支持类作为每个 HttpContext 的单例。

public static class YourHtmlHelperExtensionClass 
{
    private class TagSrcAttrTracker
    {
        private TagSrcAttrTracker() { }

        public Dictionary<string, string> sources { get; } = new Dictionary<string, string>();

        public static TagSrcAttrTrackerInstance {
            get {
                IDictionary items = HttpContext.Current.Items;
                const string instanceName = "YourClassInstanceNameMakeSureItIsUniqueInThisDictionary";

                if(!items.Contains(instanceName)) 
                    items[instanceName] = new TagSrcAttrTracker();

                return items[instanceName] as TagSrcAttrTracker;
            }
        }
    }

    public static MvcHtmlString IncludeScriptOnlyOnce(this HtmlHelper helper, string urlOfScript) 
    {
        if(TagSrcAttrTracker.Instance.sources.ContainsKey(urlOfScript))
            return null;

        TagSrcAttrTracker.Instance.sources[urlOfScript] = urlOfScript;

        TagBuilder script = new TagBuilder("script");
        scriptTag.MergeAttribute("src", urlOfScript);

        return MvcHtmlString.Create(script.ToString());
    }
}

然后,将 JavaScript 和其他代码分离到单独的文件中。

示例 .js 文件内容

class MyClass{
    myFunction() {
        constructor(divId){
            this._divId = divId
        }

        doSomething() {
            // do something with a div with id == divId
        }
    }
}

局部视图的示例 .cshtml 文件内容

<link rel="stylesheet" type="text/css" href="~/Content/CustomCSS/MyPartialView.css"/>

<div id="@ViewBag.id">
    Some content!  Yay!
</div>

@Html.IncludeScriptOnlyOnce("/Scripts/CustomScripts/MyPartialView.js")

使用局部视图的示例 .cshtml 文件

...
<body>
    <h1>Here is some content!</h1>
    @Html.Partial("/Views/MyPartial.cshtml", new ViewDataDictionary() { { "id", "id_1"} })
    @Html.Partial("/Views/MyPartial.cshtml", new ViewDataDictionary() { { "id", "id_2"} })
    @Html.Partial("/Views/MyPartial.cshtml", new ViewDataDictionary() { { "id", "id_3"} })
</body>
<script>
$().ready(
    const id1Functionality = new MyClass("id_1") // forgive the poor naming :-)
    const id2Functionality = new MyClass("id_3")
    const id3Functionality = new MyClass("id_2")

    id1Functionality.doSomething();
    id2Functionality.doSomething();
    id3Functionality.doSomething();
)
</script>

部分视图可能被多次包含,并且 JavaScript 与部分视图一起打包,但 .js 文件只包含在页面中一次,因此浏览器不会抱怨 MyClass 被多次声明。

于 2020-05-28T15:14:04.727 回答