integrando el diálogo de jquery ui con knockoutjs

Intento crear enlaces knockoutjs para los diálogos de jquery ui, y no puedo abrir el cuadro de diálogo. El elemento de diálogo se crea correctamente, pero parece tener display: none ese dialog('open') llamada dialog('open') no se elimina. Además, la llamada al dialog('isOpen') de dialog('isOpen') devuelve el objeto de diálogo en lugar de un booleano.

Estoy usando los últimos knockoutjs y jquery 1.4.4 con jquery ui 1.8.7. También lo probé con jQuery 1.7.1 con los mismos resultados. Aquí está mi HTML:

 

foo dialog

y este es el 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); 

He configurado un JSFiddle que reproduce el problema.

Me pregunto si esto tiene algo que ver con knockoutjs y manejo de eventos. Intenté devolver true desde el controlador de clics, pero eso no pareció afectar nada.

Parece que escribir en el widget a .data (“diálogo”) y luego tratar de operarlo está causando un problema. Aquí hay una muestra donde no se usa .data y se llama a abrir / cerrar en función del elemento: http://jsfiddle.net/rniemeyer/durKS/

Alternativamente, me gusta trabajar con el diálogo de una manera ligeramente diferente. Me gusta controlar si el diálogo está abierto o cerrado mediante el uso de un observable. Entonces, usaría un enlace único en el diálogo mismo. El init inicializaría el diálogo, mientras que la update verificaría un observable para ver si debería abrir o cerrar. Ahora, los botones de apertura / cierre solo necesitan alternar un observable booleano en lugar de preocuparse por los identificadores o localizar el diálogo real.

 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"); } } }; 

Usado como:

 
foo dialog

Aquí hay una muestra: http://jsfiddle.net/rniemeyer/SnPdE/

Hice un pequeño cambio en la respuesta de RP Niemeyer para permitir que las opciones del diálogo sean observables

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

Obtenga los valores observables con ko.toJS para inicializar el widget

 setTimeout(function() { options.close = function() { allBindingsAccessor().dialogVisible(false); }; $(element).dialog(ko.toJS(options)); }, 0); 

y verificar si hay observaciones en la actualización

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

Agregando esto aquí porque esto es lo que la mayoría de la gente encuentra cuando busca problemas con jQuery UI Dialog y Knockout JS.

Solo otra opción para evitar el problema de “doble enlace” explicado en la respuesta anterior. Para mí, el setTimeout () causaba que fallaran otros enlaces que requieren que el diálogo se inicialice. La solución simple que funcionó para mí fue hacer los siguientes cambios a la respuesta aceptada:

  1. Agregue class = ‘dialog’ a cualquier elemento utilizando el enlace de diálogo personalizado.

  2. Llámalo después de cargar la página, pero antes de llamar a ko.applyBindings ():

    $ (‘. dialog’). dialog ({autoOpen: false});

Elimine setTimeout dentro del init del enlace personalizado, y simplemente llame al código directamente.

El paso 2 asegura que todos los cuadros de diálogo de jQuery UI se hayan inicializado antes de cualquier enlace de KO. De esta forma, jQuery UI ya ha movido los elementos DOM, para que no tenga que preocuparse de que se muevan en medio de applyBindings. El código de inicio todavía funciona tal como está (además de eliminar setTimeout) porque la función dialog () simplemente actualizará un cuadro de diálogo existente si ya se ha inicializado.

Un ejemplo de por qué lo necesitaba es debido a un enlace personalizado que utilizo para actualizar el título del diálogo:

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

Utilizo un enlace separado para esto en lugar de la función de actualización para el enlace de diálogo principal, porque solo quiero actualizar el título, no otras propiedades como altura y ancho (no quiero que el diálogo cambie de tamaño solo porque cambio el título ) Supongo que también podría usar la actualización y simplemente eliminar la altura / ancho, pero ahora puedo hacer las dos cosas y no preocuparme por la finalización o no de setTimeout.

Esta es una variación del gran controlador de enlace RP Niemeyer, que es útil para un escenario diferente.

Para permitir la edición de una entidad, puede crear un

con controles de edición, y usar a with enlace, que depende de un observable hecho a propósito para la edición.

Por ejemplo, para permitir la edición de una person , puede crear y observar como editedPerson , y crear un div con controles de edición, con un enlace como este:

 data-bind="with: editedPerson" 

Cuando agregas a una persona al lke observable entonces:

 vm.editedPerson(personToEdit); 

el enlace hace que el div visible. Cuando termine la edición, puede establecer que el observable sea nulo, como tal

 vm.editedPerson(null); 

y el div se cerrará

Mi variación del bindingHandler de RP Niemeyer permite mostrar automáticamente este div dentro de un diálogo de interfaz de usuario jQuery. Para usarlo, simplemente tiene que mantener el original with enlace y especificar las opciones de diálogo de la interfaz de usuario de jQuery de la siguiente manera:

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

Puede obtener el código de mi controlador de enlace y verlo en acción aquí:

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

Puede modificar este código fácilmente para tener diferentes valores predeterminados para el diálogo, e incluso para hacer que estos valores predeterminados sean configurables al encerrar el controlador en un módulo js y agregar una función de configuración pública para modificarlo. (Puede agregar esta función al controlador de enlace, y seguirá funcionando).

 // 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; } 
     

Person editor
Name:

Age:


Ahora hay esta biblioteca que tiene todos los enlaces JQueryUI para KnockoutJS, incluido, por supuesto, el widget de diálogo.