将 jquery ui 对话框与 knockoutjs 集成

发布于 2024-12-22 17:00:07 字数 2220 浏览 1 评论 0原文

我正在尝试为 jquery ui 对话框创建 knockoutjs 绑定,但无法打开对话框。对话框元素已正确创建,但似乎有 display: none ,调用 dialog('open') 不会删除。此外,对 dialog('isOpen') 的调用返回对话框对象而不是布尔值。

我正在使用最新的knockoutjs 和jquery 1.4.4 以及jquery ui 1.8.7。我也用 jQuery 1.7.1 进行了尝试,得到了相同的结果。这是我的 HTML:

<h1 class="header" data-bind="text: label"></h1>

<div id="dialog" data-bind="dialog: {autoOpen: false, title: 'Dialog test'}">foo dialog</div>

<div>
    <button id="openbutton" data-bind="dialogcmd: {id: 'dialog'}" >Open</button>
    <button id="openbutton" data-bind="dialogcmd: {id: 'dialog', cmd: 'close'}" >Close</button>
</div>

这是 javascript:

var jQueryWidget = function(element, valueAccessor, name, constructor) {
    var options = ko.utils.unwrapObservable(valueAccessor());
    var $element = $(element);
    var $widget = $element.data(name) || constructor($element, options);
    $element.data(name, $widget);

};

ko.bindingHandlers.dialog = {
        init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
            jQueryWidget(element, valueAccessor, 'dialog', function($element, options) {
                console.log("Creating dialog on "  + $element);
                return $element.dialog(options);
            });
        }        
};

ko.bindingHandlers.dialogcmd = {
        init: function(element, valueAccessor, allBindingsAccessor, viewModel) {          
            $(element).button().click(function() {
                var options = ko.utils.unwrapObservable(valueAccessor());
                var $dialog = $('#' + options.id).data('dialog');
                var isOpen = $dialog.dialog('isOpen');
                console.log("Before command dialog is open: " + isOpen);
                $dialog.dialog(options.cmd || 'open');
                return false;
            });
        }        
};

var viewModel = {
    label: ko.observable('dialog test')
};

ko.applyBindings(viewModel);

我已经设置了一个 JSFiddle 来重现问题。

我想知道这是否与 knockoutjs 和事件处理有关。我尝试从点击处理程序返回 true,但这似乎没有影响任何内容。

I am trying to create knockoutjs bindings for jquery ui dialogs, and cannot get the dialog to open. The dialog element is created correctly, but seems to have display: none that calling dialog('open') doesn't remove. Also, the call to dialog('isOpen') returns the dialog object rather than a boolean.

I am using the latest knockoutjs and jquery 1.4.4 with jquery ui 1.8.7. I've also tried it with jQuery 1.7.1 with the same results. Here's my HTML:

<h1 class="header" data-bind="text: label"></h1>

<div id="dialog" data-bind="dialog: {autoOpen: false, title: 'Dialog test'}">foo dialog</div>

<div>
    <button id="openbutton" data-bind="dialogcmd: {id: 'dialog'}" >Open</button>
    <button id="openbutton" data-bind="dialogcmd: {id: 'dialog', cmd: 'close'}" >Close</button>
</div>

and this is the javascript:

var jQueryWidget = function(element, valueAccessor, name, constructor) {
    var options = ko.utils.unwrapObservable(valueAccessor());
    var $element = $(element);
    var $widget = $element.data(name) || constructor($element, options);
    $element.data(name, $widget);

};

ko.bindingHandlers.dialog = {
        init: function(element, valueAccessor, allBindingsAccessor, viewModel) {
            jQueryWidget(element, valueAccessor, 'dialog', function($element, options) {
                console.log("Creating dialog on "  + $element);
                return $element.dialog(options);
            });
        }        
};

ko.bindingHandlers.dialogcmd = {
        init: function(element, valueAccessor, allBindingsAccessor, viewModel) {          
            $(element).button().click(function() {
                var options = ko.utils.unwrapObservable(valueAccessor());
                var $dialog = $('#' + options.id).data('dialog');
                var isOpen = $dialog.dialog('isOpen');
                console.log("Before command dialog is open: " + isOpen);
                $dialog.dialog(options.cmd || 'open');
                return false;
            });
        }        
};

var viewModel = {
    label: ko.observable('dialog test')
};

ko.applyBindings(viewModel);

I have set up a JSFiddle that reproduces the problem.

