MVC 3, повторное использование частичных представлений и jquery, не конфликтуя с DOM - PullRequest
11 голосов
/ 06 августа 2011

Поскольку я все еще новичок в MVC 3 и jquery, я хотел бы узнать лучшее решение для решения следующих проблем:

У меня есть представление, где я использую jquery ajax для извлечения и отображения частичного представления с некоторыми сведениями о продукте для продукта A. Загруженное частичное представление состоит из набора html и кода jquery, который связан с определенными идентификаторами частичный вид.

Таким образом, я хотел бы повторно использовать то же частичное представление, чтобы показать детали из других продуктов в том же представлении (например, показать детали продукта B во всплывающем диалоговом окне). Всякий раз, когда отображается всплывающее окно, вновь полученное частичное представление будет конфликтовать с частичным представлением для продукта A, так как те же идентификаторы используются в html.

Conceptual overview of the case

Есть ли способ инкапсулировать html и javascript в частичное представление и повторно использовать его на нескольких страницах, не беспокоясь о конфликтах с идентификаторами и прочим?

Надеюсь, мой вопрос имеет смысл. Спасибо,

/ Нима

ОБНОВЛЕНО

Вот некоторый псевдокод, обрисовывающий мою проблему:

VIEW

<script type="text/javascript">
$(document).ready(function () {

    $('.productItems').click(function () {
        var input = { productId: $(this).attr('data-productID') };
        var url = url = '<%: Url.Content("~/ProductDetails/ShowProductDetails") %>';


        // Show the modal box with product details
        $('#dialogBox').dialog({
            title: $(this).attr('data-productTitle')
        });


        // Fetch content in the background
        $.get(url, input, function (result, response) {
            $('#dialogBox').html(result);
            });
    });
});
</script>


<div id="detailsArea">
    <% Html.RenderPartial("ProductDetails", Model.Product); %>
</div>

<div id="productLinks">
  <span class="productItems" data-productID="123">Product B</a>
</div>

<div id="dialogBox" style="display: none;"></div>

Контроллер -> Действие (ShowProductDetails)

public ActionResult ShowProductDetails(int productId)
{
  // Get product from db. and return the partial view

  return PartialView("ProductDetails", p);
}

Частичное представление (ProductDetails)

