Razor View Engine을 사용하여 부분보기 ASP.NET MVC 3에서 특정 섹션으로 컨텐츠 주입


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 패키지가 있습니다 : nuget.org/packages/Forloop.HtmlHelpers
Russ Cam

@RussCam이 질문에 대답해야합니다. 너겟 패키지 +1은 OP가 겪고있는 정확한 문제를 해결합니다.
캐리 켄달

1
@RussCam NuGet 패키지는 솔루션이 아니며 패키지 코드 일 수 있습니다.
Maksim Vi.

8
@MaksimVi. 글쎄, 나는 nuget 패키지를 작성하고 그것을 제거 할 의도가 없으므로 코드 ( bitbucket.org/forloop/forloop-htmlhelpers/src ) 또는 wiki ( bitbucket.org/forloop/forloop-htmlhelpers/wiki )를 반복하는 대신 / Home )에서 주석으로 연결되는 링크는 IMO (stackoverflow)의 정신 내에 있습니다.
Russ Cam

아주 멋진 것처럼 보이는 또 다른 해결책이 있습니다 : stackoverflow.com/questions/5355427/…
jkokorian

답변:


235

섹션은 부분보기에서 작동하지 않으며 의도적으로 설계된 것입니다. 일부 사용자 지정 도우미 를 사용 하여 유사한 동작을 수행 할 수 있지만 정직하게는 부분 스크립트의 책임이 아니라 필요한 스크립트를 포함시키는 것이 뷰의 책임입니다. 메인 뷰의 @scripts 섹션을 사용하여 그렇게하고 부분에 대해서는 스크립트에 대해 걱정하지 않는 것이 좋습니다.


445
그러나 스크립트가 부분적으로 매우 구체적이라면 어떻게 될까요? 뷰가 아닌 부분적으로 정의되는 것이 논리적으로 이해되지 않습니까?
Jez

43
왜 의도적으로 설계된 것입니까?
Shimmy Weitzhandler 10

56
@Darin : 동의하지 않습니다. DRY 원리는 어떻습니까? 스크립트 참조 일지라도 자신을 반복하고 싶지 않습니다.
fretje

14
@fretje, 모든 사람은 주제에 대한 자신의 의견을 표현할 권리가 있습니다. 나는 당신을 존중합니다. 내 대답에서 나는 내 것을 표현 하고이 작업을 수행 할 수있는 대답과 연결되었습니다. 그러나 나는이 상황에서 내가 추천하고해야 할 일에 대해서도 강조했다.
Darin Dimitrov

33
두 번째 @JoshNoe와 나머지- "위젯"(디스플레이 + 리치 상호 작용)은 관련 자바 스크립트와 밀접하게 연결된 부분 뷰의 완벽한 예입니다. 설계 상 전체 기능을 얻기 위해 서로 다른 장소에 두 개의 include 문을 작성할 필요가 없습니다. 왜냐하면 디스플레이가 교환 원 상호 작용 없이는 절대 상호 작용이없고 다른 곳에서는 상호 작용이 나타나지 않기 때문입니다.
drzaus

83

이것은 매우 인기있는 질문이므로 솔루션을 게시하겠습니다.
나는 같은 문제가 있었지만 이상적이지는 않지만 실제로는 잘 작동하며 부분에 따라 뷰에 의존하지 않는다고 생각합니다.
내 시나리오는 작업 자체로 액세스 할 수 있지만 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 보기에는 페이지에 렌더링 될 자바 스크립트가 포함되어 있습니다.

이렇게하면 내 스크립트가 격리되어 필요한 경우 페이지에서 렌더링 될 수 있습니다. body_scripts , 면도기보기 엔진이 찾은 첫 번째 항목에서만 태그가 렌더링됩니다.

그것은 나에게 모든 것을 분리시킬 수있게한다.-그것은 나에게 아주 잘 작동하는 솔루션이고, 다른 사람들은 그것에 문제가있을 수 있지만, "디자인에 의한"구멍을 패치한다.


2
나는 당신에게 투표 할 사람이 아니었지만,이 솔루션은 여전히 ​​뷰 특정 스크립트를 뷰 자체와 분리하기 때문에이 솔루션이 마음에 들지 않는다고 말할 것입니다.
호감

3
다른 20 명과 나는 다른 의견을 가지고 있습니다. 별도의 파일에있는 뷰와 직접 관련된 스크립트를 계속 가질 수 있습니다. 뷰와 함께 스크립트를 포함하지 않으면 프로그래밍 오류가 발생합니다. 별도의 파일로 작성하면 프리젠 테이션과 상호 작용을 분리하고 별도의 파일로 제공함으로써 다른 많은 이점을 얻을 수 있습니다.
단 리처드슨

