Tiêm nội dung vào các phần cụ thể từ chế độ xem một phần ASP.NET MVC 3 với Công cụ xem dao cạo


324

Tôi có phần này được xác định trong _Layout.cshtml

@RenderSection("Scripts", false)

Tôi có thể dễ dàng sử dụng nó từ một khung nhìn:

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

Điều tôi đang vật lộn là làm thế nào để đưa một số nội dung vào bên trong phần này từ chế độ xem một phần.

Giả sử đây là trang xem của tôi:

@section Scripts { 

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

<div>
    poo bar poo
</div>

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

Tôi cần phải tiêm một số nội dung bên trong Scriptsphần từ _myPartialgóc nhìn một phần.

Tôi có thể làm cái này như thế nào?


17
cho bất cứ ai đến sau này - có gói nuget
Russ Cam

@RussCam bạn nên trả lời câu hỏi này. +1 gói nuget giải quyết vấn đề chính xác mà OP đang gặp phải.
Carrie Kendall

1
@RussCam Gói NuGet không phải là một giải pháp, mã của gói có thể là.
Maksim Vi.

8
@MaksimVi. tốt, tôi đã viết gói nuget và không có ý định gỡ nó xuống, thay vì lặp lại mã ( bitbucket.org/forloop/forloop-htmlhelpers/src ) hoặc wiki ( bitbucket.org/forloop/forloop-htmlhelpers/wiki / Trang chủ ) ở đây, một liên kết đến nó như một bình luận được giữ trong tinh thần của stackoverflow, IMO.
Nga Cam

Đây là một giải pháp khác có vẻ rất hay: stackoverflow.com/questions/5355427/
trộm

Câu trả lời:


235

Các phần không hoạt động trong chế độ xem một phần và đó là do thiết kế. Bạn có thể sử dụng một số trình trợ giúp tùy chỉnh để đạt được hành vi tương tự, nhưng thành thật mà nói, trách nhiệm của nó là bao gồm các tập lệnh cần thiết, không phải là trách nhiệm của một phần. Tôi sẽ khuyên bạn nên sử dụng phần @scripts của chế độ xem chính để làm điều đó và không phải lo lắng về các tập lệnh.


445
Nhưng nếu kịch bản rất cụ thể cho một phần thì sao? Nó không có ý nghĩa logic cho nó được xác định trong một phần, và không phải là chế độ xem?
Jez

43
Tại sao nó là do thiết kế?
Shimmy Weitzhandler

56
@Darin: Tôi không đồng ý. Còn nguyên tắc DRY thì sao? Tôi không muốn lặp lại chính mình, ngay cả khi đó chỉ là tài liệu tham khảo kịch bản.
Fretje

14
@fretje, mọi người đều có quyền bày tỏ ý kiến ​​của mình về chủ đề này. Tôi tôn trọng bạn. Trong câu trả lời của tôi, tôi đã bày tỏ và liên kết với một câu trả lời cho phép bạn đạt được nhiệm vụ này. Nhưng tôi cũng đã nhấn mạnh về những gì tôi muốn giới thiệu và làm cho tình huống này.
Darin Dimitrov

33
biệt phái @JoshNoe và phần còn lại - một "widget" (hiển thị + tương tác phong phú) là một ví dụ hoàn hảo về chế độ xem một phần được kết hợp chặt chẽ với javascript được liên kết. Theo thiết kế, tôi không nên viết hai câu lệnh bao gồm ở những nơi khác nhau để có được chức năng đầy đủ, bởi vì màn hình sẽ không bao giờ thiếu sự tương tác của người tham dự và tương tác sẽ không bao giờ xuất hiện ở nơi khác.
drzaus

83

Đây là một câu hỏi khá phổ biến, vì vậy tôi sẽ đăng giải pháp của mình lên.
Tôi có cùng một vấn đề và mặc dù nó không lý tưởng, tôi nghĩ nó thực sự hoạt động khá tốt và không phụ thuộc một phần vào quan điểm.
Kịch bản của tôi là một hành động có thể tự truy cập nhưng cũng có thể được nhúng vào chế độ xem aa - bản đồ google.

Trong tôi _layoutcó:

@RenderSection("body_scripts", false)

Theo indexquan điểm của tôi, tôi có:

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

Theo clientsquan điểm của tôi, tôi có (tất cả bản đồ và PGS.):

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

Của tôi Clients_ScriptsChế độ xem chứa javascript được hiển thị trên trang

Bằng cách này, tập lệnh của tôi được tách biệt và có thể được hiển thị vào trang khi cần, với body_scripts thẻ chỉ được hiển thị trong lần xuất hiện đầu tiên mà công cụ xem dao cạo tìm thấy nó.

Điều đó cho phép tôi tách biệt mọi thứ - đó là một giải pháp hoạt động khá tốt đối với tôi, những giải pháp khác có thể có vấn đề với nó, nhưng nó vá lỗ hổng "theo thiết kế".


2
Tôi không phải là người bỏ phiếu cho bạn, nhưng tôi sẽ nói rằng tôi không thực sự thích giải pháp này vì nó vẫn tách biệt các kịch bản xem cụ thể khỏi chế độ xem.
nghiền nát

3
20 người khác và tôi có ý kiến ​​khác. Bạn vẫn có thể có các tập lệnh liên quan trực tiếp đến một chế độ xem trong một tệp riêng biệt, đó là lỗi lập trình nếu bạn không bao gồm tập lệnh của mình cùng với chế độ xem. Có nó trong một tệp riêng biệt tách biệt sự tương tác với bản trình bày và cho phép rất nhiều lợi ích khác từ nó trong một tệp riêng biệt.
dan richardson

1
Bạn hoàn toàn đúng. Tôi thực sự hoàn toàn đồng ý và thích phương pháp này cá nhân. Vấn đề thực sự đối với tôi là các đồng nghiệp của tôi phải vật lộn với sự tách biệt nhiều này. Đó là một vấn đề tên miền, mặc dù. Tôi nghĩ phương pháp này là lý tưởng, đặc biệt là khi bạn tham gia vào quá trình xây dựng JavaScript. Tôi sẽ tiếp tục làm việc để giáo dục các đồng nghiệp của mình sử dụng phương pháp này và hoàn toàn ủng hộ nó. Tôi nghĩ rằng câu trả lời của bạn có thể được cải thiện, mặc dù. Bạn không cần phải đề cập đến "20 người đồng ý". Chỉ vì một câu trả lời là phổ biến, không phải lúc nào cũng có nghĩa là nó đúng. Trong trường hợp này là đúng.
nghiền nát

Rất đúng, và tôi luôn vui vẻ chấp nhận phản hồi mang tính xây dựng và thay đổi mã và câu trả lời của riêng tôi nếu có một cải tiến cần có :)
dan richardson

