jquery-ui knockout.js

evento de cambio jQuery UI datepicker no captado por KnockoutJS



jquery-ui knockout.js (13)

Estoy tratando de usar KnockoutJS con jQuery UI. Tengo un elemento de entrada con un marcador de fecha adjunto. Actualmente estoy ejecutando knockout.debug.1.2.1.js y parece que el evento de cambio nunca será atrapado por Knockout. El elemento se ve así:

<input type="text" class="date" data-bind="value: RedemptionExpiration"/>

Incluso he intentado cambiar el tipo de evento valueUpdate pero fue en vano. Parece que Chrome provoca un evento de focus justo antes de que cambie el valor, pero IE no lo hace.

¿Hay algún método Knockout que "vuelva a enlazar todas las vinculaciones"? Técnicamente, solo necesito que se modifique el valor antes de volver a enviarlo al servidor. Entonces podría vivir con ese tipo de solución.

Creo que el problema es la culpa del datepicker, pero no puedo resolver cómo solucionarlo.

¿Algunas ideas?


Alternativamente, puede especificar esto en el enlace:

Actualizar:

function (element, valueAccessor) { var value = ko.utils.unwrapObservable(valueAccessor()), current = $(element).datepicker("getDate"); if (typeof value === "string") { var dateValue = new Date(value); if (dateValue - current !== 0) $(element).datepicker("setDate", dateValue); } }


Aquí hay una versión de la respuesta de RP Niemeyer que funcionará con los scripts de validación knockout que se encuentran aquí: http://github.com/ericmbarnard/Knockout-Validation

