Впорскування вмісту в конкретні розділи з часткового перегляду ASP.NET MVC 3 за допомогою системи Razor View Engine


324

Цей розділ у мене визначений _Layout.cshtml

@RenderSection("Scripts", false)

Я можу легко використовувати його з точки зору:

@section Scripts { 
    @*Stuff comes here*@
}

З чим я борюся - це як отримати частковий вміст у цей розділ з часткового перегляду.

Припустимо, це моя сторінка перегляду:

@section Scripts { 

    <script>
        //code comes here
    </script>
}

<div>
    poo bar poo
</div>

<div>
  @Html.Partial("_myPartial")
</div>

Мені потрібно ввести деякий вміст всередині Scriptsрозділу з _myPartialчасткового перегляду.

Як я можу це зробити?


17
для тих, хто прийде до цього пізніше - існує пакет пакунків для вирішення цього питання: nuget.org/packages/Forloop.HtmlHelpers
Russ Cam

@RussCam ви повинні відповісти на це питання. +1 пакунок "nuget" вирішує точну проблему, яка виникає в ОП.
Керрі Кендалл

1
@RussCam NuGet пакет не є рішенням, код пакету може бути.
Максим Ві.

8
@MaksimVi. добре, я написав пакунок nuget і не маю наміру знімати його, тому замість повторення коду ( bitbucket.org/forloop/forloop-htmlhelpers/src ) або вікі ( bitbucket.org/forloop/forloop-htmlhelpers/wiki / Головна ) тут, посилання на нього як коментар зберігається в дусі stackoverflow, IMO.
Russ Cam

Ось ще одне рішення , яке , здається , дуже добре: stackoverflow.com/questions/5355427 / ...
jkokorian

Відповіді:


235

Розділи не працюють за частковим виглядом, і це за дизайном. Ви можете використовувати деякі спеціальні помічники для досягнення подібної поведінки, але, чесно кажучи, це відповідальність за перегляд, включаючи необхідні сценарії, а не часткову відповідальність. Я рекомендую використовувати розділ @scripts головного виду, щоб це зробити, і не турбуватися про особливості сценаріїв.


445
Але що робити, якщо сценарій дуже специфічний для часткового? Чи не має логічного сенсу це визначати в частковому, а не в погляді?
Jez

43
Чому саме по дизайну?
Шиммі Вайцхандлер

56
@Darin: Я не згоден. Що щодо принципу DRY? Я не люблю повторювати себе, навіть якщо це лише посилання на сценарій.
fretje

14
@fretje, кожен має право висловити свою думку з цього приводу. Я поважаю твою. У своїй відповіді я висловив своє і пов'язав з відповіддю, яка дозволила б вам досягти цього завдання. Але я також наголосив на тому, що я рекомендував би зробити у цій ситуації.
Дарин Димитров

33
seconding @JoshNoe та інше - "віджет" (дисплей + багата взаємодія) є прекрасним прикладом часткового перегляду, щільно поєднаного з пов’язаним JavaScript. За дизайном я не повинен писати два включення заяви в різних місцях, щоб отримати повний функціонал, оскільки дисплей ніколи не буде без супутньої взаємодії, а взаємодія ніколи не з’явиться в іншому місці.
drzaus

83

Це досить популярне питання, тому я опублікую своє рішення.
У мене була така ж проблема, і хоча вона не є ідеальною, я думаю, що вона насправді працює досить добре і не робить часткове залежність від погляду.
Мій сценарій полягав у тому, що дія була доступна сама по собі, але також могла бути вбудована у перегляд aa - карта Google.

В моєму _layout:

@RenderSection("body_scripts", false)

На мою indexдумку, я маю:

@Html.Partial("Clients")
@section body_scripts
{
    @Html.Partial("Clients_Scripts")
}

На мій clientsпогляд, у мене є (вся карта та допоміжні html):

@section body_scripts
{
    @Html.Partial("Clients_Scripts")
}

Мій Clients_Scriptsпогляд містить JavaScript, який потрібно відобразити на сторінці

Таким чином мій скрипт є ізольованим і його можна перенести на сторінку там, де це потрібно, при цьому body_scriptsтег виводиться лише при першому появі, коли движок перегляду бритви знайде його.

Це дозволяє мені все розділити - це рішення, яке працює досить добре для мене, інші можуть мати проблеми з цим, але він зашиває отвір "за задумом".


2
Я не був тим, хто вас проголосував, але я скажу, що мені це рішення не дуже подобається, оскільки воно все ще відокремлює певні сценарії для перегляду від самого погляду.
розчавити