1
Giải pháp này có thêm lợi ích là vẫn có thể thực hiện tất cả các công cụ MVC mà bạn mong đợi có thể thực hiện trong Chế độ xem thông thường, chẳng hạn như có thể mã hóa JSON được truyền trong Mô hình và tạo URL bằng Url. Hoạt động. Cách tiếp cận này sau đó là một cách thanh lịch để thiết lập bộ điều khiển AngularJS của bạn - mỗi chế độ xem một phần có thể đại diện cho một bộ điều khiển riêng trong mô-đun Angular. Thật sạch sẽ!
Dan

40

Từ các giải pháp trong luồng này , tôi đã đưa ra giải pháp có thể quá phức tạp sau đây cho phép bạn trì hoãn hiển thị bất kỳ html (tập lệnh nào) trong một khối sử dụng.

SỬ DỤNG

Tạo "phần"

  1. Kịch bản điển hình: Trong chế độ xem một phần, chỉ bao gồm khối một lần cho dù số lần xem một phần được lặp lại trong trang bao nhiêu lần:

    @using (Html.Delayed(isOnlyOne: "some unique name for this section")) {
        <script>
            someInlineScript();
        </script>
    }
  2. Trong chế độ xem một phần, bao gồm khối cho mỗi lần sử dụng một phần:

    @using (Html.Delayed()) {
        <b>show me multiple times, @Model.Whatever</b>
    }
  3. Trong chế độ xem một phần, chỉ bao gồm khối một lần cho dù phần đó được lặp lại bao nhiêu lần, nhưng sau đó hiển thị cụ thể theo tên 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>
    }

