【问题标题】:Knockout and JQuery UI custom binding no recoginizing widget on observable array update淘汰赛和 JQuery UI 自定义绑定无法识别可观察数组更新的小部件
【发布时间】:2013-10-23 06:10:40
【问题描述】:

大家好,

我将 Knockoutjs 与 Jquery UI 小部件结合使用,以显示一个自动完成框,其中每个选定项目具有多个跨度。

我遵循以下方法

1) 在视图模型中有一个可观察数组 (selecteditems) 并将其绑定到声明性模板以显示 SPAN

2) 绑定到 JQUERY UI 自动完成小部件以显示建议的输入框,并在每次选择时使用 CustomBindingHandler 将新项目添加到 selecteditems 数组。

3) 使用 CustomBindingHandler 向绑定到可观察数组 selecteditems 的每个 SPAN 显示 JQUERY UI ToolTip 小部件。

我面临的问题是 JQUERY UI ToolTip 小部件在负载中显示没有任何问题,但是只要 selecteditems 数组发生更改,CustomBindingHandler 中就无法识别 Tooltip 小部件

非常感谢任何帮助。

<div>

    <div style="max-height: 105px;" data-bind="foreach: selectedItems">

        <span data-bind="text: name, id: id, assignToolTip: id"></span>

        <input data-bind="assignAutoComplete: { rootVm: $root }" type="email" value="">
    </div>

</div>

<script>

    var MyViewModel = function () {
        this.selectedItems = ko.observableArray(
            [{ name: "eww", id: "ww" },
                { name: "aa", id: "vv" },
                { name: "xx", id: "zz" }]);
    };

    ko.bindingHandlers.assignToolTip = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
            if ($(element) != undefined) {
                var currentDataItem = ko.dataFor(element);
                $(element).tooltip({
                    items: 'span',
                    track: true,
                    content: function () {

                        return "<ul><li>" + currentDataItem.name + "</li><li>" + currentDataItem.id + "</li></ul>";
                    }
                });
            }
        },

    };

    ko.bindingHandlers.assignAutoComplete = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
            if ($(element) != undefined) {
                var currentDataItem = ko.dataFor(element);
                $(element).autocomplete({
                    source: function (request, response) {
                        $.ajax({
                            url: "http://ws.geonames.org/searchJSON",
                            dataType: "jsonp",
                            data: {
                                featureClass: "P",
                                style: "full",
                                maxRows: 12,
                                name_startsWith: request.term
                            },
                            success: function (data) {

                                response($.map(data.geonames, function (item) {
                                    return {
                                        label: item.name + (item.adminName1 ? ", " + item.adminName1 : "") + ", " + item.countryName,
                                        value: item.name
                                    };
                                }));
                            }
                        });
                    },
                    minLength: 2,
                    select: function (event, ui) {
                        var settings = valueAccessor();
                        var rootVm = settings.rootVm;
                        rootVm.selectedItems.push({ name: ui.item.label, id: ui.item.label });
                        return false;
                    },
                    open: function () {
                        $(this).removeClass("ui-corner-all").addClass("ui-corner-top");
                    },
                    close: function () {
                        $(this).removeClass("ui-corner-top").addClass("ui-corner-all");
                    }
                });
            }
        }
    };


    ko.applyBindings(new MyViewModel());
</script>
<script src="~/Scripts/jquery-ui-1.10.3.js"></script>