3
Ще 20 людей, і я маю іншу думку. Ви все ще можете мати сценарії, безпосередньо пов’язані з представленням даних, що знаходяться в окремому файлі, це помилка програмування, якщо ви не включите свій скрипт разом із поданням. Наявність його в окремому файлі відокремлює взаємодію з презентацією і дозволяє безліч інших переваг від неї знаходитись в окремому файлі.
дан Річардсон

1
Ви абсолютно праві. Я фактично повністю згоден і віддаю перевагу особисто цьому методу. Справжня проблема для мене полягає в тому, що мої колеги борються з такою сильною розлукою. Однак це проблема домену. Я вважаю, що цей метод є ідеальним, особливо коли ви беруть участь у процесі збирання JavaScript. Я продовжуватиму працювати над тим, щоб навчити своїх колег використовувати цей метод і повністю підтримувати його. Я думаю, що вашу відповідь можна було б покращити. Вам не потрібно було згадувати "20 людей згодні". Тільки тому, що відповідь популярна, не завжди означає, що вона правильна. У цьому випадку це правильно.
розчавити

Дуже правда, і я завжди радий прийняти конструктивні відгуки та змінити власний код та відповісти, якщо буде покращення, яке відбулося :)
Дан Річардсон,

1
Це рішення має додаткову перевагу в тому, що ви все ще можете виконувати всі MVC-речі, які, як ви очікували, зможете зробити в типовому представленні даних, наприклад, можливість JSON кодувати передане в моделі та генерувати URL-адреси за допомогою URL-адреси. Дія. Тоді такий підхід є елегантним способом налаштування контролерів AngularJS - кожен частковий вигляд може представляти окремий контролер у кутовому модулі. Так чисто!
Ден

40

З рішень у цій темі я придумав таке, ймовірно, надскладне рішення, яке дозволяє затягувати візуалізацію будь-якого HTML (і сценаріїв) всередині використовуючого блоку.

ВИКОРИСТАННЯ

Створіть "розділ"

  1. Типовий сценарій: У частковому перегляді введіть блок лише один раз, незалежно від того, скільки разів повторне часткове перегляд на сторінці:

    @using (Html.Delayed(isOnlyOne: "some unique name for this section")) {
        <script>
            someInlineScript();
        </script>
    }
    
  2. У частковому поданні включайте блок щоразу, коли частковий використовується:

    @using (Html.Delayed()) {
        <b>show me multiple times, @Model.Whatever</b>
    }
    
  3. У частковому поданні включіть блок лише один раз, незалежно від того, скільки разів повторність частки повторюється, але пізніше надайте її конкретно за назвою when-i-call-you:

    @using (Html.Delayed("when-i-call-you", isOnlyOne: "different unique name")) {
        <b>show me once by name</b>
        <span>@Model.First().Value</span>
    }
    

Відображення "розділів"

(тобто відображати розділ із затримкою у батьківському поданні)

@Html.RenderDelayed(); // writes unnamed sections (#1 and #2, excluding #3)
@Html.RenderDelayed("when-i-call-you", false); // writes the specified block, and ignore the `isOnlyOne` setting so we can dump it again
@Html.RenderDelayed("when-i-call-you"); // render the specified block by name
@Html.RenderDelayed("when-i-call-you"); // since it was "popped" in the last call, won't render anything due to `isOnlyOne` provided in `Html.Delayed`

КОД

public static class HtmlRenderExtensions {

    /// <summary>
    /// Delegate script/resource/etc injection until the end of the page
    /// <para>@via https://stackoverflow.com/a/14127332/1037948 and http://jadnb.wordpress.com/2011/02/16/rendering-scripts-from-partial-views-at-the-end-in-mvc/ </para>
    /// </summary>
    private class DelayedInjectionBlock : IDisposable {
        /// <summary>
        /// Unique internal storage key
        /// </summary>
        private const string CACHE_KEY = "DCCF8C78-2E36-4567-B0CF-FE052ACCE309"; // "DelayedInjectionBlocks";

        /// <summary>
        /// Internal storage identifier for remembering unique/isOnlyOne items
        /// </summary>
        private const string UNIQUE_IDENTIFIER_KEY = CACHE_KEY;

        /// <summary>
        /// What to use as internal storage identifier if no identifier provided (since we can't use null as key)
        /// </summary>
        private const string EMPTY_IDENTIFIER = "";