Kết xuất "phần"

(tức là hiển thị phần bị trì hoãn trong chế độ xem chính)

@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
Wow thậm chí rất phức tạp đối với tôi để hiểu mã, nhưng +1 để đưa ra giải pháp
Rameez Ahmed Sayad

@RameezAhmedSayad bạn nói đúng - quay lại đây ngay cả khi tôi bối rối không biết tôi muốn nói như thế nào để sử dụng nó. Cập nhật câu trả lời ...
drzaus 14/08/2015

Và để làm rõ hơn - lý do có hai "tên" là nếu bạn chỉ muốn nó được hiển thị một khi nó cần khóa duy nhất trong tham số isOnlyOne, nhưng chỉ khi bạn muốn hiển thị nó tại một vị trí cụ thể theo tên thì bạn mới cung cấp mã định danh, nếu không nó bị đổ tại Html.RenderDelayed().
drzaus

Cá nhân tôi không nghĩ rằng sẽ có nhu cầu mua rắc rối và sử dụng phương pháp này, phần trong chế độ xem một phần đơn giản là không cần thiết vì nó có thể được loại bỏ và các tập lệnh có thể đi đến đó mà không cần xác định một phần. Đó là bởi vì đó được hiển thị bên ngoài và nếu bạn thấy mã cho trang được hiển thị, bạn chỉ cần chú ý mã cho chế độ xem một phần không hiển thị trong đó. Vì vậy, nếu đó là vấn đề của tổ chức tốt hơn, v.v., điều đó sẽ không có bất kỳ ảnh hưởng nào cả.
Siêu việt

@Transcendent "cuộc tranh luận" đã được bắt đầu trong các nhận xét về câu trả lời được chấp nhận stackoverflow.com/a/7556594/1037948
drzaus

16

Tôi đã có vấn đề này và sử dụng kỹ thuật này .

Đó là giải pháp tốt nhất tôi tìm thấy rất linh hoạt.

Ngoài ra, vui lòng bỏ phiếu tại đây để thêm hỗ trợ cho khai báo phần tích lũy


9

Nếu bạn có nhu cầu chính đáng để chạy một số jstừ partial, đây là cách bạn có thể làm điều đó, jQuerylà bắt buộc:

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

Tôi đã thử @drzaus, nó cần 'See IfReady' hoặc nó không hoạt động.
Cacho Santa

8

Theo nguyên tắc không phô trương , "_myPartial" không cần thiết phải đưa nội dung trực tiếp vào phần tập lệnh. Bạn có thể thêm các tập lệnh xem một phần vào .jstệp riêng biệt và tham chiếu chúng vào phần @scripts từ chế độ xem chính.


10
Điều gì sẽ xảy ra nếu chế độ xem một phần hoàn toàn không được hiển thị trong trang? Chúng ta vẫn tham chiếu các tệp .js đó trong cha mẹ và làm cho nó quá tải chứ?
Murali Murugesan

5

Có một lỗ hổng cơ bản trong cách chúng ta nghĩ về web, đặc biệt là khi sử dụng MVC. Lỗ hổng là JavaScript bằng cách nào đó là trách nhiệm của quan điểm. Một khung nhìn là một khung nhìn, JavaScript (hành vi hoặc cách khác) là JavaScript. Trong mẫu MVVM của Silverlight và WPF, chúng tôi phải đối mặt với "xem trước" hoặc "mô hình trước". Trong MVC, chúng ta phải luôn cố gắng suy luận theo quan điểm của mô hình và JavaScript là một phần của mô hình này theo nhiều cách.

