Knockout和JQuery UI自定义绑定无法识别observablearray更新中的小部件

时间:2013-10-23 06:10:41

标签: jquery jquery-ui knockout.js

大家好,

我正在使用Knockoutjs与Jquery UI小部件结合使用,以显示每个所选项目具有多个跨度的自动完成框。

我正在遵循以下方法

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

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

3)使用CustomBindingHandler向每个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>

2 个答案:

答案 0 :(得分:0)

如果您在阵列中的值发生更改时尝试更新工具提示,则需要稍微更改一下,以便观察阵列中对象的值。

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/

答案 1 :(得分:0)

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

例如,要获取纯jQuery中<li>中所有<ul>的工具提示,您可以这样做:

$("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/