2011-08-06 11 views
11

jak jestem wciąż nowe do MVC 3 i jQuery, chciałbym wiedzieć, najlepszym rozwiązaniem praktyce, w jaki sposób można rozwiązać następujący:MVC 3, ponowne wykorzystanie częściowym widokiem i jQuery, bez sprzecznych DOM

I mieć widok, w którym używam jquery ajax do pobierania i wyświetlania częściowego widoku z niektórymi szczegółami produktu dla produktu A. Załadowany widok częściowy składa się z pakietu html i kodu jquery, który jest powiązany ze zdefiniowanym identyfikatorem w częściowym widoku.

Dlatego chciałbym ponownie użyć tego samego widoku częściowego, aby wyświetlić szczegóły z innych produktów w tym samym widoku (np. Wyświetlić szczegóły produktu B w wyskakującym oknie dialogowym). Za każdym razem, gdy zostanie wyświetlone okno podręczne, nowo pobrany widok częściowy będzie kolidował z widokiem częściowym dla produktu A, ponieważ te same identyfikatory są używane w html.

Conceptual overview of the case

Czy istnieje sposób do hermetyzacji kodu HTML i JavaScript w częściowym widoku i używać go z kilku stron, bez obaw o jakiekolwiek konflikty z ID i rzeczy?

Mam nadzieję, że moje pytanie ma sens. Dzięki,

/Nima

AKTUALIZACJA

Oto pseudo kod, przedstawiając mój problem:

WIDOK

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

Controller -> Akcja (ShowProductDetails)

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

    return PartialView("ProductDetails", p); 
} 

częściowy widok (informacje szczegółowe)

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

WYDANIE Gdy częściowy widok zostanie załadowany dwa razy w DOM, konflikty DIV.

+0

Co o pokazanie zawartości DialogBox w iframe? – Nima

Odpowiedz

7

Tak. Jak zauważyłeś, nie używaj identyfikatorów i identyfikatorów w JavaScript. Zamiast używać klasy selektorów:

Np w widoku za znaczników:

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

JS:

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

aby wyeliminować możliwość wyboru inne tagi z samej nazwy klasy, przypisać programowa nazwa elementów w widoku częściowym, który jest używany tylko jako uchwyt selektora, a nie jako klasa CSS.

Chociaż wyszukiwanie oparte na identyfikatorze jest najlepszą wydajnością, w tym przypadku rozsądniej jest przejść przez wyszukiwanie oparte na [tag + class], aby uniknąć konfliktów id. Wyszukiwanie oparte na tagach i tagach jest dość zbliżone do selektorów id pod względem wydajności.

Ponadto, można uzyskać dalszą poprawę poprzez ograniczenie zakresu odnośnika:

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

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

Jednakże, jeśli wiesz, że child jest wewnątrz kontenera div, można ograniczyć zakres mówiąc:

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

Kolejną wskazówką jest wykonanie wyszukiwania raz i ponowne użycie:

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


} 

Aktualizacja: post refaktoringu OP demo koncepcję:

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

Uwaga: usunąłem document.ready opakowanie ponieważ nie będą uruchamiane podczas ładowania częściowy widok. Zamiast tego refactored Pana zdaniem JS, aby wywołać funkcję konfiguracji, a także przekazać w zakresie (co pozwoli uniknąć wybierając inne div z tej samej klasy):

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

Oczywiście, można zmodyfikować to dalej jak uznają zmieścić się w aplikacja, co pokazałem to pomysł, a nie ostateczne rozwiązanie.

  • Po ponownym załadowaniu #dialog nadpisują istniejące znaczniki, dlatego nie będzie ich duplikować.
  • Jeśli załadować częściowy widok ponownie w innym pojemniku, który można przekazać jako kontekstu i że uniemożliwi Ci uzyskiwania dostępu do children z #dialog
  • wymyśliłem tej arbitralnej prefiksu pv_ do klamek programowych klasy. W ten sposób możesz powiedzieć, patrząc na nazwę klasy, jeśli jest ona dla CSS lub do użycia w twoim skrypcie.
+1

Problem z selektorami klas jest taki, że jeśli ja np. wykonać pewne działanie na produkcie B (w wyskakującym okienku), to samo wystąpi w produkcie A. Te dwie nie powinny od siebie od siebie zależać. – Nima

+0

Spróbuj użyć stylu danych * HTML5 i wybierz te oparte na nowym atrybucie. Lik data-product = "1" – turtlepick

+0

To tam pomaga wyszukiwanie zakresu. Możesz wyszukiwać w określonym zakresie. Co więcej, możesz nazwać selektory takimi, że będą unikatowe: 'class =" partial_view_main_container "'. Nie musi to być klasa css, której można użyć do stylizacji, ale tylko do wyszukiwania zawartości częściowego widoku. – Mrchief

0

Najprostszym sposobem na to jest, uczynić identyfikatory produktów w ramach znaczników HTML identyfikatorami

coś takiego

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

jeśli używasz Razor

<input type="text" id="[email protected]"> 
+0

To jest niechlujne podejście w dłuższej perspektywie. Kilka przykładów: 1) Onus jest w stanie wygenerować unikatowe identyfikatory za każdym razem (więc między żądaniami, musisz jakoś zachować kartę). 2) Musisz wstrzyknąć to w jakiś sposób javascript (chyba że piszesz skrypty wbudowane) również. 3) Twój javscript nie może być testowany jednostkowo, ponieważ zależy od identyfikatorów dynamicznie geibrowanych. – Mrchief

+0

Cóż, właściwie ma sens dołączenie identyfikatora do html. Ale wszystkie punkty stworzone przez @Mrchief mają również sens. Ale czy to naprawdę jest sposób na zrobienie tego? – Nima

+0

@Nima jest to najprostszy sposób, aby zapobiec konfliktom między elementami div, gdy widok częściowy jest ładowany dwa razy, ale istnieje wiele innych sposobów wykonywania próbkowania –

0

I Zdziwiłem się, że to nie pojawia się częściej. Wydaje mi się, że większość programistów nie tworzy własnych kontrolek z częściami. Tutaj jest coś, co przyniosłem, działa całkiem nieźle i nie jest trudne do wdrożenia w MVC4.

Najpierw próbowałem przekazać model dynamiczny częściowemu, a to nie działa. (smutna twarz) Potem poszedłem na maszynową trasę częściowego widoku. Utworzono kolekcję obiektów nazywanych krokami (ponieważ zbudowano kontrolkę kreatora).

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

} 

Razor kod wygląda następująco:

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

lub

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

lub

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

i więcej

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

Częściowy widok wygląda mniej więcej tak:

@model SomeProject.Models.WizardStep 
<div id="[email protected]" > 
<a id="[email protected]" >Somelinke</a> 
</div> 

Szczęśliwa kodowanie ...

+0

BTW patrząc przy użyciu selektora klasy spowoduje problemy w kontroli typu kreatora. – Tim

Powiązane problemy