Tôi sẽ đề nghị sử dụng mẫu AMD (bản thân tôi thích RequireJS ). Sắp xếp JavaScript của bạn trong các mô-đun, xác định chức năng của bạn và nối vào html của bạn từ JavaScript thay vì dựa vào chế độ xem để tải JavaScript. Điều này sẽ làm sạch mã của bạn, tách biệt mối quan tâm của bạn và làm cho cuộc sống dễ dàng hơn trong một cú trượt ngã.


Trong khoảng hai hoặc ba tháng hoặc lâu hơn, tôi đang sử dụng RequireJS và tôi không nghĩ mình sẽ phát triển một ứng dụng web khác mà không có RequireJS.
kéo co

6
JavaScript cũng có thể là trách nhiệm của View.
Kelmen

1
Sử dụng mẫu AMD là một ý tưởng hay, nhưng tôi không đồng ý với khẳng định của bạn rằng JavaScript là một phần của mô hình. Nó thường được xác định hành vi Xem, đặc biệt là khi được kết hợp với một cái gì đó như Knockout. Bạn kết xuất một đại diện JSON của mô hình của bạn vào Chế độ xem JavaScript của bạn. Cá nhân, tôi chỉ sử dụng các bao đóng, một "không gian tên" tùy chỉnh trên windowđối tượng và bao gồm các tập lệnh thư viện trước bất kỳ phần nào.
đè bẹp

Tôi nghĩ rằng có một sự hiểu lầm ở đây. Khi phát triển hầu hết các ứng dụng web, chúng tôi thực sự đang phát triển hai ứng dụng: một ứng dụng chạy trên máy chủ và một ứng dụng chạy trên máy khách. Từ quan điểm của máy chủ, bất cứ điều gì bạn gửi xuống trình duyệt là "chế độ xem". Theo nghĩa đó, JavaScript là một phần của khung nhìn. Từ quan điểm của ứng dụng khách, HTML thuần túy là chế độ xem và JS là mã tương đương với M và C theo thuật ngữ MVC của máy chủ. Tôi nghĩ đây là lý do tại sao mọi người không đồng ý ở đây.
TheAgent

3

Mục tiêu của OP là anh ấy muốn xác định các tập lệnh nội tuyến vào Chế độ xem một phần của mình, mà tôi cho rằng tập lệnh này chỉ dành riêng cho Chế độ xem một phần đó và đưa khối đó vào phần tập lệnh của anh ấy.

Tôi hiểu rằng anh ta muốn có Chế độ xem một phần để được khép kín. Ý tưởng tương tự như các thành phần khi sử dụng Angular.

Cách của tôi là chỉ giữ các tập lệnh bên trong Chế độ xem một phần. Bây giờ vấn đề với điều đó là khi gọi Chế độ xem một phần, nó có thể thực thi tập lệnh trong đó trước tất cả các tập lệnh khác (thường được thêm vào dưới cùng của trang bố cục). Trong trường hợp đó, bạn chỉ cần có tập lệnh Chế độ xem một phần chờ các tập lệnh khác. Có nhiều hướng khác nhau để làm điều đó. Cách đơn giản nhất mà tôi đã sử dụng trước đây là sử dụng một sự kiện trên body.

Về cách bố trí của tôi, tôi sẽ có một cái gì đó ở phía dưới như thế này:

// 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>

Sau đó, trên Chế độ xem một phần của tôi (ở dưới cùng):

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

      // .. do your thing here

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

Một giải pháp khác là sử dụng một ngăn xếp để đẩy tất cả các tập lệnh của bạn và gọi từng tập lệnh ở cuối. Một giải pháp khác, như đã đề cập, là mẫu RequireJS / AMD, cũng hoạt động rất tốt.


2

Giải pháp đầu tiên tôi có thể nghĩ đến là sử dụng ViewBag để lưu trữ các giá trị phải được hiển thị.

Trên thực tế, tôi không bao giờ thử nếu công việc này từ một góc nhìn một phần, nhưng nó nên imo.