ko.bindingHandlers.datepicker = { init: function (element, valueAccessor, allBindingsAccessor) { //initialize datepicker with some optional options var options = allBindingsAccessor().datepickerOptions || {}; $(element).datepicker(options); //handle the field changing ko.utils.registerEventHandler(element, "change", function () { var observable = valueAccessor(); observable($(element).val()); if (observable.isValid()) { observable($(element).datepicker("getDate")); $(element).blur(); } }); //handle disposal (if KO removes by the template binding) ko.utils.domNodeDisposal.addDisposeCallback(element, function () { $(element).datepicker("destroy"); }); ko.bindingHandlers.validationCore.init(element, valueAccessor, allBindingsAccessor); }, update: function (element, valueAccessor) { var value = ko.utils.unwrapObservable(valueAccessor()); //handle date data coming via json from Microsoft if (String(value).indexOf(''/Date('') == 0) { value = new Date(parseInt(value.replace(///Date/((.*?)/)///gi, "$1"))); } current = $(element).datepicker("getDate"); if (value - current !== 0) { $(element).datepicker("setDate", value); } } };

Los cambios consisten en que el manejador de eventos de cambio pase primero el valor ingresado y no la fecha a los scripts de validación, y luego solo establece la fecha en el valor observable si es válido. También agregué el validationCore.init que se necesita para los enlaces personalizados que se tratan aquí:

http://github.com/ericmbarnard/Knockout-Validation/issues/69

También agregué la sugerencia de rpenrose sobre la falta de claridad en el cambio para eliminar algunos escenarios molestos que se ponen en el camino de las cosas.


Aunque todas estas respuestas me ahorraron mucho trabajo, ninguna de ellas me funcionó por completo. Después de seleccionar una fecha, el valor encuadernado no se actualizará. Solo pude actualizarlo al cambiar el valor de la fecha usando el teclado y luego hacer clic fuera del cuadro de entrada. Lo arreglé al aumentar el código de RP Niemeyer con el código de syb para obtener:

ko.bindingHandlers.datepicker = { init: function (element, valueAccessor, allBindingsAccessor) { //initialize datepicker with some optional options var options = allBindingsAccessor().datepickerOptions || {}; var funcOnSelectdate = function () { var observable = valueAccessor(); observable($(element).datepicker("getDate")); } options.onSelect = funcOnSelectdate; $(element).datepicker(options); //handle the field changing ko.utils.registerEventHandler(element, "change", funcOnSelectdate); //handle disposal (if KO removes by the template binding) ko.utils.domNodeDisposal.addDisposeCallback(element, function () { $(element).datepicker("destroy"); }); }, update: function (element, valueAccessor) { var value = ko.utils.unwrapObservable(valueAccessor()); if (typeof(value) === "string") { // JSON string from server value = value.split("T")[0]; // Removes time } var current = $(element).datepicker("getDate"); if (value - current !== 0) { var parsedDate = $.datepicker.parseDate(''yy-mm-dd'', value); $(element).datepicker("setDate", parsedDate); } } };

Sospecho que poner el observable ($ (element) .datepicker ("getDate")); declaración en su propia función y registrar eso con options.onSelect hizo el truco?


Basado en la solución de Ryan, myDate devuelve la cadena de fecha estándar, que no es la ideal en mi caso. Usé date.js para analizar el valor, por lo que siempre devolverá el formato de fecha que desee. Eche un vistazo a este ejemplo Ejemplo de violín .

update: function(element, valueAccessor) { var value = ko.utils.unwrapObservable(valueAccessor()), current = $(element).datepicker("getDate"); var d = Date.parse(value); if (value - current !== 0) { $(element).datepicker("setDate", d.toString("MM/dd/yyyy")); } }


Creo que para jQuery UI datepicker es preferible usar un enlace personalizado que leerá / escribirá con objetos Date usando las API provistas por el datepicker.

El enlace podría ser similar (de mi respuesta here ):

ko.bindingHandlers.datepicker = { init: function(element, valueAccessor, allBindingsAccessor) { //initialize datepicker with some optional options var options = allBindingsAccessor().datepickerOptions || {}, $el = $(element); $el.datepicker(options); //handle the field changing by registering datepicker''s changeDate event ko.utils.registerEventHandler(element, "changeDate", function () { var observable = valueAccessor(); observable($el.datepicker("getDate")); }); //handle disposal (if KO removes by the template binding) ko.utils.domNodeDisposal.addDisposeCallback(element, function() { $el.datepicker("destroy"); }); }, update: function(element, valueAccessor) { var value = ko.utils.unwrapObservable(valueAccessor()), $el = $(element); //handle date data coming via json from Microsoft if (String(value).indexOf(''/Date('') == 0) { value = new Date(parseInt(value.replace(///Date/((.*?)/)///gi, "$1"))); } var current = $el.datepicker("getDate"); if (value - current !== 0) { $el.datepicker("setDate", value); } } };

Lo usarías como:

<input data-bind="datepicker: myDate, datepickerOptions: { minDate: new Date() }" />

Muestra en jsFiddle aquí: http://jsfiddle.net/rniemeyer/NAgNV/


Creo que se puede hacer mucho más fácil: <input data-bind="value: myDate, datepicker: myDate, datepickerOptions: {}" />

Por lo tanto, no necesita el cambio manual en la función init.

Pero en este caso, su variable ''myDate'' obtendrá solo el valor visible, no el objeto Date.


El uso de enlaces personalizados proporcionados en respuestas anteriores no siempre es posible. Llamar a $(element).datepicker(...) lleva bastante tiempo, y si tiene, por ejemplo, unas pocas docenas, o incluso cientos de elementos para llamar a este método, tiene que hacerlo "flojo", es decir, Bajo demanda.

Por ejemplo, el modelo de vista se puede inicializar, las input se insertan en el DOM, pero los selectores de fecha correspondientes solo se inicializarán cuando un usuario haga clic en ellos.

Entonces, aquí está mi solución:

Agregue un enlace personalizado que permita adjuntar datos arbitrarios a un nodo:

KO.bindingHandlers.boundData = { init: function(element, __, allBindings) { element.boundData = allBindings.get(''boundData''); } };

Use el enlace para mostrar el observable utilizado para el valor de la input :

<input type=''text'' class=''my-date-input'' data-bind=''textInput: myObservable, boundData: myObservable'' />

Y finalmente, al inicializar el selector de fecha, utilícelo en la opción onSelect :

$(''.my-date-input'').datepicker({ onSelect: function(dateText) { this.myObservable(dateText); } //Other options });

De esta forma, cada vez que un usuario cambia la fecha con el selector de fecha, también se actualiza el correspondiente observador Knockout.


Gracias por este artículo, lo encontré muy útil.

Si desea que el DatePicker se comporte exactamente como el comportamiento predeterminado de JQuery UI, le recomiendo que agregue un desenfoque al elemento en el manejador de eventos change:

es decir

//handle the field changing ko.utils.registerEventHandler(element, "change", function () { var observable = valueAccessor(); observable($(element).datepicker("getDate")); $(element).blur(); });


He usado un enfoque diferente. Como knockout.js no parece activar el evento en el cambio, forcé al selector de fechas a llamar a change () para su entrada una vez cerrado.

$(".date").datepicker({ onClose: function() { $(this).change(); // Forces re-validation } });


Igual que RP Niemeyer, pero una mejor compatibilidad con WCF DateTime, Timezones y Using the DatePicker onSelect JQuery.

ko.bindingHandlers.datepicker = { init: function (element, valueAccessor, allBindingsAccessor) { //initialize datepicker with some optional options var options = allBindingsAccessor().datepickerOptions || {}; var funcOnSelectdate = function () { var observable = valueAccessor(); var d = $(element).datepicker("getDate"); var timeInTicks = d.getTime() + (-1 * (d.getTimezoneOffset() * 60 * 1000)); observable("/Date(" + timeInTicks + ")/"); } options.onSelect = funcOnSelectdate; $(element).datepicker(options); //handle the field changing ko.utils.registerEventHandler(element, "change", funcOnSelectdate); //handle disposal (if KO removes by the template binding) ko.utils.domNodeDisposal.addDisposeCallback(element, function () { $(element).datepicker("destroy"); }); }, update: function (element, valueAccessor) { var value = ko.utils.unwrapObservable(valueAccessor()); //handle date data coming via json from Microsoft if (String(value).indexOf(''/Date('') == 0) { value = new Date(parseInt(value.replace(///Date/((.*?)/)///gi, "$1"))); } current = $(element).datepicker("getDate"); if (value - current !== 0) { $(element).datepicker("setDate", value); } } };

Disfruta :)

http://jsfiddle.net/yechezkelbr/nUdYH/


Necesitaba actualizar varias veces mis datos del servidor, pero no terminó el trabajo para mis necesidades compartidas a continuación (mi formato de fecha / fecha (1224043200000) /):

//Object Model function Document(data) { if (String(data.RedemptionExpiration).indexOf(''/Date('') == 0) { var newDate = new Date(parseInt(data.BDate.replace(///Date/((.*?)/)///gi, "$1"))); data.RedemptionExpiration = (newDate.getMonth()+1) + "/" + newDate.getDate() + "/" + newDate.getFullYear(); } this.RedemptionExpiration = ko.observable(data.RedemptionExpiration); } //View Model function DocumentViewModel(){ ///additional code removed self.afterRenderLogic = function (elements) { $("#documentsContainer .datepicker").each(function () { $(this).datepicker(); }); }; }

Después de formatear correctamente el modelo para la salida, agregué una plantilla con la documentación knockoutjs :

<div id="documentsContainer"> <div data-bind="template: { name: ''document-template'', foreach: documents, afterRender: afterRenderLogic }, visible: documents().length > 0"></div> </div> //Inline template <script type="text/html" id="document-template"> <input data-bind="value: RedemptionExpiration" class="datepicker" /> </script>


Pocas personas pidieron opciones dinámicas de datepicker. En mi caso, necesitaba un rango de fechas dinámico, por lo que la primera entrada define el valor mínimo del segundo y el segundo establece el valor máximo para el primero. Lo resolví extendiendo el controlador de RP Niemeyer. Entonces a su original:

ko.bindingHandlers.datepicker = { init: function(element, valueAccessor, allBindingsAccessor) { //initialize datepicker with some optional options var options = allBindingsAccessor().datepickerOptions || {}, $el = $(element); $el.datepicker(options); //handle the field changing by registering datepicker''s changeDate event ko.utils.registerEventHandler(element, "change", function() { var observable = valueAccessor(); observable($el.datepicker("getDate")); }); //handle disposal (if KO removes by the template binding) ko.utils.domNodeDisposal.addDisposeCallback(element, function() { $el.datepicker("destroy"); }); }, update: function(element, valueAccessor) { var value = ko.utils.unwrapObservable(valueAccessor()), $el = $(element); //handle date data coming via json from Microsoft if (String(value).indexOf(''/Date('') == 0) { value = new Date(parseInt(value.replace(///Date/((.*?)/)///gi, "$1"))); } var current = $el.datepicker("getDate"); if (value - current !== 0) { $el.datepicker("setDate", value); } } };

He agregado dos controladores más que corresponden a las opciones que quería modificar:

ko.bindingHandlers.minDate = { update: function(element, valueAccessor) { var value = ko.utils.unwrapObservable(valueAccessor()), current = $(element).datepicker("option", "minDate", value); } }; ko.bindingHandlers.maxDate = { update: function(element, valueAccessor) { var value = ko.utils.unwrapObservable(valueAccessor()), current = $(element).datepicker("option", "maxDate", value); } };

Y los usé así en mi plantilla:

<input data-bind="datepicker: selectedLowerValue, datepickerOptions: { minDate: minValue()}, maxDate: selectedUpperValue" /> <input data-bind="datepicker: selectedUpperValue, datepickerOptions: { maxDate: maxValue()}, minDate: selectedLowerValue" />


Resolví este problema cambiando el orden de mis archivos de script incluidos:

<script src="@Url.Content("~/Scripts/jquery-ui-1.10.2.custom.js")"></script> <script src="@Url.Content("~/Scripts/knockout-2.2.1.js")"></script>