        /// <summary>
        /// Retrieve a context-aware list of cached output delegates from the given helper; uses the helper's context rather than singleton HttpContext.Current.Items
        /// </summary>
        /// <param name="helper">the helper from which we use the context</param>
        /// <param name="identifier">optional unique sub-identifier for a given injection block</param>
        /// <returns>list of delayed-execution callbacks to render internal content</returns>
        public static Queue<string> GetQueue(HtmlHelper helper, string identifier = null) {
            return _GetOrSet(helper, new Queue<string>(), identifier ?? EMPTY_IDENTIFIER);
        }

        /// <summary>
        /// Retrieve a context-aware list of cached output delegates from the given helper; uses the helper's context rather than singleton HttpContext.Current.Items
        /// </summary>
        /// <param name="helper">the helper from which we use the context</param>
        /// <param name="defaultValue">the default value to return if the cached item isn't found or isn't the expected type; can also be used to set with an arbitrary value</param>
        /// <param name="identifier">optional unique sub-identifier for a given injection block</param>
        /// <returns>list of delayed-execution callbacks to render internal content</returns>
        private static T _GetOrSet<T>(HtmlHelper helper, T defaultValue, string identifier = EMPTY_IDENTIFIER) where T : class {
            var storage = GetStorage(helper);

            // return the stored item, or set it if it does not exist
            return (T) (storage.ContainsKey(identifier) ? storage[identifier] : (storage[identifier] = defaultValue));
        }

        /// <summary>
        /// Get the storage, but if it doesn't exist or isn't the expected type, then create a new "bucket"
        /// </summary>
        /// <param name="helper"></param>
        /// <returns></returns>
        public static Dictionary<string, object> GetStorage(HtmlHelper helper) {
            var storage = helper.ViewContext.HttpContext.Items[CACHE_KEY] as Dictionary<string, object>;
            if (storage == null) helper.ViewContext.HttpContext.Items[CACHE_KEY] = (storage = new Dictionary<string, object>());
            return storage;
        }


        private readonly HtmlHelper helper;
        private readonly string identifier;
        private readonly string isOnlyOne;

        /// <summary>
        /// Create a new using block from the given helper (used for trapping appropriate context)
        /// </summary>
        /// <param name="helper">the helper from which we use the context</param>
        /// <param name="identifier">optional unique identifier to specify one or many injection blocks</param>
        /// <param name="isOnlyOne">extra identifier used to ensure that this item is only added once; if provided, content should only appear once in the page (i.e. only the first block called for this identifier is used)</param>
        public DelayedInjectionBlock(HtmlHelper helper, string identifier = null, string isOnlyOne = null) {
            this.helper = helper;

            // start a new writing context
            ((WebViewPage)this.helper.ViewDataContainer).OutputStack.Push(new StringWriter());

            this.identifier = identifier ?? EMPTY_IDENTIFIER;
            this.isOnlyOne = isOnlyOne;
        }

        /// <summary>
        /// Append the internal content to the context's cached list of output delegates
        /// </summary>
        public void Dispose() {
            // render the internal content of the injection block helper
            // make sure to pop from the stack rather than just render from the Writer
            // so it will remove it from regular rendering
            var content = ((WebViewPage)this.helper.ViewDataContainer).OutputStack;
            var renderedContent = content.Count == 0 ? string.Empty : content.Pop().ToString();
            // if we only want one, remove the existing
            var queue = GetQueue(this.helper, this.identifier);

            // get the index of the existing item from the alternate storage
            var existingIdentifiers = _GetOrSet(this.helper, new Dictionary<string, int>(), UNIQUE_IDENTIFIER_KEY);

            // only save the result if this isn't meant to be unique, or
            // if it's supposed to be unique and we haven't encountered this identifier before
            if( null == this.isOnlyOne || !existingIdentifiers.ContainsKey(this.isOnlyOne) ) {
                // remove the new writing context we created for this block
                // and save the output to the queue for later
                queue.Enqueue(renderedContent);

                // only remember this if supposed to
                if(null != this.isOnlyOne) existingIdentifiers[this.isOnlyOne] = queue.Count; // save the index, so we could remove it directly (if we want to use the last instance of the block rather than the first)
            }
        }
    }


