javascript dialog knockout.js modal-dialog late-binding

javascript - knockout.js-databinding diferido para modal?



dialog modal-dialog (3)

Estoy usando knockout.js para mostrar una lista de empleados. Tengo un único marcado modal oculto en la página. Cuando se hace clic en el botón "detalles" para un solo empleado, quiero vincular los datos de ese empleado a la ventana emergente modal. Estoy usando ko.applyBindings (empleado, elemento) pero el problema es que cuando la página se carga, se espera que el modal comience por algo.

Entonces me pregunto, ¿hay un truco / estrategia para hacer un enlace de datos tarde / diferido? Busqué enlaces virtuales, pero la documentación no fue lo suficientemente útil.

¡Gracias!


Crearía otro observable que envuelva al empleado.

this.detailedEmployee = ko.observable({}), var self = this; this.showDetails = function(employee){ self.detailedEmployee(employee); $("#dialog").dialog("show"); //or however your dialog works }

Adjunte el clic para showDetails . Entonces puedes simplemente llamar a applyBindings en la carga de la página.


El JSFiddle vinculado a en la respuesta proporcionada por @Romanych ya no parecía funcionar.

Entonces, construí mi propio ejemplo (basado en su violín original ) con soporte CRUD completo y validación básica usando Bootstrap 3 y la biblioteca Bootstrap Modal : https://jsfiddle.net/BitWiseGuy/4u5egybp/

Controladores de enlace personalizados

ko.bindingHandlers[''modal''] = { init: function(element, valueAccessor, allBindingsAccessor) { var allBindings = allBindingsAccessor(); var $element = $(element); $element.addClass(''hide modal''); if (allBindings.modalOptions && allBindings.modalOptions.beforeClose) { $element.on(''hide'', function() { var value = ko.utils.unwrapObservable(valueAccessor()); return allBindings.modalOptions.beforeClose(value); }); } }, update: function(element, valueAccessor) { var value = ko.utils.unwrapObservable(valueAccessor()); if (value) { $(element).removeClass(''hide'').modal(''show''); } else { $(element).modal(''hide''); } } };

Ejemplo de uso

La vista

<div data-bind="modal: UserBeingEdited" class="fade" role="dialog" tabindex="-1"> <form data-bind="submit: $root.SaveUser"> <div class="modal-header"> <a class="close" data-dismiss="modal">×</a> <h3>User Details</h3> </div> <div class="modal-body"> <div class="form-group"> <label for="NameInput">Name</label> <input type="text" class="form-control" id="NameInput" placeholder="User''s name" data-bind="value: UserBeingEdited() && UserBeingEdited().Name, valueUpdate: ''afterkeydown''"> </div> <div class="form-group"> <label for="AgeInput">Age</label> <input type="text" class="form-control" id="AgeInput" placeholder="User''s age" data-bind="value: UserBeingEdited() && UserBeingEdited().Age, valueUpdate: ''afterkeydown''"> </div> <!-- ko if: ValidationErrors() && ValidationErrors().length > 0 --> <div class="alert alert-danger" style="margin: 20px 0 0"> Please correct the following errors: <ul data-bind="foreach: { data: ValidationErrors, as: ''errorMessage'' }"> <li data-bind="text: errorMessage"></li> </ul> </div> <!-- /ko --> </div> <div class="modal-footer"> <button type="button" data-dismiss="modal" class="btn btn-default">Cancel</button> <button type="submit" class="btn btn-primary">Save Changes</button> </div> </form> </div>

The ViewModel

/* ViewModel for the individual records in our collection. */ var User = function(name, age) { var self = this; self.Name = ko.observable(ko.utils.unwrapObservable(name)); self.Age = ko.observable(ko.utils.unwrapObservable(age)); } /* The page''s main ViewModel. */ var ViewModel = function() { var self = this; self.Users = ko.observableArray(); self.ValidationErrors = ko.observableArray([]); // Logic to ensure that user being edited is in a valid state self.ValidateUser = function(user) { if (!user) { return false; } var currentUser = ko.utils.unwrapObservable(user); var currentName = ko.utils.unwrapObservable(currentUser.Name); var currentAge = ko.utils.unwrapObservable(currentUser.Age); self.ValidationErrors.removeAll(); // Clear out any previous errors if (!currentName) self.ValidationErrors.push("The user''s name is required."); if (!currentAge) { self.ValidationErrors.push("Please enter the user''s age."); } else { // Just some arbitrary checks here... if (Number(currentAge) == currentAge && currentAge % 1 === 0) { // is a whole number if (currentAge < 2) { self.ValidationErrors.push("The user''s age must be 2 or greater."); } else if (currentAge > 99) { self.ValidationErrors.push("The user''s age must be 99 or less."); } } else { self.ValidationErrors.push("Please enter a valid whole number for the user''s age."); } } return self.ValidationErrors().length <= 0; }; // The instance of the user currently being edited. self.UserBeingEdited = ko.observable(); // Used to keep a reference back to the original user record being edited self.OriginalUserInstance = ko.observable(); self.AddNewUser = function() { // Load up a new user instance to be edited self.UserBeingEdited(new User()); self.OriginalUserInstance(undefined); }; self.EditUser = function(user) { // Keep a copy of the original instance so we don''t modify it''s values in the editor self.OriginalUserInstance(user); // Copy the user data into a new instance for editing self.UserBeingEdited(new User(user.Name, user.Age)); }; // Save the changes back to the original instance in the collection. self.SaveUser = function() { var updatedUser = ko.utils.unwrapObservable(self.UserBeingEdited); if (!self.ValidateUser(updatedUser)) { // Don''t allow users to save users that aren''t valid return false; } var userName = ko.utils.unwrapObservable(updatedUser.Name); var userAge = ko.utils.unwrapObservable(updatedUser.Age); if (self.OriginalUserInstance() === undefined) { // Adding a new user self.Users.push(new User(userName, userAge)); } else { // Updating an existing user self.OriginalUserInstance().Name(userName); self.OriginalUserInstance().Age(userAge); } // Clear out any reference to a user being edited self.UserBeingEdited(undefined); self.OriginalUserInstance(undefined); } // Remove the selected user from the collection self.DeleteUser = function(user) { if (!user) { return falase; } var userName = ko.utils.unwrapObservable(ko.utils.unwrapObservable(user).Name); // We could use another modal here to display a prettier dialog, but for the // sake of simplicity, we''re just using the browser''s built-in functionality. if (confirm(''Are you sure that you want to delete '' + userName + ''?'')) { // Find the index of the current user and remove them from the array var index = self.Users.indexOf(user); if (index > -1) { self.Users.splice(index, 1); } } }; }

Inicializando Knockout con la Vista y el ViewModel

var viewModel = new ViewModel(); // Populate the ViewModel with some dummy data for (var i = 1; i <= 10; i++) { var letter = String.fromCharCode(i + 64); var userName = ''User '' + letter; var userAge = i * 2; viewModel.Users.push(new User(userName, userAge)); } // Let Knockout do its magic! ko.applyBindings(viewModel);


Me gustaría proponer una forma diferente de trabajar con modales en MVVVM. En MVVM, ViewModel es información para la Vista, y View es responsable de la UI. Si examinamos esta propuesta:

this.detailedEmployee = ko.observable({}), var self = this; this.showDetails = function(employee){ self.detailedEmployee(employee); $("#dialog").dialog("show"); //or however your dialog works }

Estoy totalmente de acuerdo con this.detailedEmployee = ko.observable({}) , pero estoy en this.detailedEmployee = ko.observable({}) desacuerdo con esta línea: $("#dialog").dialog("show"); . Este código se coloca en ViewModel y muestra la ventana modal, en la que es responsabilidad de View, por lo que nos equivocamos con el enfoque de MVVM. Diría que este código resolverá su tarea actual, pero podría causar muchos problemas en el futuro.

  • Al cerrar la ventana emergente, debe establecer DetailEmployee en undefined para tener su ViewModel principal en un estado coherente.
  • Al cerrar la ventana emergente, es posible que desee tener validación y la posibilidad de descartar la operación de cierre cuando desee utilizar el componente de otro modal en la aplicación

En cuanto a mí, estos puntos son muy críticos, por lo que me gustaría proponer una forma diferente. Si "olvidamos" que necesita mostrar los datos en una ventana emergente, el enlace podría resolver su problema.

this.detailedEmployee = ko.observable(undefined); var self = this; this.showDetails = function(employee){ self.detailedEmployee(employee); } <div data-bind="with: detailedEmployee"> Data to show </div>

Como puede ver, su ViewModel no sabe nada sobre cómo deben mostrarse los datos. Solo conoce los datos que deben mostrarse. El enlace with mostrará el contenido solo cuando se define el detalleEmployee. A continuación, deberíamos encontrar un enlace similar a pero que muestre contenido en el menú emergente. Vamos a darle el nombre modal . Su código es así:

ko.bindingHandlers[''modal''] = { init: function(element) { $(element).modal(''init''); return ko.bindingHandlers[''with''].init.apply(this, arguments); }, update: function(element, valueAccessor) { var value = ko.utils.unwrapObservable(valueAccessor()); var returnValue = ko.bindingHandlers[''with''].update.apply(this, arguments); if (value) { $(element).modal(''show''); } else { $(element).modal(''hide''); } return returnValue; } };

Como puede ver, usa el plugin with interno, y muestra u oculta una ventana emergente dependiendo del valor pasado al enlace. Si está definido, ''mostrar''. Si no, ''ocultar''. Su uso será el mismo que con:

<div data-bind="modal: detailedEmployee"> Data to show </div>

Lo único que debes hacer es usar tu complemento de modales favorito. Preparé un ejemplo con el componente emergente Twitter Bootstrap: http://jsfiddle.net/euvNr/embedded/result/

En este ejemplo, el enlace personalizado es un poco más poderoso; puede suscribirse al evento onBeforeClose y cancelar este evento si es necesario. Espero que esto ayude.