MVC 3,部分视图和jquery的重用,而不影响DOM [英] MVC 3, reuse of partial views and jquery, without conflicting the DOM

查看:133
本文介绍了MVC 3,部分视图和jquery的重用,而不影响DOM的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因为我仍然是MVC 3和jquery的新手,我想知道如何解决以下问题的最佳实践方案:



我有一个视图,我使用jquery ajax来获取并显示产品A的一些产品详细信息的部分视图。加载的部分视图包含一堆html和jquery代码,它与局部视图中的定义的ID相关联。 >

因此,我想重复使用相同的部分视图来显示同一视图中其他产品的详细信息(例如,在弹出对话框中显示产品B的详细信息)。每当显示弹出窗口时,新获取的部分视图将与产品A的部分视图冲突,因为在html中使用相同的id。





有没有办法在部分视图中封装html和javascript,并重新使用几页,而不用担心与ID和内容的任何冲突?



我希望我的问题有道理。谢谢,



/ Nima



更新



以下是一些伪代码,概述了我的问题:



VIEW

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

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


//显示具有产品详细信息的模式框
$('#dialogBox')。对话框({
标题:$(this).attr('data-productTitle')


$ b //在后台获取内容
$ .get(url,input,function(result,response){
$(' #dialogBox')。html(result);
});
});
});
< / script>


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

< div id =productLinks>
< span class =productItemsdata-productID =123>产品B< / a>
< / div>

< div id =dialogBoxstyle =display:none;>< / div>

控制器 - >动作(ShowProductDetails)

  public ActionResult ShowProductDetails(int productId)
{
//从db获取产品。并返回部分视图

返回PartialView(ProductDetails,p);
}

部分视图(ProductDetails)

 < script type =text / javascript> 