    /// <summary>
    /// <para>Start a delayed-execution block of output -- this will be rendered/printed on the next call to <see cref="RenderDelayed"/>.</para>
    /// <para>
    /// <example>
    /// Print once in "default block" (usually rendered at end via <code>@Html.RenderDelayed()</code>).  Code:
    /// <code>
    /// @using (Html.Delayed()) {
    ///     <b>show at later</b>
    ///     <span>@Model.Name</span>
    ///     etc
    /// }
    /// </code>
    /// </example>
    /// </para>
    /// <para>
    /// <example>
    /// Print once (i.e. if within a looped partial), using identified block via <code>@Html.RenderDelayed("one-time")</code>.  Code:
    /// <code>
    /// @using (Html.Delayed("one-time", isOnlyOne: "one-time")) {
    ///     <b>show me once</b>
    ///     <span>@Model.First().Value</span>
    /// }
    /// </code>
    /// </example>
    /// </para>
    /// </summary>
    /// <param name="helper">the helper from which we use the context</param>
    /// <param name="injectionBlockId">optional unique identifier to specify one or many injection blocks</param>
    /// <param name="isOnlyOne">extra identifier used to ensure that this item is only added once; if provided, content should only appear once in the page (i.e. only the first block called for this identifier is used)</param>
    /// <returns>using block to wrap delayed output</returns>
    public static IDisposable Delayed(this HtmlHelper helper, string injectionBlockId = null, string isOnlyOne = null) {
        return new DelayedInjectionBlock(helper, injectionBlockId, isOnlyOne);
    }

    /// <summary>
    /// Render all queued output blocks injected via <see cref="Delayed"/>.
    /// <para>
    /// <example>
    /// Print all delayed blocks using default identifier (i.e. not provided)
    /// <code>
    /// @using (Html.Delayed()) {
    ///     <b>show me later</b>
    ///     <span>@Model.Name</span>
    ///     etc
    /// }
    /// </code>
    /// -- then later --
    /// <code>
    /// @using (Html.Delayed()) {
    ///     <b>more for later</b>
    ///     etc
    /// }
    /// </code>
    /// -- then later --
    /// <code>
    /// @Html.RenderDelayed() // will print both delayed blocks
    /// </code>
    /// </example>
    /// </para>
    /// <para>
    /// <example>
    /// Allow multiple repetitions of rendered blocks, using same <code>@Html.Delayed()...</code> as before.  Code:
    /// <code>
    /// @Html.RenderDelayed(removeAfterRendering: false); /* will print */
    /// @Html.RenderDelayed() /* will print again because not removed before */
    /// </code>
    /// </example>
    /// </para>

    /// </summary>
    /// <param name="helper">the helper from which we use the context</param>
    /// <param name="injectionBlockId">optional unique identifier to specify one or many injection blocks</param>
    /// <param name="removeAfterRendering">only render this once</param>
    /// <returns>rendered output content</returns>
    public static MvcHtmlString RenderDelayed(this HtmlHelper helper, string injectionBlockId = null, bool removeAfterRendering = true) {
        var stack = DelayedInjectionBlock.GetQueue(helper, injectionBlockId);

        if( removeAfterRendering ) {
            var sb = new StringBuilder(
#if DEBUG
                string.Format("<!-- delayed-block: {0} -->", injectionBlockId)
#endif
                );
            // .count faster than .any
            while (stack.Count > 0) {
                sb.AppendLine(stack.Dequeue());
            }
            return MvcHtmlString.Create(sb.ToString());
        } 

        return MvcHtmlString.Create(
#if DEBUG
                string.Format("<!-- delayed-block: {0} -->", injectionBlockId) + 
#endif
            string.Join(Environment.NewLine, stack));
    }


}

1
Нічого собі, мені навіть складно зрозуміти код, але +1 для приходу до рішення
Рамез Ахмед Саяд

@RameezAhmedSayad ти маєш рацію - повертаючись сюди, навіть мене бентежить те, як я мав намір сказати, як це використовувати. Оновлення відповіді ...
drzaus

І щоб уточнити далі - причина двох "імен" полягає в тому, що якщо ви хочете, щоб його було відображено лише один раз, коли йому потрібен унікальний ключ у параметрі isOnlyOne, але лише якщо ви хочете вивести його в певному місці за назвою, ви надаєте ідентифікатор, інакше його скидають на Html.RenderDelayed().
drzaus

Я особисто не думаю, що не потрібно буде купувати проблеми та використовувати цей підхід, розділ в часткових переглядах просто не потрібен, оскільки його можна усунути, і сценарії можуть туди переходити, не визначаючи розділ. Це тому, що це надано зовні, і якщо ви бачите код для візуалізованої сторінки, ви просто помітите, що код для часткового перегляду там не видно. Тож якщо це питання кращої організації тощо, це взагалі не матиме ніякого ефекту.
Трансцендентний