I am wondering if this has something to do with knockoutjs and event handling. I tried returning true from the click handler, but that did not appear to affect anything.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

月亮邮递员 2024-12-29 17:00:07

看起来像将小部件写入 .data("dialog") 然后尝试对其进行操作会导致问题。以下是一个示例,其中未使用 .data ,并且根据元素调用打开/关闭:http: //jsfiddle.net/rniemeyer/durKS/

另外,我喜欢以稍微不同的方式使用对话框。我喜欢使用可观察对象来控制对话框是打开还是关闭。因此,您可以在对话框本身上使用单个绑定。 init 将初始化对话框,而 update 将检查可观察对象以查看它是否应该调用打开或关闭。现在,打开/关闭按钮只需要切换布尔可观察值,而不用担心 ids 或定位实际的对话框。

ko.bindingHandlers.dialog = {
        init: function(element, valueAccessor, allBindingsAccessor) {
            var options = ko.utils.unwrapObservable(valueAccessor()) || {};
            //do in a setTimeout, so the applyBindings doesn't bind twice from element being copied and moved to bottom
            setTimeout(function() { 
                options.close = function() {
                    allBindingsAccessor().dialogVisible(false);                        
                };

                $(element).dialog(options);          
            }, 0);

            //handle disposal (not strictly necessary in this scenario)
             ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
                 $(element).dialog("destroy");
             });   
        },
        update: function(element, valueAccessor, allBindingsAccessor) {
            var shouldBeOpen = ko.utils.unwrapObservable(allBindingsAccessor().dialogVisible),
                $el = $(element),
                dialog = $el.data("uiDialog") || $el.data("dialog");

            //don't call open/close before initilization
            if (dialog) {
                $el.dialog(shouldBeOpen ? "open" : "close");
            }  
        }
};

使用方式如下:

<div id="dialog" data-bind="dialog: {autoOpen: false, title: 'Dialog test' }, dialogVisible: isOpen">foo dialog</div>

这是一个示例:
http://jsfiddle.net/rniemeyer/SnPdE/

It looks like writing to the widget to .data("dialog") and then trying to operate on it is causing an issue. Here is a sample where .data is not used and the open/close is called based on the element: http://jsfiddle.net/rniemeyer/durKS/

Alternatively, I like to work with the dialog in a slightly different way. I like to control whether the dialog is open or closed by using an observable. So, you would use a single binding on the dialog itself. The init would initialize the dialog, while the update would check an observable to see if it should call open or close. Now, the open/close buttons just need to toggle a boolean observable rather than worry about ids or locating the actual dialog.

ko.bindingHandlers.dialog = {
        init: function(element, valueAccessor, allBindingsAccessor) {
            var options = ko.utils.unwrapObservable(valueAccessor()) || {};
            //do in a setTimeout, so the applyBindings doesn't bind twice from element being copied and moved to bottom
            setTimeout(function() { 
                options.close = function() {
                    allBindingsAccessor().dialogVisible(false);                        
                };

                $(element).dialog(options);          
            }, 0);

            //handle disposal (not strictly necessary in this scenario)
             ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
                 $(element).dialog("destroy");
             });   
        },
        update: function(element, valueAccessor, allBindingsAccessor) {
            var shouldBeOpen = ko.utils.unwrapObservable(allBindingsAccessor().dialogVisible),
                $el = $(element),
                dialog = $el.data("uiDialog") || $el.data("dialog");

            //don't call open/close before initilization
            if (dialog) {
                $el.dialog(shouldBeOpen ? "open" : "close");
            }  
        }
};

Used like:

<div id="dialog" data-bind="dialog: {autoOpen: false, title: 'Dialog test' }, dialogVisible: isOpen">foo dialog</div>

Here is a sample:
http://jsfiddle.net/rniemeyer/SnPdE/

安静 2024-12-29 17:00:07

我对 RP Niemeyer 的答案做了一些更改,以允许对话框的选项可观察

http://jsfiddle.net/YmQTW /1/

使用 ko.toJS 获取可观察值以初始化小部件

setTimeout(function() { 
    options.close = function() {
        allBindingsAccessor().dialogVisible(false);                        
    };

    $(element).dialog(ko.toJS(options));          
}, 0);

并在更新时检查可观察值

//don't call dialog methods before initilization
if (dialog) {
    $el.dialog(shouldBeOpen ? "open" : "close");

    for (var key in options) {
        if (ko.isObservable(options[key])) {
            $el.dialog("option", key, options[key]());
        }
    }
}