函数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(){
//获取所有菜单项
var menuItems = $(# productMenu a);

//选择第一个选项卡作为默认
menuItems.first()。addClass(menuItemActive);

//处理
menuItems.click(function(){

var item = $(this);

//获取内容对于所选的选项卡
SetProductTabContent(item.attr('href'));

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


< div id =productMenustyle =>
< 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 =productDescriptionTextstyle =display:none;>
<%:Model.Product.Description%>
< / div>
< div id =productSpecificationTextstyle =display:none;>
<%:Model.Product.Description2%>
< / div>
< / div>

ISSUE
当部分视图在DOM,divs冲突。

解决方案

是的。正如您所指出的,不要在JavaScript中使用ids和id选择器。而是使用类选择器:



例如,在您的视图的标记中:

 < div class =container>部分查看内容< / div> 

JS:

  var $ div = $('div.container'); 
//做某事

为了消除选择具有相同类名的其他标签的可能性,一个编程名称部分视图中的元素仅用作选择器句柄而不是CSS类。



虽然基于ID的查找是最好的性能明智的,在这情况下,通过基于[tag + class]的查找更有意义,以避免id冲突。 [/ p>

此外,您可以通过限制查找范围获得进一步的改进:

 < div class =container>部分视图内容< span class =child>子内容< / span> < / DIV> 

var $ span = $(span.child')//这里查找的范围是整个文档

但是,如果您知道 child 在容器div内,则可以通过说:

  var $ div = $('div.container')。children('span.child'); //或只是'.child'

另一个提示是进行一次查找并重新使用: / p>

  //性能较差
函数doSomething(){

//在这里做某事b $ b $('div.container')。css('color','red');

//做其他事情
$('div.container')。find('。child');

//做更多的事情
$('div.container')。click(function(){...});
}


//更好的
函数doSomething(){
var $ div = $('div.container');

//在这里做某事
$ div.css('color','red');

//做其他事情
$ div.find('。child');

//做更多的事情
$ div.click(function(){...});

//或适当时链接
$('div.container')。css('color','red')。click(function(){...}) ;


}

更新:重构OP的帖子来演示这个概念:

 < script type =text / javascript> 

函数SetProductTabContent(selectedTab,ctx){
var $ container = $(div.pv_productDescriptionContent,ctx);

//这将只找到直接的孩子(如你所示的'>'选择器)
$ container.children('div')。css('display'没有');

switch(selectedTab){

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

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


函数SetUpMenuItems(ctx){
//获取所传递的上下文中的所有菜单项(父元素)
var menuItems = $(div.pv_productMenu a,ctx);

//选择第一个选项卡作为默认
menuItems.first()。addClass(menuItemActive);

//当用户选择一个时,处理选项卡的外观。
menuItems.click(function(){

var item = $(this);

//获取所选选项卡的内容
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_productDescriptionTextstyle =display:none;>
<%:Model.Product.Description%>
< / div>
< div class =pv_productSpecificationTextstyle =display:none;>
<%:Model.Product.Description2%>
< / div>
< / div>

注意:我删除了 document.ready 包装器,因为加载部分视图时不会触发。相反,我重构了您的View的JS来调用setup函数,并且传入范围(这将避免使用同一个类选择其他div):

  //在后台获取内容
$ .get(url,input,function(result,response){
$('#dialogBox')。html(result);
SetUpMenuItems($('#dialogBox'));
});

显然,您可以按照您认为适合您的应用的方式进一步修改,我显示的是一个想法,而不是最终的解决方案。




  • 如果您再次加载 #dialog 它们将覆盖现有的标记,因此不会有重复。

  • 如果在其他容器中再次加载部分视图,则可以将其作为上下文传递,这将阻止您访问儿童 #dialog

  • 我想出了这个任意前缀 pv _ 用于程序化类句柄。这样,您可以看到类名,如果它是用于CSS或在脚本中使用。


As i am still new to MVC 3 and jquery, i would like to know a best practice solution to how the following can be solved:

I have a view, where I use jquery ajax to fetch and display a partial view with some product details for product A. The loaded partial view consist of a bunch of html and jquery code, which is tied to the defined id's within the partial view.

Thus, i would like to reuse the same partial view to show details from other products on the same View (e.g. show product B details in a pop-up dialog). Whenever the pop-up is shown, the newly fetched partial view will conflict with the partial view for product A, as the same id's are used in the html.

Is there a way to encapsulate the html and javascript in the partial view, and reuse it several pages without worry about any conflicts with ID's and stuff?

I hope my question makes sense. Thanks,

/Nima

UPDATED

Here is some pseudo code, outlining my issue:

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>

Controller -> Action (ShowProductDetails)

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

  return PartialView("ProductDetails", p);
}

Partial View (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>

ISSUE When the partial view gets loaded twice in the DOM, the divs conflicts.

解决方案

Yes. As you pointed out, do not use ids and id selectors in your JavaScript. Instead use class selectors:

E.g., in your view's markup:

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

JS:

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

To eliminate possibility of selecting other tags with same class name, assign a programmatic name the elements in partial view which is used only as a selector handle and not as a CSS class.

While ID based lookup is the best performance wise, in this case, it makes more sense to go by the [tag+class] based lookup to avoid id conflicts. [tag+class] based lookup comes pretty close to id selectors in terms of performance.

Also, you can gain further improvement by limiting the lookup scope:

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

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

However, if you know that child is inside container div, you can limit the scope by saying:

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

Another tip is to do the lookup once and reuse it:

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


}

Update: Refactoring OP's post to demo the concept:

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

Note: I removed document.ready wrapper since that will not fire when you load the partial view. Instead, I refactored your View's JS to call the setup function and also pass in the scope (which will avoid selecting other divs with same class):

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

Obviously, you can modify this further as you deem fit in your app, what I've shown is an idea and not the final solution.

  • If you load #dialog again, they will overwrite existing markup, hence there won't be duplicate.
  • If you load the partial view again in some other container, you can pass that as the context and that will prevent you accessing the children of #dialog
  • I came up with this arbitrary prefix pv_ for programmatic class handles. That way, you can tell looking at the class name if it is for CSS or for use in your script.

这篇关于MVC 3,部分视图和jquery的重用,而不影响DOM的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