1
당신 말이 맞아요 나는 실제로이 방법을 개인적으로 완전히 동의하고 선호합니다. 저의 진짜 문제는 제 동료들이 이처럼 많은 분열에 어려움을 겪고 있다는 것입니다. 그러나 그것은 도메인 문제입니다. JavaScript 빌드 프로세스를 고려하면 특히이 방법이 이상적이라고 생각합니다. 저는이 방법을 사용하도록 동료를 교육하고 전적으로이를 지원하기 위해 계속 노력할 것입니다. 그래도 귀하의 답변이 향상 될 수 있다고 생각합니다. "20 명이 동의 함"을 언급 할 필요는 없습니다. 대답이 대중적이라고해서 항상 옳다는 것은 아닙니다. 이 경우에 맞습니다.
분쇄

매우 사실, 나는 항상 건설적인 피드백을 받아 :)가있을 수있는 개선이 있으면 내 자신의 코드와 답을 변경할 행복 해요
댄 리차드슨을

1
이 솔루션은 모델에서 전달 된 JSON을 인코딩하고 URL을 사용하여 URL을 생성하는 것과 같이 일반적인보기에서 수행 할 수있는 모든 MVC-ish 작업을 계속 수행 할 수 있다는 추가 이점을 제공합니다. 동작. 이 방법은 AngularJS 컨트롤러를 설정하는 우아한 방법입니다. 각 부분보기는 Angular 모듈에서 별도의 컨트롤러를 나타낼 수 있습니다. 너무 깨끗 해요!
Dan

40

이 스레드 의 솔루션에서 using 블록 내에서 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
와우 심지어 해결책을 주셔서 내가 코드를 이해하기 복잡하지만 하나되어
램 에즈 아메드 Sayad을

@RameezAhmedSayad 당신이 옳습니다. 여기서 다시 사용하는 방법에 대해 혼란스러워합니다. 답변 업데이트 중 ...
drzaus

그리고 더 명확하게 설명하기 위해 두 개의 "이름"이있는 이유는 매개 변수에 고유 한 키가 필요한 isOnlyOne경우에만 렌더링을 원하지만 이름으로 특정 위치에 렌더링하려는 경우에만 식별자를 제공하기 때문입니다. 그렇지 않으면에 덤프됩니다 Html.RenderDelayed().
drzaus

개인적으로 문제를 구매 하고이 접근법을 사용해야 할 필요가 없다고 생각합니다. 부분보기의 섹션은 제거 할 수 있기 때문에 필요하지 않으며 스크립트는 섹션을 정의하지 않고 갈 수 있습니다. 외부에서 렌더링되었으므로 렌더링 된 페이지의 코드가 표시되면 부분보기의 코드가 표시되지 않는 것입니다. 따라서 그것이 더 나은 조직 등의 문제라면 전혀 영향을 미치지 않을 것입니다.
초월

@Transcendent "토론"은 이미 승인 된 답변에 대한 의견에서 시작되었습니다 stackoverflow.com/a/7556594/1037948
drzaus

16

나는이 문제가 있었고이 기술을 사용 했다 .

내가 찾은 최고의 솔루션은 매우 유연합니다.

또한 누적 섹션 선언에 대한 지원을 추가하려면 여기투표 하십시오.


9

당신이 어떤 실행하는 합법적 인 필요성이있는 경우 jsA로부터를 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 파일을 부모로 참조하고 과부하합니까?
Murali Murugesan

5

특히 MVC를 사용할 때 웹에 대해 생각하는 방식에는 근본적인 결함이 있습니다. 결함은 JavaScript가 어떻게 든 뷰의 책임이라는 점입니다. 보기는보기이며 JavaScript (동작 또는 기타)는 JavaScript입니다. Silverlight와 WPF의 MVVM 패턴에서 우리는 "먼저보기"또는 "모델 우선"에 직면합니다. MVC에서는 항상 모델의 관점에서 추론을 시도해야하며 JavaScript는 여러 가지면에서이 모델의 일부입니다.

AMD 패턴을 사용하는 것이 좋습니다 ( RequireJS 좋아 합니다 ). JavaScript를 모듈로 분리하고, 기능을 정의하고, JavaScript를로드하기 위해 뷰에 의존하지 않고 JavaScript에서 HTML로 연결하십시오. 이렇게하면 코드를 정리하고 걱정을 분리하며 한 번에 모두 쉽게 생활 할 수 있습니다.


2 ~ 3 개월 정도 동안 RequireJS를 사용하고 있으며 RequireJS가 없으면 다른 웹 응용 프로그램을 개발할 것이라고 생각하지 않습니다.
tugberk

6
JavaScript도 View 책임이 될 수 있습니다.
Kelmen

1
AMD 패턴을 사용하는 것은 좋은 생각이지만 JavaScript가 모델의 일부라는 주장에 동의하지 않습니다. 특히 Knockout과 같은 경우 View 동작을 정의하는 경우가 많습니다. 모델의 JSON 표현을 JavaScript보기에 덤프합니다. 개인적으로 저는 클로저 (objects)에 커스텀 "네임 스페이스 (namespace)"를 사용하고 window부분 앞에 라이브러리 스크립트를 포함시킵니다.
호감