<script type="text/javascript">

   function SetProductTabContent(selectedTab) {
        $("#productDescriptionContent > div").css('display', 'none');

        switch (selectedTab) {

            case '#tab-1':
                $('#productDescriptionText').css('display', 'block');
                break;

            case '#tab-2':
                $('#productSpecificationText').css('display', 'block');
                break;   
        }


$(document).ready(function () {
    // Get all the menu items
    var menuItems = $("#productMenu a");

    // Select the first tab as default
    menuItems.first().addClass("menuItemActive");

    // Handle the look of the tabs, when user selects one. 
    menuItems.click(function () {

        var item = $(this);

        // Get content for the selected tab
        SetProductTabContent(item.attr('href'));

        menuItems.removeClass("menuItemActive");
        item.addClass("menuItemActive");
        return false;
    });
});
</script>


<div id="productMenu" style="">
    <a href="#tab-1">
        <div class="menuItemHeader">Menu1</div>
    </a>
    <a href="#tab-2">
        <div class="menuItemHeader">Menu2 </div>
    </a>
</div>


<div id="productDescriptionContent">

        <div id="productDescriptionText" style="display: none;">
            <%: Model.Product.Description %>
        </div>
        <div id="productSpecificationText" style="display: none;">
            <%: Model.Product.Description2%>
        </div>
</div>

ВЫПУСКА Когда частичное представление загружается дважды в DOM, div конфликтует.

Ответы [ 3 ]

7 голосов
/ 06 августа 2011

Да. Как вы указали, не используйте идентификаторы и селекторы идентификаторов в вашем JavaScript. Вместо этого используйте селекторы классов:

Например, по вашему мнению разметка :

<div class="container">Partial View content</div>

JS:

var $div = $('div.container');
// do something

Чтобы исключить возможность выбора других тегов с тем же именем класса, присвойте программному имени элементы в частичном представлении, которые используются только как дескриптор селектора, а не как класс CSS.

Несмотря на то, что поиск на основе идентификаторов является лучшим с точки зрения производительности, в этом случае имеет больше смысла использовать поиск на основе [tag + class], чтобы избежать конфликтов идентификаторов. Поиски на основе [tag + class] очень близки к селекторам идентификаторов по производительности.

Кроме того, вы можете добиться дальнейшего улучшения, ограничив область поиска:

<div class="container">Partial View content <span class="child">Child content </span></div>

var $span = $(span.child')  // scope of lookup here is entire document

Однако, если вы знаете, что child находится внутри контейнера div, вы можете ограничить область, сказав:

var $div = $('div.container').children('span.child'); // or just '.child'

Еще один совет - один раз выполнить поиск и использовать его повторно:

// less performant
function doSomething() {

    // do something here
    $('div.container').css('color', 'red');

    // do other things
    $('div.container').find('.child');

   // do more things
    $('div.container').click(function() {...});
}


// better
function doSomething() {
    var $div = $('div.container');

    // do something here
    $div.css('color', 'red');

    // do other things
    $div.find('.child');

   // do more things
    $div.click(function() {...});

   // or chaining them when appropriate
   $('div.container').css('color', 'red').click(function() { ... });


}

Обновление: Рефакторинг поста ОП для демонстрации концепции:

<script type="text/javascript">

       function SetProductTabContent(selectedTab, ctx) {
            var $container = $("div.pv_productDescriptionContent", ctx);

            // this will find only the immediate child (as you had shown with '>' selector)
            $container.children('div').css('display', 'none');  

            switch (selectedTab) {

                case '#tab-1':
                    $('div.pv_productDescriptionText', $container).css('display', 'block');
                    // or $container.children('div.pv_productDescriptionText').css('display', 'block');
                    break;

                case '#tab-2':
                    $('div.pv_productSpecificationText', $container).css('display', 'block');
                    // or $container.children('div.pv_productSpecificationText').css('display', 'block');
                    break;   
            }


    function SetUpMenuItems(ctx) {
        // Get all the menu items within the passed in context (parent element)
        var menuItems = $("div.pv_productMenu a", ctx);

        // Select the first tab as default
        menuItems.first().addClass("menuItemActive");

        // Handle the look of the tabs, when user selects one. 
        menuItems.click(function () {

            var item = $(this);

            // Get content for the selected tab
            SetProductTabContent(item.attr('href'), ctx);

            menuItems.removeClass("menuItemActive");
            item.addClass("menuItemActive");
            return false;
        });
    }
    </script>


<div style="" class="pv_productMenu">
    <a href="#tab-1">
        <div class="menuItemHeader">
            Menu1</div>
    </a><a href="#tab-2">
        <div class="menuItemHeader">
            Menu2
        </div>
    </a>
</div>
<div class="pv_productDescriptionContent">
    <div class="pv_productDescriptionText" style="display: none;">
        <%: Model.Product.Description %>
    </div>
    <div class="pv_productSpecificationText" style="display: none;">
        <%: Model.Product.Description2%>
    </div>
</div>

Примечание: Я удалил document.ready упаковщик, так как он не будет срабатывать при загрузке частичного представления. Вместо этого я реорганизовал JS вашего View для вызова функции установки, а также передал область действия (что позволит избежать выбора других элементов div с тем же классом):

// Fetch content in the background
$.get(url, input, function (result, response) {
       $('#dialogBox').html(result);
       SetUpMenuItems($('#dialogBox'));   
});

Очевидно, что вы можете изменить это дальше, как считаете нужным, в своем приложении. Я показал идею, а не окончательное решение.

  • Если вы снова загрузите #dialog, они будут перезаписывать существующую разметку, поэтому дубликатов не будет.
  • Если вы снова загрузите частичное представление в каком-либо другом контейнере, вы можете передать его в качестве контекста, и это помешает вам получить доступ к children из #dialog
  • Я придумал этот произвольный префикс pv_ для программных дескрипторов классов. Таким образом, вы можете определить имя класса для CSS или для использования в вашем скрипте.
0 голосов
/ 10 августа 2013

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

Сначала я попытался передать динамическую модель частичным, и это не работает. (грустное лицо) Затем я пошел набранный частичный маршрут просмотра. Я создал коллекцию объектов, называемых шагами (потому что создал мастер управления).

public class WizardStep
{
    public string Id { get; set; }
    public string Class { get; set; }
}

public class WizardSteps
{

    public WizardSteps()
    {
        Steps = new List<WizardStep>();
        Steps.Add(new WizardStep() {Id = "Step1"});
        Steps.Add(new WizardStep() { Id = "Step2" });
        Steps.Add(new WizardStep() { Id = "Step3" });
        Steps.Add(new WizardStep() { Id = "Step4" });
        Steps.Add(new WizardStep() { Id = "Step5" });

    }
    public List<WizardStep> Steps { get; set; }

}

Код бритвы выглядит так:

@Html.Partial("_WizardButtonPanel", @Model.WizardSteps.Steps.First())

или

@Html.Partial("_WizardButtonPanel", @Model.WizardSteps.Steps.Skip(1).First() )

или

@Html.Partial("_WizardButtonPanel", @Model.WizardSteps.Steps.Skip(2).First())

и более

@Html.Partial("_WizardButtonPanel",@Model.WizardSteps.Steps.Skip(3).First())

Частичный вид выглядит примерно так:

@model SomeProject.Models.WizardStep
<div id="buttonpanel-@Model.Id" >
<a id="link-@Model.Id" >Somelinke</a>
</div>

Счастливого кодирования ...

0 голосов
/ 06 августа 2011

Простейший способ сделать это - создать идентификаторы продуктов как часть идентификаторов тегов html

что-то в этом роде

<input type="text" id="txt_<%=Model.ID%>"> 

если вы используете Razor

<input type="text" id="txt_@Model.ID"> 
...