I made a little change to RP Niemeyer's answer to allow the dialog's options to be observables

http://jsfiddle.net/YmQTW/1/

Get the observables values with ko.toJS to initialize the widget

setTimeout(function() { 
    options.close = function() {
        allBindingsAccessor().dialogVisible(false);                        
    };

    $(element).dialog(ko.toJS(options));          
}, 0);

and check for observables on update

//don't call dialog methods before initilization
if (dialog) {
    $el.dialog(shouldBeOpen ? "open" : "close");

    for (var key in options) {
        if (ko.isObservable(options[key])) {
            $el.dialog("option", key, options[key]());
        }
    }
}
请爱~陌生人 2024-12-29 17:00:07

在此处添加此内容是因为这是大多数人在搜索 jQuery UI Dialog 和 Knockout JS 问题时发现的内容。

这是避免上述答案中解释的“双重绑定”问题的另一种选择。对我来说, setTimeout() 导致其他需要初始化对话框的绑定失败。对我有用的简单解决方案是对接受的答案进行以下更改:

  1. 使用自定义对话框绑定将 class='dialog' 添加到任何元素。

  2. 在页面加载之后、调用 ko.applyBindings() 之前调用此方法:

    $('.dialog').dialog({autoOpen: false});

去掉自定义绑定的init里面的setTimeout,直接调用代码即可。

步骤 2 确保所有 jQuery UI 对话框都已在任何 KO 绑定之前初始化。这样 jQuery UI 已经移动了 DOM 元素,因此您不必担心它们在 applyBindings 中间移动。初始化代码仍然按原样工作(除了删除 setTimeout 之外),因为dialog()函数只会更新现有的对话框(如果已经初始化)。

我需要这个的一个例子是由于我用来更新对话框标题的自定义绑定:

ko.bindingHandlers.jqDialogTitle = {
    update: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        $(element).dialog('option', 'title', value);
    }
};

我为此使用单独的绑定而不是主对话框绑定的更新函数,因为我只想更新标题,不是其他属性,例如高度和宽度(不希望对话框仅仅因为我更改标题而调整大小)。我想我也可以使用更新并仅删除高度/宽度,但现在我可以同时执行两者/任一操作,而不必担心 setTimeout 是否完成。

Adding this here because this is what most people find when searching on issues with jQuery UI Dialog and Knockout JS.

Just another option to avoid the "double binding" issue explained in the above answer. For me, the setTimeout() was causing other bindings to fail that require the dialog to be initialized already. The simple solution that worked for me was making the following changes to the accepted answer:

  1. Add class='dialog' to any elements using the custom dialog binding.

  2. Call this after page load, but before calling ko.applyBindings():

    $('.dialog').dialog({autoOpen: false});

Remove the setTimeout inside the init of the custom binding, and just call the code directly.

Step 2 makes sure that any jQuery UI Dialogs have been initialized prior to any KO bindings. This way jQuery UI has already moved the DOM elements, so that you don't have to worry about them moving in the middle of applyBindings. The init code still works as-is (other than removing setTimeout) because the dialog() function will just update an existing dialog if already initialized.

An example of why I needed this is due to a custom binding I use to update the title of the dialog:

ko.bindingHandlers.jqDialogTitle = {
    update: function(element, valueAccessor) {
        var value = ko.utils.unwrapObservable(valueAccessor());
        $(element).dialog('option', 'title', value);
    }
};