여기에 오해가 있다고 생각합니다. 대부분의 웹 응용 프로그램을 개발할 때 실제로 서버에서 실행되는 응용 프로그램과 클라이언트에서 실행되는 두 가지 응용 프로그램을 개발하고 있습니다. 서버의 관점에서 볼 때 브라우저로 보내는 것은 "보기"입니다. 그런 의미에서 JavaScript는보기의 일부입니다. 클라이언트 앱의 관점에서 순수한 HTML은 뷰이고 JS는 서버의 MVC 용어로 M과 C를 병렬로하는 코드입니다. 나는 이것이 사람들이 여기에 동의하지 않는 이유라고 생각합니다.
TheAgent

3

OP의 목표는 인라인 스크립트를 Partial View에 정의하려는 것입니다.이 스크립트는 Partial View에만 해당되며 해당 블록이 스크립트 섹션에 포함되어 있다고 가정합니다.

나는 그가 부분 뷰를 자립하기를 원한다는 것을 알았습니다. 아이디어는 Angular를 사용할 때 구성 요소와 유사합니다.

내 방식은 스크립트를 Partial View 내부에 그대로 유지하는 것입니다. 이제 문제는 부분보기를 호출 할 때 다른 모든 스크립트보다 먼저 스크립트를 실행할 수 있습니다 (일반적으로 레이아웃 페이지의 맨 아래에 추가됨). 이 경우 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를 사용하여 렌더링 해야하는 값을 저장하는 것입니다.

솔직히 나는 이것이 부분적인 관점에서 작동한다면 시도하지 않았지만 그것은 imo이어야합니다.


그냥 시도했다; 슬프게도이없는 작업 (생성 된 않는 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);
                }
            }
        }
    }
}

다음과 같이 사용하십시오.

partial 예 : (_MyPartial.cshtml) html을 if에 넣고 js를 else에 넣으십시오.

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

_Layout.cshtml 또는 부분의 스크립트를 렌더링하려는 위치에 다음을 한 번 입력하십시오. 현재 위치의 모든 부분에 대한 자바 스크립트 만이 위치에 렌더링합니다.

@{ 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());
        });
    }
}

Views \ 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"이라는 부분 뷰를 사용하는 View에서

<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-else 문으로 감쌌습니다.

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

그런 다음 사용자 정의 ViewData로 Partial을 두 번 호출했습니다.

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

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

확실하게 전체 아이디어는 부분 뷰의 소비자가 스크립트를 포함해야한다는 것을 알 필요가 없다는 것입니다. 그렇지 않으면 당신은 잘 말할 수도 있습니다 @Html.Partial("MyPartialViewScripts")
dan richardson

아니오, 아이디어는 스크립트가 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를 별도의 파일로 이동하여이 문제를 해결해야합니다.
Worthy7

0

선택적으로 Folder / index.cshtml을 마스터 페이지로 사용한 다음 섹션 스크립트를 추가 할 수 있습니다. 그런 다음 레이아웃에 다음이 있습니다.

@RenderSection("scripts", required: false) 

그리고 index.cshtml :

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

그리고 그것은 당신의 모든 부분적 견해에 대해 작동 할 것입니다. 그것은 나를 위해 작동


0

Mvc Core를 사용하면 scripts아래와 같이 깔끔한 TagHelper 를 만들 수 있습니다. 이 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

글쎄, 다른 포스터는 부분에 @ 섹션을 직접 포함시킬 수있는 수단을 제공했다고 생각합니다 (제 3 자 HTML 도우미 사용).

그러나 스크립트가 부분적으로 밀접하게 결합되어 있으면 자바 스크립트를<script> 부분 내부의 인라인 태그 안에 직접 넣고 처리하십시오 (부분을 두 번 이상 사용하려는 경우 스크립트 복제에주의하십시오) 단일 관점에서);


1
jQuery 등의 로딩은 인라인 스크립트 다음에 발생하기 때문에 일반적으로 이상적이지 않지만 ... 기본 코드의 경우 문제가 없습니다.
Worthy7

-3

_contact.cshtml이라는 부분보기가 있다고 가정하면 연락처는 법적 (이름) 또는 실제 주제 (이름, 성) 일 수 있습니다. 당신의 견해는 렌더링 된 것에주의를 기울여야하며 자바 스크립트로 해결할 수 있습니다. 따라서 렌더링 지연과 JS 내부 뷰가 필요할 수 있습니다.

내가 생각할 수있는 유일한 방법, 어떻게 생략 할 수 있는지는 UI 문제를 다루는 눈에 거슬리지 않는 방법을 만들 때입니다.

또한 MVC 6에는 소위 View Component가 있으며 MVC 선물에도 비슷한 것들이 있었고 Telerik도 그러한 것을 지원합니다 ...


1
3 년 늦었지만 이것이 질문에 전혀 대답하지 않는다고 생각합니까? 여기서 뭐라고하세요? 의 투기 적 특징로 3 년 후 질문에 대답 미래 기술 정말 대답 또는 특히 도움이되지 않습니다
단 리처드슨

-3

방금 부분 뷰 에이 코드를 추가하고 문제를 해결했지만 매우 깨끗하지는 않지만 작동합니다. 렌더링 할 오브젝트의 ID를 확인해야합니다.

<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)
}

그건 내가 추측 추측 예쁜 방법입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.