@Transcendent в «дискусії» вже почалася в коментарях загальноприйнятому відповідь stackoverflow.com/a/7556594/1037948
drzaus

16

У мене була ця проблема і я використовував цю техніку.

Його найкраще рішення, яке я знайшов, є дуже гнучким.

Також, будь ласка, проголосуйте тут, щоб додати підтримку декларації накопичувального розділу


9

Якщо у вас є законна потреба запустити деякі jsз атрибутів partial, ось, як це можна зробити, jQueryпотрібно:

<script type="text/javascript">        
    function scriptToExecute()
    {
        //The script you want to execute when page is ready.           
    }

    function runWhenReady()
    {
        if (window.$)
            scriptToExecute();                                   
        else
            setTimeout(runWhenReady, 100);
    }
    runWhenReady();
</script>

Я спробував @drzaus, йому потрібен 'SeeIfReady' або він не працює.
Cacho Santa

8

Дотримуючись ненав'язливого принципу, "_myPartial" вводити вміст безпосередньо в розділ сценаріїв не зовсім потрібно. Ви можете додати ці сценарії часткового перегляду в окремий .jsфайл і віднести їх до розділу @scripts з батьківського подання.


10
Що буде, якщо частковий вигляд зовсім не відображається на сторінці? Чи досі ми посилаємось на ці файли .js у батьківському режимі і робимо це перевантаженим?
Муралі Муругесан

5

Існує принциповий недолік у тому, як ми думаємо про Інтернет, особливо при використанні MVC. Недолік у тому, що JavaScript якимось чином несе відповідальність за перегляд. Перегляд - це перегляд, JavaScript (поведінковий чи інший) - це JavaScript. У схемі MVVM Silverlight та WPF ми стикаємося з "переглядом спочатку" або "спочатку моделлю". У MVC ми завжди повинні намагатися міркувати з позицій моделі, а JavaScript є частиною цієї моделі багатьма способами.

Я б запропонував використовувати шаблон AMD (мені подобається RequireJS ). Відокремте свій JavaScript в модулях, визначте свою функціональність і підключіть у свій HTML-код JavaScript, а не покладаючись на перегляд, щоб завантажити JavaScript. Це очистить ваш код, відокремить ваші занепокоєння та полегшить життя одним махом.


Приблизно два-три місяці або близько того, я використовую RequireJS, і я не думаю, що коли-небудь буду розробляти інший веб-додаток без RequireJS.
буксир

6
JavaScript може бути також відповідальним за перегляд.
Келмен

1
Використання шаблону AMD - хороша ідея, але я не згоден з вашим твердженням, що JavaScript є частиною моделі. Часто визначають поведінку перегляду, особливо в поєднанні з чимось на кшталт нокауту. Ви скидаєте JSON-представлення своєї моделі у свій JavaScript View. Особисто я просто використовую закриття, спеціальний "простір імен" на windowоб'єкті і включаю бібліотечні скрипти перед будь-якими сторонніми сторонами.
розчавити

Я думаю, тут є непорозуміння. Розробляючи більшість веб-додатків, ми фактично розробляємо два додатки: той, який працює на сервері і той, який працює на клієнті. З точки зору сервера, все, що ви надсилаєте в браузер, - це "перегляд". У цьому сенсі JavaScript є частиною перегляду. З точки зору клієнтського додатка, чистий HTML - це перегляд, а JS - код, що паралельний M і C в MVC-термінах сервера. Я думаю, саме тому люди тут не згодні.
TheAgent

3

Завданням ОП є те, що він хоче визначити вбудовані сценарії до свого часткового перегляду, і я вважаю, що цей сценарій є специфічним лише для цього часткового перегляду, і цей блок буде включений до його розділу сценаріїв.

Я розумію, що він хоче мати Частичний вигляд, щоб бути самодостатнім. Ідея схожа на компоненти при використанні Angular.

Мій спосіб полягав у тому, щоб просто зберегти сценарії всередині часткового перегляду таким, яким він є. Тепер проблема полягає в тому, що виклик Partial View, він може виконувати там скрипт перед усіма іншими сценаріями (що зазвичай додається внизу сторінки макета). У цьому випадку у вас просто сценарій часткового перегляду чекає інших сценаріїв. Існує кілька способів зробити це. Найпростіший, який я використовував раніше, - це використання події на body.

У моєму макеті я мав би щось на дні так:

// global scripts
<script src="js/jquery.min.js"></script>
// view scripts
@RenderSection("scripts", false)
// then finally trigger partial view scripts
<script>
  (function(){
    document.querySelector('body').dispatchEvent(new Event('scriptsLoaded'));
  })();