I use a separate binding for this instead of the update function for the main dialog binding, because I only want to update the title, not other properties such as height and width (don't want the dialog to resize just because I change the title). I suppose I could also use update and just remove height/width, but now I can do both/either and not worry about the setTimeout being completed or not.

把人绕傻吧 2024-12-29 17:00:07

这是出色的 RP Niemeyer 绑定处理程序的变体,对于不同的场景很有用。

要允许实体的版本,您可以创建一个带有版本控件的

,并使用 with 绑定,这取决于专门为实体创建的可观察对象版。

例如,要允许编辑 person,您可以像 editedPerson 一样创建和观察,并创建一个带有编辑控件的 div,并使用如下绑定

data-bind="with: editedPerson"

:一个人对可观察者来说就像这样:

vm.editedPerson(personToEdit);

绑定使 div 可见。当你完成编辑后,你可以将 observable 设置为 null,就像这样

vm.editedPerson(null);

div 将关闭。

我对 RP Niemeyer 的 bindingHandler 的变体允许在 jQuery UI 对话框中自动显示此 div。要使用它,您只需保留原始 with 绑定,并指定 jQuery UI 对话框选项,如下所示:

data-bind="with: editedPerson, withDialog: {/* jQuery UI dialog options*/}"

您可以获取我的绑定处理程序的代码,并在此处查看它的实际操作:

http://jsfiddle.net/jbustos/dBLeg/

您可以轻松修改此代码以使对话框具有不同的默认值,甚至让这些默认情况下,可以通过将处理程序封装在 js 模块中并添加公共配置函数来修改它来进行配置。 (您可以将此函数添加到绑定处理程序中,它将继续工作)。

// Variation on Niemeyer's http://jsfiddle.net/rniemeyer/SnPdE/

/*
This binding works in a simple way:
1) bind an observable using "with" binding
2) set the dialog options for the ui dialog using "withDialog" binding (as you'd do with an standard jquery UI dialog) Note that you can specify a "close" function in the options of the dialog an it will be invoked when the dialog closes.

Once this is done:
- when the observable is set to null, the dialog closes
- when the observable is set to something not null, the dialog opens
- when the dialog is cancelled (closed with the upper right icon), the binded observable is closed

Please, note that you can define the defaults for your binder. I recommend setting here the modal state, and the autoOpen to false.

*/

ko.bindingHandlers.withDialog = {
        init: function(element, valueAccessor, allBindingsAccessor) {
            var defaults = {
                modal: false,
                autoOpen: false,
            };
            var options = ko.utils.unwrapObservable(valueAccessor());
            //do in a setTimeout, so the applyBindings doesn't bind twice from element being copied and moved to bottom
            $.extend(defaults, options)
            setTimeout(function() { 
                var oldClose = options.close;
                defaults.close = function() {
                    if (options.close) options.close();
                    allBindingsAccessor().with(null);                        
                };
                
                $(element).dialog(defaults);          
            }, 0);
            
            //handle disposal (not strictly necessary in this scenario)
             ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
                 $(element).dialog("destroy");
             });   
        },
        update: function(element, valueAccessor, allBindingsAccessor) {
            var shouldBeOpen = ko.utils.unwrapObservable(allBindingsAccessor().with),
                $el = $(element),
                dialog = $el.data("uiDialog") || $el.data("dialog");
            
            //don't call open/close before initilization
            if (dialog) {
                $el.dialog(shouldBeOpen ? "open" : "close");
            }  
        }
};
    
var person = function() {
    this.name = ko.observable(),
    this.age = ko.observable()
}

var viewModel = function() {
    label= ko.observable('dialog test');
    editedPerson= ko.observable(null);
    clearPerson= function() {
       editedPerson(null);
    };
    newPerson= function() {
        editedPerson(new person());
    };
    savePerson= function() {
        alert('Person saved!');
        clearPerson();
    };
    return {
        label: label,
        editedPerson: editedPerson,
        clearPerson: clearPerson,
        newPerson: newPerson,
        savePerson: savePerson,
    };
}


var vm = viewModel();

ko.applyBindings(vm);
.header {
    font-size: 16px;
    font-family: sans-serif;
    font-weight: bold;
    margin-bottom: 20px;
}
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/themes/base/jquery-ui.css" rel="stylesheet"/>
<h1 class="header" data-bind="text: label"></h1>

<div id="dialog" data-bind="with: editedPerson, withDialog: {autoOpen: false, title: 'Dialog test', close: function() { alert('closing');} }">
    Person editor<br/>
    Name:<br/><input type="text" data-bind="value: $data.name"/><br/>
    Age:<br/><input type="text" data-bind="value: $data.age"/><br/>
    <button data-bind="click: $parent.savePerson">Ok</button>
    <button data-bind="click: $parent.clearPerson">Cancel</button>
</div>

<div>
    <button data-bind="click: clearPerson">Clear person</button>
    <button data-bind="click: newPerson">New person</button>
</div>

<hr/>

<div data-bind="text: ko.toJSON($root)"></div>

This is a variation of the great RP Niemeyer binding handler, which is useful for a differente scenario.

To allow the edition of an entity, you can create a <div> with edition controls, and use a with binding, which depends on an observable made on purpose for the edition.

For example, to allow the edition of a person, you can create and observable like editedPerson, and create a div with edition controls, with a binding like this:

data-bind="with: editedPerson"

When you add a person to the observable lke so:

vm.editedPerson(personToEdit);

the binding makes the div visible. When you finish the edition, you can set the observable to null, like so

vm.editedPerson(null);

and the div will close.

My variation of RP Niemeyer's bindingHandler allows to automatically show this div inside a jQuery UI dialog. To use it you simply have to keep the original with binding, and specify the jQuery UI dialog options like so:

data-bind="with: editedPerson, withDialog: {/* jQuery UI dialog options*/}"

You can get the code of my binding handler, and see it in action here:

http://jsfiddle.net/jbustos/dBLeg/

You can modify this code easily to have different defaults for the dialog, and even to make these defaults configurable by enclosing the handler in a js module, and adding a public configuration function to modify it. (You can add this function to the binding handler, and it will keep working).

// Variation on Niemeyer's http://jsfiddle.net/rniemeyer/SnPdE/

/*
This binding works in a simple way:
1) bind an observable using "with" binding
2) set the dialog options for the ui dialog using "withDialog" binding (as you'd do with an standard jquery UI dialog) Note that you can specify a "close" function in the options of the dialog an it will be invoked when the dialog closes.

Once this is done:
- when the observable is set to null, the dialog closes
- when the observable is set to something not null, the dialog opens
- when the dialog is cancelled (closed with the upper right icon), the binded observable is closed

Please, note that you can define the defaults for your binder. I recommend setting here the modal state, and the autoOpen to false.

*/

ko.bindingHandlers.withDialog = {
        init: function(element, valueAccessor, allBindingsAccessor) {
            var defaults = {
                modal: false,
                autoOpen: false,
            };
            var options = ko.utils.unwrapObservable(valueAccessor());
            //do in a setTimeout, so the applyBindings doesn't bind twice from element being copied and moved to bottom
            $.extend(defaults, options)
            setTimeout(function() { 
                var oldClose = options.close;
                defaults.close = function() {
                    if (options.close) options.close();
                    allBindingsAccessor().with(null);                        
                };
                
                $(element).dialog(defaults);          
            }, 0);
            
            //handle disposal (not strictly necessary in this scenario)
             ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
                 $(element).dialog("destroy");
             });   
        },
        update: function(element, valueAccessor, allBindingsAccessor) {
            var shouldBeOpen = ko.utils.unwrapObservable(allBindingsAccessor().with),
                $el = $(element),
                dialog = $el.data("uiDialog") || $el.data("dialog");
            
            //don't call open/close before initilization
            if (dialog) {
                $el.dialog(shouldBeOpen ? "open" : "close");
            }  
        }
};
    
var person = function() {
    this.name = ko.observable(),
    this.age = ko.observable()
}

var viewModel = function() {
    label= ko.observable('dialog test');
    editedPerson= ko.observable(null);
    clearPerson= function() {
       editedPerson(null);
    };
    newPerson= function() {
        editedPerson(new person());
    };
    savePerson= function() {
        alert('Person saved!');
        clearPerson();
    };
    return {
        label: label,
        editedPerson: editedPerson,
        clearPerson: clearPerson,
        newPerson: newPerson,
        savePerson: savePerson,
    };
}


var vm = viewModel();

ko.applyBindings(vm);
.header {
    font-size: 16px;
    font-family: sans-serif;
    font-weight: bold;
    margin-bottom: 20px;
}
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/2.2.1/knockout-min.js"></script>
<link href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/themes/base/jquery-ui.css" rel="stylesheet"/>
<h1 class="header" data-bind="text: label"></h1>

<div id="dialog" data-bind="with: editedPerson, withDialog: {autoOpen: false, title: 'Dialog test', close: function() { alert('closing');} }">
    Person editor<br/>
    Name:<br/><input type="text" data-bind="value: $data.name"/><br/>
    Age:<br/><input type="text" data-bind="value: $data.age"/><br/>
    <button data-bind="click: $parent.savePerson">Ok</button>
    <button data-bind="click: $parent.clearPerson">Cancel</button>
</div>

<div>
    <button data-bind="click: clearPerson">Clear person</button>
    <button data-bind="click: newPerson">New person</button>
</div>

<hr/>

<div data-bind="text: ko.toJSON($root)"></div>

铁憨憨 2024-12-29 17:00:07

现在 这个库 包含 KnockoutJS 的所有 JQueryUI 绑定,包括当然,还有对话框小部件。

There's now this library that has all the JQueryUI bindings for KnockoutJS including, of course, the dialog widget.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文