为jquery模板添加包含html的脚本

时间:2011-08-27 11:48:36

标签: jquery web-parts jquery-templates

当我尝试解释这个时,请耐心等待,因为它有些复杂,我对JQuery完全不熟悉并试图了解它。

我和我的团队正在为一个SharePoint项目开发WebParts。 WebPart包含一个用户控件(SearchControl),它有一个链接打开一个包含另一个用户控件(SelectorControl)的对话框(使用jquery ui)。

SelectorControl包含:

  • 使用JQuery模板填充的下拉列表(objTemplates)
  • 使用其他JQuery模板填充的表;和
  • 一个只读文本输入,用于显示表格中当前选定的项目

SelectorControl代码:

        <div class="objSelector">
        <script id="objSelectorTemplate" type="text/x-jquery-tmpl">
        <div class="table">
            <div class="row">
               <div class="labelInputGroup">
                    <label for="objTemplates">Obj Template</label>
                    <select id="objTemplates">
                        {{each ObjectTemplates}}
                            <option value="${ObjectTemplateId}">${Name}</option>
                        {{/each}}
                    </select>
                </div>
            </div>
            <div><br></div>
            <div class="row">
                <div>
                    <table class="tableGrid">
                        <thead>
                            <tr>
                                <th>Object Name</th>
                            </tr>
                        </thead>
                        <tbody id="listPlaceholder">
                        </tbody>
                    </table>
                    <div id="pagerPlaceholder"></div>
                </div>        
            </div>     
            <div id="templatePlaceholder">
            </div>
            <div><br></div>
            <div class="row">
                <input type="hidden" id="localSelectedObjectId" class="hidden" />
                <label for="localSelectedObject">Selected Object</label>
                <input type="text" class="dealName" id="localSelectedObject" readonly="readonly" placeholder="No object selected"/>
            </div>
        </div>
        </script>    
        <script id="listTemplate" type="text/x-jquery-tmpl">
            <tr>
                <td><a class="objectItem" id="${Id}" href="#">${Name}</a></td>
            </tr>
       </script>    
       <div id="pagerControl">
          <uc1:PagerControl ID="PagerControl1" runat="server" />
       </div> 
    </div>

在使用从WCF服务检索的数据创建对话框之前填充下拉列表,该服务随后绑定到第一个模板。

“更改”事件绑定到下拉列表,使用live来填充表(第二个模板),方法是调用WCF服务并传递下拉列表中当前选定的项目。

在下拉列表中触发更改事件时,将调用LoadObject:

            function LoadObjects(event) {

            var dialogDom = event.data.DialogDom;
            var searchObj = GetSearchFilters(); // removed irrelevant code here

            var listTemplate = searchDom.find('#listTemplate');
                    var templatePlaceholder = dialogDom.find('#templatePlaceholder');
                    templatePlaceholder.empty();
                    templatePlaceholder.append(listTemplate);

                    var pagerControl = searchDom.find('#pagerControl');
                    var pagerPlaceholder = dialogDom.find('#pagerPlaceholder');
                    pagerPlaceholder.append(pagerControl);

                    var list = GenerateList(
                        dialogDom,
                        $('<div></div>'),
                        searchObj,
                        Connection('MyUiService', 'GetObjects'));
        }

GenerateList()方法使用各种方法创建一个JQuery对象,包括从WCF服务获取对象数据并将其绑定到模板的方法(listTemplate)

PagerControl代码还包含一个模板,该模板与listTemplate同时填充。

listTemplate和pager的代码必须放在objectSelectorTemplate脚本之外,然后在以后复制,以防止在解析页面以填充下拉列表时删除它。

首次打开对话框并从下拉列表中选择一个项目时,以上所有工作都很正常。可以毫无问题地更改表中数据中的页面,但只要下拉列表中的所选项发生更改,就不再加载表数据。如果我单步执行代码,我可以看到append()(在LoadObjects方法中)将listTemplate脚本插入到listPlaceholder时会删除它,这意味着当对话框再次加载表数据时,脚本不再是那里。为了解决这个问题,我尝试添加一个克隆:

    templatePlaceholder.append(listTemplate.clone());