</script>

Потім на моєму частковому поданні (внизу):

<script>
  (function(){
    document.querySelector('body').addEventListener('scriptsLoaded', function() {

      // .. do your thing here

    });
  })();
</script>

Іншим рішенням є використання стека для виштовхування всіх ваших сценаріїв та виклик кожного в кінці. Іншим рішенням, як уже говорилося, є шаблон RequireJS / AMD, який також дуже добре працює.


2

Перше рішення, про яке я можу придумати - це використовувати ViewBag для зберігання значень, які повинні бути надані.

По-перше, я ніколи не пробував, якщо ця робота з часткового погляду, але вона повинна мати іммо.


Щойно спробував; на жаль, це не працює (створив ViewBag.RenderScripts = new List<string>();у верхній частині головної сторінки, потім зателефонував @Html.Partial("_CreateUpdatePartial",Model,ViewData), потім поставив @section Scripts {@foreach (string script in ViewBag.RenderScripts) Scripts.Render(script); }}. У частковий вигляд я поставив @{ViewBag.RenderScripts = ViewBag.RenderScripts ?? new List<string>();ViewBag.RenderScripts.Add("~/bundles/jquery");}.
JohnLBevan

2

Не потрібно використовувати розділи в частковому перегляді.

Включіть у свій частковий вигляд. Він виконує функцію після завантаження jQuery. Ви можете змінити умову для свого коду.

<script type="text/javascript">    
var time = setInterval(function () {
    if (window.jQuery != undefined) {
        window.clearInterval(time);

        //Begin
        $(document).ready(function () {
           //....
        });
        //End
    };
}, 10); </script>

Хуліо Шпадер


2

Ви можете використовувати ці методи розширення : (Зберегти як PartialWithScript.cs)

namespace System.Web.Mvc.Html
{
    public static class PartialWithScript
    {
        public static void RenderPartialWithScript(this HtmlHelper htmlHelper, string partialViewName)
        {
            if (htmlHelper.ViewBag.ScriptPartials == null)
            {
                htmlHelper.ViewBag.ScriptPartials = new List<string>();
            }

            if (!htmlHelper.ViewBag.ScriptPartials.Contains(partialViewName))
            {
                htmlHelper.ViewBag.ScriptPartials.Add(partialViewName);
            }

            htmlHelper.ViewBag.ScriptPartialHtml = true;
            htmlHelper.RenderPartial(partialViewName);
        }

        public static void RenderPartialScripts(this HtmlHelper htmlHelper)
        {
            if (htmlHelper.ViewBag.ScriptPartials != null)
            {
                htmlHelper.ViewBag.ScriptPartialHtml = false;
                foreach (string partial in htmlHelper.ViewBag.ScriptPartials)
                {
                    htmlHelper.RenderPartial(partial);
                }
            }
        }
    }
}

Використовуйте так:

Приклад частковий: (_MyPartial.cshtml) Помістіть html у if, а js - в іншому.

@if (ViewBag.ScriptPartialHtml ?? true)
    <p>I has htmls</p>
}
else {
    <script type="text/javascript">
        alert('I has javascripts');
    </script>
}

У своєму _Layout.cshtml або в іншому місці, де ви хочете виводити сценарії з партій, додайте наступне (один раз): Це відображатиме лише javascript усіх частин на поточній сторінці в цьому місці.

@{ Html.RenderPartialScripts(); }

Потім, щоб скористатися вашим частковим, просто зробіть це: він відображатиме лише html у цьому місці.

@{Html.RenderPartialWithScript("~/Views/MyController/_MyPartial.cshtml");}

1

Є спосіб вставити розділи в часткові види, хоча це не дуже. Потрібно мати доступ до двох змінних з батьківського подання. Оскільки частиною метою вашого часткового перегляду є створення цього розділу, є сенс вимагати цих змінних.

Ось як виглядає вставлення розділу в частковий вигляд:

@model KeyValuePair<WebPageBase, HtmlHelper>
@{
    Model.Key.DefineSection("SectionNameGoesHere", () =>
    {
        Model.Value.ViewContext.Writer.Write("Test");
    });
}

І на сторінці, що вставляє частковий вигляд ...

@Html.Partial(new KeyValuePair<WebPageBase, HtmlHelper>(this, Html))

Ви також можете використовувати цю техніку для програмного визначення вмісту розділу в будь-якому класі.

Насолоджуйтесь!