Vừa thử; buồn bã mà không làm việc (tạo một ViewBag.RenderScripts = new List<string>();ở phía trên cùng của trang chính, sau đó được gọi @Html.Partial("_CreateUpdatePartial",Model,ViewData), sau đó đặt @section Scripts {@foreach (string script in ViewBag.RenderScripts) Scripts.Render(script); }}Theo quan điểm của một phần tôi đặt. @{ViewBag.RenderScripts = ViewBag.RenderScripts ?? new List<string>();ViewBag.RenderScripts.Add("~/bundles/jquery");}.
JohnLBevan

2

Bạn không cần sử dụng các phần trong chế độ xem một phần.

Bao gồm trong Chế độ xem một phần của bạn. Nó thực thi chức năng sau khi tải jQuery. Bạn có thể thay đổi mệnh đề điều kiện de cho mã của bạn.

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

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

Julio Spader


2

Bạn có thể sử dụng các Phương thức tiện ích mở rộng này : (Lưu dưới dạng 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);
                }
            }
        }
    }
}

Sử dụng như thế này:

Ví dụ một phần: (_MyPartial.cshtml) Đặt html vào if và js trong phần khác.

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

Trong _Layout.cshtml của bạn hoặc bất cứ nơi nào bạn muốn các tập lệnh từ các phần được bật được hiển thị, hãy đặt như sau (một lần): Nó sẽ chỉ hiển thị javascript của tất cả các phần trên trang hiện tại ở vị trí này.

@{ Html.RenderPartialScripts(); }

Sau đó, để sử dụng một phần của bạn, chỉ cần làm điều này: Nó sẽ chỉ hiển thị html tại vị trí này.

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

1

Có một cách để chèn các phần trong chế độ xem một phần, mặc dù nó không đẹp. Bạn cần có quyền truy cập vào hai biến từ Chế độ xem cha. Vì một phần của mục đích xem một phần của bạn là tạo ra phần đó, nên yêu cầu các biến này là hợp lý.

Đây là những gì nó trông giống như để chèn một phần trong chế độ xem một phần:

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

Và trong trang chèn chế độ xem một phần ...

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

Bạn cũng có thể sử dụng kỹ thuật này để xác định nội dung của một phần theo chương trình trong bất kỳ lớp nào.

Thưởng thức!


1
Bạn có thể vui lòng và một liên kết đến một dự án làm việc đầy đủ?
Ehsan Zargar Ershadi

1

Ý tưởng của sao Diêm Vương theo cách đẹp hơn:

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

Lượt xem \ web.config:

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

Lượt xem:

@PartialWithScripts("_BackendSearchForm")

Một phần (_BackendSearchForm.cshtml):

@{ RenderScriptsInBasePage(scripts()); }

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

Trang bố trí:

@RenderSection("scripts", required: false)

1

Điều này làm việc cho tôi cho phép tôi đồng định vị trí javascript và html để xem một phần trong cùng một tệp. Giúp với quá trình suy nghĩ để xem html và phần liên quan trong cùng một tệp xem một phần.


Trong Chế độ xem sử dụng Chế độ xem một phần được gọi là "_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" } })

 }

Trong tập tin Xem một phần

@model SomeType

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

@if (region == "HTMLSection")
{


}

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

0

Tôi đã giải quyết vấn đề này theo một lộ trình hoàn toàn khác (vì tôi đang vội và không muốn triển khai HtmlHelper mới):

Tôi đã bao bọc Chế độ xem một phần của mình trong một tuyên bố nếu khác:

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

Sau đó, tôi đã gọi Partial hai lần với ViewData tùy chỉnh:

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

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

Chắc chắn toàn bộ ý tưởng là người tiêu dùng của chế độ xem một phần không cần phải biết rằng nó phải bao gồm các tập lệnh, đó có phải là vấn đề không? Nếu không, bạn cũng có thể nói @Html.Partial("MyPartialViewScripts")
dan richardson

Không, ý tưởng là cho phép các tập lệnh được xác định trong cùng một tài liệu với html, nhưng tôi đồng ý rằng điều này không lý tưởng.
Rick Love