【问题讨论】:

    标签: jquery jquery-ui knockout.js


    【解决方案1】:

    如果您尝试在数组中的值更改时更新工具提示,那么您需要稍微更改一下,以便观察数组中对象的值。

    var SelectedItem = function(obj){
        var self = this;
        self.name = ko.observable(obj.name);
        self.id = ko.observable(obj.id);
        self.tooltipText = ko.computed(function(){
            return "<ul><li>" + self.name() + "</li><li>" + self.id() + "</li></ul>";
        });
        return self;
    };
    
    var MyViewModel = function () {
        var self = this;
        self.selectedItems = ko.observableArray(
            [new SelectedItem({ name: "eww", id: "ww" }),
                new SelectedItem({ name: "aa", id: "vv" }),
                new SelectedItem({ name: "xx", id: "zz" })]);
        return self;
    };
    

    完成后,您需要更新 customBinding 以处理更新:

    ko.bindingHandlers.assignToolTip = {
        init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
            if ($(element) != undefined) {                
                var currentDataItem = ko.dataFor(element);
                $(element).tooltip({
                    items: 'span',
                    track: true,
                    content: function () {
                        return currentDataItem.tooltipText();
                    }
                });
            }
        },
        update: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext){            
            if ($(element) != undefined) {                
                $(element).tooltip( "destroy" );
                var currentDataItem = ko.dataFor(element);
                $(element).tooltip({
                    items: 'span',
                    track: true,
                    content: function () {
                        return currentDataItem.tooltipText();
                    }
                });
            }
        }
    };
    

    最后需要更改的是,任何时候你推入可观察数组时,它都应该是SelectedItem 对象的一个​​实例:

    select: function (event, ui) {
        var settings = valueAccessor();
        var rootVm = settings.rootVm;
        rootVm.selectedItems.push(
            new SelectedItem({ name: ui.item.label, id: ui.item.label })
        );
        return false;
    },
    

    工作示例:http://jsfiddle.net/infiniteloops/PLYKk/

    【讨论】:

    • 为什么是if ($(element) != undefined)?一方面,使用if (element) 更短并且不涉及一次性的jQuery 调用,但更重要的是:element 永远不会被未定义。 (哦,最重要的是,$(anything) 一开始就永远不会未定义,它将永远是一个 jQuery 对象)
    • @Tomalak 我没有尝试对现有代码进行代码审查。当然if(element) 更有意义,但这不是 OP 的问题。
    • 哦,我设法忽略了这直接来自 OP。那么,假设评论是针对他的。 :)
    【解决方案2】:

    jQuery UI Tooltip widget 的 API 文档表明工具提示逻辑旨在绑定到容器而不是单个元素。

    例如,要在纯 jQuery 中为 &lt;ul&gt; 中的所有 &lt;li&gt; 获取工具提示,您可以这样做:

    $("ul").tooltip({
        items: "li",
        content: function () {
            return "tooltip text for this element";
        }
    });
    

    主要优点是当容器的子项发生更改时,您不需要绑定/取消绑定/更新任何工具提示逻辑。另一个优点是这样可以减少页面的负载,因为它只注册一个工具提示而不是几个。


    您可以(而且应该!)使用这种方法,因为它完全符合您的要求。您有一个容器,其中包含可变数量的子级,这些子级都应该显示一个工具提示,其中包含根据相同逻辑构建的内容。

    由于我们绑定到容器,我们需要在容器的视图模型上使用一个小型代理函数来为我们检索单独的工具提示文本。

    HTML 模板:

    <div data-bind="
        foreach: items, 
        tooltip: {items: 'label', content: tooltipContentProxy}
    ">
        <div>
            <label data-bind="text: name, attr: {for: id}"></label>
            <input data-bind="attr: {id: id}, value: inputVal, valueUpdate: 'keyup'" type="text" />
        </div>
    </div>
    

    tooltip 自定义绑定处理程序:

    ko.bindingHandlers.tooltip = {
        init: function (element, valueAccessor) {
            var options = ko.unwrap(valueAccessor());
            $(element).tooltip(options);
        }
    };
    

    注意我们的方式

    • 可以在绑定中方便地配置所有提示选项
    • 对视图或视图模型没有任何依赖关系
    • 甚至不需要 update 处理程序,因为此设置与任何数据更改无关

    最后是我们的视图模型:

    function Item(data) {
        var self = this;
    
        self.id = ko.observable(data.id);
        self.name = ko.observable(data.name);
        self.inputVal = ko.observable(""); 
        self.tooltipText = ko.computed(function () {
            var escapedVal = $("<div>", {text: self.inputVal()}).html();
            return "Hi! My value is '" + escapedVal + "'.";
        });
    }
    
    function ViewModel() {
        var self = this;
    
        self.items = ko.observableArray([/* Item objects here ...*/]);
    
        self.tooltipContentProxy = function () {
            var targetItem = ko.dataFor(this);
            return targetItem.tooltipText();
        };
    }
    

    现在工具提示可以正确显示,无需大惊小怪。 http://jsfiddle.net/7TqpK/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-12-20
      • 2015-01-25
      • 2014-09-28
      • 1970-01-01
      • 2013-05-01
      • 2012-09-08
      • 1970-01-01
      相关资源
      最近更新 更多