1
Можете, будь ласка, і посилання на повністю працюючий проект?
Ehsan Zargar Ershadi

1

Ідея Плутона приємніше:

CustomWebViewPage.cs:

    public abstract class CustomWebViewPage<TModel> : WebViewPage<TModel> {

    public IHtmlString PartialWithScripts(string partialViewName, object model) {
        return Html.Partial(partialViewName: partialViewName, model: model, viewData: new ViewDataDictionary { ["view"] = this, ["html"] = Html });
    }

    public void RenderScriptsInBasePage(HelperResult scripts) {
        var parentView = ViewBag.view as WebPageBase;
        var parentHtml = ViewBag.html as HtmlHelper;
        parentView.DefineSection("scripts", () => {
            parentHtml.ViewContext.Writer.Write(scripts.ToHtmlString());
        });
    }
}

Перегляди \ web.config:

<pages pageBaseType="Web.Helpers.CustomWebViewPage">

Вид:

@PartialWithScripts("_BackendSearchForm")

Часткове (_BackendSearchForm.cshtml):

@{ RenderScriptsInBasePage(scripts()); }

@helper scripts() {
<script>
    //code will be rendered in a "scripts" section of the Layout page
</script>
}

Сторінка макета:

@RenderSection("scripts", required: false)

1

Це працювало для мене, дозволяючи мені знаходити javascript та html для часткового перегляду в одному файлі. Допомагає з продуманим процесом бачити HTML і пов'язану з ним частину в тому самому файлі часткового перегляду.


У перегляді, який використовує частковий вигляд під назвою "_MyPartialView.cshtml"

<div>
    @Html.Partial("_MyPartialView",< model for partial view>,
            new ViewDataDictionary { { "Region", "HTMLSection" } } })
</div>

@section scripts{

    @Html.Partial("_MyPartialView",<model for partial view>, 
                  new ViewDataDictionary { { "Region", "ScriptSection" } })

 }

У файлі часткового перегляду

@model SomeType

@{
    var region = ViewData["Region"] as string;
}

@if (region == "HTMLSection")
{


}

@if (region == "ScriptSection")
{
        <script type="text/javascript">
    </script">
}

0

Я вирішив це зовсім іншим маршрутом (тому що я поспішав і не хотів впроваджувати новий HtmlHelper):

Я завернув свій частковий вигляд у велику іншу заяву:

@if ((bool)ViewData["ShouldRenderScripts"] == true){
// Scripts
}else{
// Html
}

Потім я два рази зателефонував за допомогою користувацького ViewData:

@Html.Partial("MyPartialView", Model, 
    new ViewDataDictionary { { "ShouldRenderScripts", false } })

@section scripts{
    @Html.Partial("MyPartialView", Model, 
        new ViewDataDictionary { { "ShouldRenderScripts", true } })
}

Безумовно, вся ідея полягає в тому, що споживачеві часткового перегляду не потрібно знати, що він повинен включати сценарії, це щось не так? Інакше ви можете просто сказати @Html.Partial("MyPartialViewScripts")
Дан Річардсон,

Ні, ідея полягає у тому, щоб дозволити визначення сценаріїв у тому ж документі, що і html, але я згоден, це не ідеально.
Rick Love

0

У мене була подібна проблема, де я мав головну сторінку наступним чином:

@section Scripts {
<script>
    $(document).ready(function () {
        ...
    });
</script>
}

...

@Html.Partial("_Charts", Model)

але частковий вигляд залежав від деякого JavaScript у розділі Сценарії. Я вирішив це, кодуючи частковий вигляд як JSON, завантаживши його в змінну JavaScript, а потім скориставшись цим для заповнення div, таким чином:

@{
    var partial = Html.Raw(Json.Encode(new { html = Html.Partial("_Charts", Model).ToString() }));
}

@section Scripts {
<script>
    $(document).ready(function () {
        ...
        var partial = @partial;
        $('#partial').html(partial.html);
    });
</script>
}

<div id="partial"></div>

IMO, ви повинні були це вирішити, перемістивши JS в окремий файл.
Варто 7

0

Ви можете вибрати ваш Folder / index.cshtml як головну сторінку, а потім додати сценарії розділів. Потім у своєму макеті ви маєте:

@RenderSection("scripts", required: false) 

і ваш index.cshtml:

@section scripts{
     @Scripts.Render("~/Scripts/file.js")
}

і це працюватиме над усіма вашими частковими переглядами. Це працює для мене


0