0

Tôi đã có một vấn đề tương tự, nơi tôi có một trang chủ như sau:

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

...

@Html.Partial("_Charts", Model)

nhưng chế độ xem một phần phụ thuộc vào một số JavaScript trong phần Tập lệnh. Tôi đã giải quyết nó bằng cách mã hóa chế độ xem một phần dưới dạng JSON, tải nó vào một biến JavaScript và sau đó sử dụng hàm này để điền vào div, vì vậy:

@{
    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 bạn nên giải quyết điều này bằng cách chuyển JS của bạn thành một tệp riêng.
Worthy7

0

một cách chọn lọc, bạn có thể sử dụng Thư mục / index.cshtml của bạn làm trang chủ sau đó thêm tập lệnh phần. Sau đó, trong bố trí của bạn, bạn có:

@RenderSection("scripts", required: false) 

và index.cshtml của bạn:

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

và nó sẽ làm việc trên tất cả các phần xem xét của bạn. Nó làm việc cho tôi


0

Sử dụng Mvc Core, bạn có thể tạo TagHelper gọn gàng scriptsnhư bên dưới. Điều này có thể dễ dàng được đưa vào một sectionthẻ nơi bạn cũng đặt tên cho nó (hoặc tên được lấy từ loại dẫn xuất). Lưu ý rằng tiêm phụ thuộc cần phải được thiết lập cho IHttpContextAccessor.

Khi thêm tập lệnh (ví dụ: một phần)

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

Khi xuất các tập lệnh (ví dụ: trong tệp bố cục)

<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

Chà, tôi đoán rằng các áp phích khác đã cung cấp cho bạn một phương tiện để bao gồm trực tiếp @section trong phần của bạn (bằng cách sử dụng trình trợ giúp html của bên thứ 3).

Nhưng, tôi cho rằng, nếu tập lệnh của bạn được liên kết chặt chẽ với một phần của bạn, chỉ cần đặt javascript của bạn trực tiếp vào một <script>thẻ nội tuyến trong một phần của bạn và được thực hiện với nó (chỉ cần cẩn thận sao chép tập lệnh nếu bạn có ý định sử dụng một phần nhiều lần trong một cái nhìn duy nhất);


1
Điều này thường không lý tưởng vì việc tải jQuery, v.v. sẽ xảy ra sau các tập lệnh nội tuyến ... nhưng đối với mã gốc tôi đoán nó vẫn ổn.
Worthy7

-3

giả sử bạn có chế độ xem một phần được gọi là _contact.cshtml, liên hệ của bạn có thể là hợp pháp (tên) hoặc chủ đề vật lý (tên, họ). Chế độ xem của bạn nên quan tâm đến những gì được hiển thị và có thể đạt được bằng javascript. do đó, kết xuất bị trì hoãn và JS bên trong xem có thể cần thiết.

cách duy nhất tôi nghĩ, làm thế nào nó có thể được thông báo, là khi chúng ta tạo ra một cách xử lý không phô trương để xử lý các mối quan tâm UI như vậy.

cũng lưu ý rằng MVC 6 sẽ có cái gọi là Thành phần View, thậm chí tương lai MVC cũng có một số thứ tương tự và Telerik cũng hỗ trợ một thứ như vậy ...


1
3 năm trễ, và tôi không nghĩ điều này thậm chí trả lời câu hỏi nào cả? Bạn đang cố gắng nói điều gì? Trả lời một câu hỏi 3 năm sau với các tính năng đầu cơ của các công nghệ trong tương lai không thực sự là một câu trả lời hoặc đặc biệt hữu ích
dan richardson

-3

Tôi vừa thêm mã này vào chế độ xem một phần của mình và giải quyết vấn đề, mặc dù không sạch lắm, nhưng nó hoạt động. Bạn phải đảm bảo Id của các đối tượng bạn đang hiển thị.

<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

Tôi đã có vấn đề tương tự giải quyết nó với điều này:

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

Đó là một cách khá hay để tiêm.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.