MVC выполняет код представления перед кодом макета и разрушает мой порядок скриптов - PullRequest
7 голосов
/ 04 ноября 2011

Я пытаюсь переместить все мои javascript-сообщения в конец страницы.Я использую MVC с Razor.Я написал вспомогательный метод для регистрации скриптов.Он сохраняет скрипты в порядке их регистрации и исключает дублирование.

@{ Html.RegisterScript("/scripts/someFile.js"); }

А потом, прямо перед закрывающим тегом body, я делаю что-то вроде этого:

    @Html.RenderScripts()
</body>

Это все работает нормально, если все скрипты зарегистрированы на одной странице.Если я регистрирую некоторые сценарии в моем макете, а некоторые сценарии в моем внутреннем представлении, все идет не так, как надо.По сути, внутренний вид выполняется перед макетом.Поэтому, хотя сценарии во внутреннем представлении зависят от сценариев во внешнем представлении, они регистрируются ранее.

Так что, если у меня есть _Master.cshstml и он имеет

@{
   Html.RegisterScript("script1.js");
   Html.RegisterScript("script2.js");
}

И тогда у меня есть InnerView.cshtml с _Master.cshtml в качестве макета, и в представлении у меня есть

@{
   Html.RegisterScript("script3.js");
}

Сценарии отображаются в следующем порядке: script3, script1, script 2.

Есть ли способ заставить макет выполнить перед внутренним видом?Я пытался перемещать вещи повсюду, и все, что кажется внутренним взглядом, всегда выполняется первым.Возможно, это ожидается?Если да, то как мне лучше всего выполнить то, что я пытаюсь сделать?

Ответы [ 6 ]

3 голосов
/ 04 ноября 2011

Нет, это порядок, который они выполняют, от самого внутреннего до самого внешнего. Я бы порекомендовал использовать какую-то коллекцию LIFO (последний пришел первым - вышел), например Stack<T>, чтобы удержать скрипты, а затем вытолкнуть их из стека для их рендеринга; таким образом, сценарии, добавленные последними, т. е. те, что в макете, будут отображаться первыми.

EDIT

Я написал пакет nuget, который обрабатывает сценарии рендеринга из частичных представлений и шаблонов , которые решают эту проблему.

2 голосов
/ 04 ноября 2011

Я решил эту проблему, отслеживая количество сценариев для каждого просмотра. В основном я держу

Dictionary<string, SortedList<int, string>> registeredScripts;

и

 SortedList<int, string> viewOrder;

Каждый раз, когда я регистрирую скрипт, я передаю путь к скрипту и имя представления (получено из ViewDataContainer.ToString ()). Я отслеживаю порядок, в котором я видел представления в объекте viewOrder. Затем я использую словарь для отслеживания сценариев для каждого представления. Ключ - это имя представления. Когда приходит время рендерить все, я переворачиваю viewOrder SortedList <> и выписываю все сценарии по представлению. Приятно то, что сценарии располагаются по порядку в представлении, а обратный порядок представления является правильным.

Я сделал ужасную работу, объясняя это, но по соображениям авторского права я не могу поделиться кодом.

1 голос
/ 26 ноября 2011

Сначала создайте 2 помощника HTML. Один для добавления сценариев по приоритету:

    public static MvcHtmlString AddScript(this HtmlHelper htmlHelper, string script, int executionSequence = int.MaxValue)
    {
        Dictionary<int, List<string>> scripts = new Dictionary<int, List<string>>();
        if (htmlHelper.ViewContext.HttpContext.Items["_scripts_"] != null)
        {
            scripts = htmlHelper.ViewContext.HttpContext.Items["_scripts_"] as Dictionary<int, List<string>>;
        }

        if (!scripts.ContainsKey(executionSequence))
        {
            scripts.Add(executionSequence, new List<string>());
        }
        string tag = string.Format("<script type=\"text/javascript\" src=\"{0}\"></script>", script);
        scripts[executionSequence].Add(tag);
        htmlHelper.ViewContext.HttpContext.Items["_scripts_"] = scripts;
        return MvcHtmlString.Empty;
    }

И один для написания этих скриптов на ваш взгляд:

    public static IHtmlString RenderScripts(this HtmlHelper htmlHelper)
    {
        StringBuilder sb = new StringBuilder();
        foreach (object key in htmlHelper.ViewContext.HttpContext.Items.Keys)
        {
            if (key.ToString() == "_scripts_")
            {
                Dictionary<int, List<string>> scripts = htmlHelper.ViewContext.HttpContext.Items[key] as Dictionary<int, List<string>>;
                foreach (int index in scripts.Keys.OrderBy(x => x))
                {
                    foreach (string script in scripts[index])
                    {
                        if (script != null)
                        {
                            sb.AppendLine(script);
                        }
                    }
                }
            }
        }

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

Затем добавьте сценарии к частичным представлениям:

@Html.AddScript("script2.js", 2)
@Html.AddScript("script1.js", 1)

И, наконец, визуализируйте сценарии в шаблоне Razor:

@Html.RenderScripts()

Результаты будут отсортированы по приоритету и отображены внутри тегов скрипта:

<script type="text/javascript" src="script1.js"></script>
<script type="text/javascript" src="script2.js"></script>
1 голос
/ 04 ноября 2011

Нет способа изменить порядок рендеринга страниц MVC (насколько я знаю), но вы можете попытаться обновить функциональность сценария рендеринга, чтобы указать, когда макеты регистрируют свои сценарии.Так, например, ваш макет может быть:

@{
   Html.StartLayoutScriptRegistration();
   Html.RegisterScript("script1.js");
   Html.RegisterScript("script2.js");
}

Это приведет к тому, что в вашей системе сначала будут отображаться сценарии, зарегистрированные после вызова StartLayoutScriptRegistration(), а затем - регистрации сценариев вашего представления, зарегистрированные до 'StartLayoutScriptRegistration () 'вызывается ..

0 голосов
/ 23 мая 2013

Код, размещенный в @ секции части всегда выполняются в ожидаемом порядке, поэтому вы можете использовать этот факт:

В вашем _Master.cshtml пишите:

@{
   Html.RegisterScript("script1.js");
   Html.RegisterScript("script2.js");
}

@RenderSection("references", required: false)

И в вашем InnerView.cshtml:

@section references {@{
       Html.RegisterScript("script3.js");
}}

Это приведет к желаемому порядку включения: script1, script2, script3

Примечания: Поскольку раздел «ссылки» будет состоять только из одного блока кода и без реального вывода HTML, вы можете поместить часть @RenderSection где угодно в _Master.cshtml. Это будет работать и с вложенными макетами!

0 голосов
/ 29 мая 2012

В качестве точного решения для зависимостей сценариев вы можете предоставить несколько вспомогательных методов для:

Укажите зависимости

Html.RegisterScript("script3.js", depsOn: new string[] {"script1.js", "script2.js"});

Укажите относительный приоритет:

Html.RegisterScript("script3.js", Position.Bottom);
Html.RegisterScript("script1.js", Position.Top);
Html.RegisterScript("script2.js", Position.Top);

Этото, как у вас есть контроль, необходимый в большинстве ситуаций.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...