Використовуючи Mvc Core, ви можете створити акуратний TagHelper, scriptsяк показано нижче. Це може легко перетворитись у sectionтег, де ви також даєте йому ім'я (або ім'я взято з похідного типу). Зауважте, що потрібно встановити ін'єкцію залежності IHttpContextAccessor.

При додаванні сценаріїв (наприклад, частково)

<scripts>
    <script type="text/javascript">
        //anything here
    </script>
</scripts>

При виведенні сценаріїв (наприклад, у файл макета)

<scripts render="true"></scripts>

Код

public class ScriptsTagHelper : TagHelper
    {
        private static readonly object ITEMSKEY = new Object();

        private IDictionary<object, object> _items => _httpContextAccessor?.HttpContext?.Items;

        private IHttpContextAccessor _httpContextAccessor;

        public ScriptsTagHelper(IHttpContextAccessor httpContextAccessor)
        {
            _httpContextAccessor = httpContextAccessor;
        }

        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            var attribute = (TagHelperAttribute)null;
            context.AllAttributes.TryGetAttribute("render",out attribute);

            var render = false;

            if(attribute != null)
            {
                render = Convert.ToBoolean(attribute.Value.ToString());
            }

            if (render)
            {
                if (_items.ContainsKey(ITEMSKEY))
                {
                    var scripts = _items[ITEMSKEY] as List<HtmlString>;

                    var content = String.Concat(scripts);

                    output.Content.SetHtmlContent(content);
                }
            }
            else
            {
                List<HtmlString> list = null;

                if (!_items.ContainsKey(ITEMSKEY))
                {
                    list = new List<HtmlString>();
                    _items[ITEMSKEY] = list;
                }

                list = _items[ITEMSKEY] as List<HtmlString>;

                var content = await output.GetChildContentAsync();

                list.Add(new HtmlString(content.GetContent()));
            }
        }
    }

-1

Ну, мабуть, інші афіші надали вам спосіб безпосередньо включити @section у свій частковий (за допомогою сторонніх html-помічників).

Але я вважаю, що якщо ваш скрипт щільно пов'язаний з вашим частковим, просто покладіть свій JavaScript безпосередньо всередину вбудованого <script>тегу в рамках вашої часткової роботи і виконайте з ним (просто будьте обережні щодо дублювання сценарію, якщо ви збираєтесь використовувати часткове більше, ніж один раз в одному виді);


1
Зазвичай це не ідеально, тому що завантаження jQuery тощо відбуватиметься після вбудованих сценаріїв ... але для рідного коду, я думаю, це добре.
Варто7

-3

припустимо, у вас є частковий вигляд під назвою _contact.cshtml, ваш контакт може бути юридичним (ім'я) або фізичним суб'єктом (ім'я, прізвище). ваш погляд повинен дбати про те, що відображається, і що можна досягти за допомогою JavaScript. тому може знадобитися затримка візуалізації та внутрішній вигляд JS.

Єдиний спосіб, на який я думаю, як це можна пропустити, це коли ми створюємо ненав’язливий спосіб вирішення подібних проблем з інтерфейсом користувача.

також зауважте, що MVC 6 матиме так званий компонент View, навіть у MVC ф'ючерси були подібні речі, а Telerik також підтримує таке ...


1
На 3 роки пізніше, і я не думаю, що це взагалі відповідає на питання? Що ви тут намагаєтеся сказати? Відповідь на питання через 3 роки з умоглядними особливостями майбутніх технологій насправді не є відповіддю чи особливо корисною
Дан Річардсон

-3

Я щойно додав цей код до свого часткового перегляду і вирішив проблему, хоча це не дуже чисто, але він працює. Ви повинні переконатися, що ідентифікатори об'єктів, які ви надаєте.

<script>
    $(document).ready(function () {
        $("#Profile_ProfileID").selectmenu({ icons: { button: 'ui-icon-circle-arrow-s' } });
        $("#TitleID_FK").selectmenu({ icons: { button: 'ui-icon-circle-arrow-s' } });
        $("#CityID_FK").selectmenu({ icons: { button: 'ui-icon-circle-arrow-s' } });
        $("#GenderID_FK").selectmenu({ icons: { button: 'ui-icon-circle-arrow-s' } });
        $("#PackageID_FK").selectmenu({ icons: { button: 'ui-icon-circle-arrow-s' } });
    });
</script>

-5

У мене була аналогічна проблема, яку я вирішив із цим:

@section ***{
@RenderSection("****", required: false)
}

Це гарний спосіб ввести я здогадуюсь.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.