这成功阻止了listTemplate脚本被删除,但是,克隆的副本包含脚本标记但不包含其内容,因此表数据仍未加载。我搜索了SO并找到了John Resig对this question的回复:

所以我将上面的代码更改为:

    var listTemplateCopy = jQuery.extend({}, listTemplate);
    templatePlaceholder.append(listTemplateCopy);

这成功地将脚本标记及其内容复制到listTemplateCopy,但append()删除了listTemplate以及listTemplateCopy。起初我假设这是因为extend用于将两个(或更多)对象合并为一个对象,但是从John的响应和我在文档中读到的内容,它听起来像是你应该使用的扩展创建一个jquery对象的副本。

任何人都可以向我解释究竟发生了什么,并推荐上述解决方案吗?

我的一些想法是:

  • 将脚本标记移动到仅包围模板代码。然后,我可以使用第二个模板代码,而不是需要附加它。绑定将通过绑定到下拉列表模板然后绑定到表
  • 来完成
  • 使用不同的方式填充下拉列表而不是模板以简化页面并删除附加脚本的“必要性”,因为整个批次周围不会有脚本标记

我希望在采用其中任何一种方法之前,先了解代码目前正在做什么。

1 个答案:

答案 0 :(得分:0)

这绝不是一个优雅的答案,但实际上,这是IE8的脚本引擎及其前身的失败。如果您的项目有支持这些浏览器的要求,那么代码将会更加难以理解,jQuery将没有多大帮助。你会注意到你在代码中首次使用.clone()实际上在IE9,Firefox,Chrome和Safari中运行得很好。请允许我解释一下这个特殊的困难。

您的要求是复制DOM对象。 $.extend()在这个实例中是不合适的,因为它只是克隆一个包装DOM目标集的jQuery对象,而不是实际的DOM目标。这意味着包装器的新副本仍然具有相同的DOM元素,并且不会附加目标副本,而是移动目标。因此,您最初使用.clone()的假设是正确的。代码无法按预期运行的原因超出了您的控制范围。

IE9使用其前任的Node.cloneNode()方法纠正了明显的缺陷,其中innerHTML对象的Node成员在script元素的情况下不会被保留。这是jQuery的.clone()调用使用的方法。在IE8及更早版本的任何脚本块上执行一系列经典JavaScript都会显示出来:

alert(document.getElementById("listTemplate").cloneNode(true).innerHTML);

此警报将包含所有其他主要浏览器中的内容。在IE8及以下版本中并非如此。 “true”参数执行深层复制只是为了证明一个观点,但事实上这并不是必要的,因为script元素在其Nodes集合中没有childNodes

解决方法是丑陋的,我不完全确定它会按预期运行,因为我没有你的系统进行测试。您必须明确修改占位符元素的innerHTML属性。您可以考虑的一件事是包括defer属性,当在IE中动态插入时,该属性往往会使脚本块表现得更好(请参阅MSDN - innerHTML Property at,“使用innerHTML插入脚本时,必须包含DEFER属性在脚本元素“)中。它会使您的JavaScript看起来像以下内容:

var listTemplate = searchDom.find('#listTemplate');
var myHtml = '<script defer="defer" type="text/x-jquery-tmpl">';
myHtml += listTemplate.html();
myHtml += "</script" + ">";

var templatePlaceholder = dialogDom.find('#templatePlaceholder');
templatePlaceholder.html(myHtml);

我假设listTemplatetemplatePlaceholder是jQuery对象,所以如果我弄错了,我道歉。但是,这里的基本思想是将整个脚本块构建为HTML字符串,并用您构建的字符串替换占位符中的HTML。

我不能保证IE会正确解释脚本块并使其可用于您的jQuery模板,但检查我选择的受害者占位符的innerHTML确实表明,至少它已被替换正确使用生成的脚本块。

我很想知道这是否有效。