ejemplos ejemplo div javascript knockout.js

javascript - ejemplo - outerhtml



Cómo definir un ''enlace de opciones'' knockout personalizado con opciones predefinidas de Texto y Valor (4)

Un escenario típico en nuestro entorno es permitirle al usuario seleccionar una lista de opciones proporcionadas por el servidor (terminales, productos, ...), y luego presentar los datos solicitados.

Las opciones proporcionadas por el servidor están en Nombre, formato ID, y por lo tanto la siguiente construcción knockout se usa a menudo:

<select data-bind="options: serverOptions, optionsText: ''Name'', optionsValue: ''ID'', value: selectedOption>

Sería deseable crear un bindingHandler personalizado, llamado ''NamedIdOptions'' especificando optionsText y optionsValue en allBindingsAccessor () y luego redirigir al manejador de enlace de opciones estándar.

es decir

<select data-bind="NamedIdOptions: serverOptions, value: selectedOption"></select>

Previamente, he creado un controlador de enlace propio que completa las opciones yo mismo; sin embargo, preferiría usar el marco provisto por el manejador de enlace de opciones.

He intentado diferentes enfoques sin demasiado éxito: el enlace de opciones usa allBindings [''optionsValue''], y allBindings [''optionsText''] para acceder al valor, y parece que no tengo forma de configurarlos. (Me gustaría evitar el enfoque de applyBindingsToNode utilizado en y escribir algo como:

ko.bindingHandlers.NamedIdOptions = { init: function(element, valueAccessor, allBindingsAccessor, viewModel) { var allBindings = allBindingsAccessor(); allBindings.*FORCESET*("optionsText", "Name"); allBindings.*FORCESET*("optionsValue", "ID"); retun ko.bindingHandlers.options.init.apply(this, arguments); }, update: function (element, valueAccessor, allBindingsAccessor, viewModel) { retun ko.bindingHandlers.options.update.apply(this, arguments); } }

Sin embargo, parece que no tengo ninguna posibilidad de establecer nada en los allBindings.

No tengo permitido usar

allBindings[''_ko_property_writers''][''optionsText'']("Name" ); allBindings[''_ko_property_writers''][''optionsValue'']("ID" );

Realmente preferiría evitar applyBindingsToNode en el constructo init como

Knockout: ¿es posible combinar enlaces de selección estándar con un enlace personalizado?

¿Alguien ahora acerca de una solución simple al problema?


De acuerdo, terminé usando enlaces de aplicación al nodo de todos modos:

ko.bindingHandlers.NamedIdOptions = { init: function (element, valueAccessor, allBindingsAccessor, viewModel) { var allBindings = allBindingsAccessor(); var newBindingOptions = { options: allBindings.NamedIdOptions, optionsText: "Name", optionsValue: "ID" }; delete allBindings.NamedIdOptions; ko.utils.extend(newBindingOptions, allBindings); ko.applyBindingsToNode(element, newBindingOptions, viewModel); } };

Y parece funcionar como se esperaba, estoy un poco inseguro sobre el valor y lasOpciones seleccionadas que tienen ''después'' establecido en las opciones. ¿Supongo que estoy a salvo cuando NamedIdOptions está listo antes del enlace de valor?


¿No puedes simplemente falsificar todo el parámetro allBindingsAccessor al reenviar la llamada?

update: function (element, valueAccessor, allBindingsAccessor, viewModel) { var allBindings = allBindingsAccessor(), fakeAllBindingsAccessor = function () { // I''ve used jQuery.extend here, you could also manually add the properties to the allBindings object return $.extend(true, allBindings, { optionsValue: ''ID'', optionsText: ''Name'' }; }; return ko.bindingHandlers.options.init.call(this, element, valueAccessor, fakeAllBindingsAccessor, viewModel); }

Editar: se agregó más código para combinar el allBindingsAccessor existente con los enlaces falsos manuales


Considere usar ko.applyBindingAccessorsToNode. Así es como comencé a hacerlo en KO 3.0:

ko.bindingHandlers.NamedIdOptions = { init: function(element, valueAccessor, allBindingsAccessor) { var injectedBindingValues = { options: valueAccessor, optionsValue: function () { return "ID" }, optionsText: function () { return "Name" } }; ko.applyBindingAccessorsToNode(element, injectedBindingValues); //tell Knockout that we have already handled binding the children of this element // return { controlsDescendantBindings: true }; } }

Puedes verlo en acción en este violín .

Nota: Por lo general, utilizo el esquema JSON enviado desde el servidor (C #, JSON.NET) para automatizar las opciones de llenado en la UI desde los atributos C # o los metadatos del esquema DB. Destilé mi código y lo cambié para que coincida con lo que hace el OP por la continuidad con la pregunta. Pero si hay algún interés en la técnica del esquema JSON, sírveme y puedo publicarlo.


Terminé con la siguiente solución, que también permite crear filtros simples y pendientes: utiliza un guión bajo para eso, pero eso es solo una cuestión de conveniencia:

// NamedIdOptions - is basically equal to the options binding - except, optionsText="Name", and "optionsValue=''ID''" // The options can be filered - Specifying optionsFilter: {''FilterProp'' : ''valueToBeMatched'', ''FilterProp2'' : VMpropToMatch, .. } // Definig optionsFilterCallback, registers a callback which will be invoked with the matched elements // which can be used to turn off elements etc. ko.bindingHandlers.NamedIdOptions = { init: function (element, valueAccessor, allBindingsAccessor, viewModel) { var allBindings = allBindingsAccessor(), appliedValueAccesor = valueAccessor(), shownOptions = appliedValueAccesor, unwrap = ko.utils.unwrapObservable; if (allBindings.optionsFilter) { shownOptions = ko.computed(function () { // First - find all items to be presented in the list var allItems = unwrap(appliedValueAccesor); // Extract items to match against // it is ensured that the computed observable dependts on all its sub properties // All items are matched by key into an array // if the match is null, undefined, or an empty array, it is not included int the match var matchItems = {}; _.each(_.keys(allBindings.optionsFilter), function (key) { var observedValues = unwrap(allBindings.optionsFilter[key]); if (!observedValues) return; if (!_.isArray(observedValues)) observedValues = [observedValues]; matchItems[key] = observedValues; }); // Find items that match the items above - uses ko own routine to do so var matchedItems = _.filter(allItems, function (elm) { return _.all(_.keys(matchItems), function (key) { var match = _.contains(matchItems[key], elm[key]); return match; }); }); // if a callback is defined - call it with the matched items if (allBindings.optionsFilterCallback) allBindings.optionsFilterCallback(matchedItems); return matchedItems; }, this); } // Change the binding options - the already handled items should not be reapplied to the node // NOTE: it would be preferable to use ''ko.3.0->applyBindingAccessorsToNode'' instead of the hack below // It assumes that the order of dictionaries are not changed - it works, but is not complient with the Ecmascript standard var newBindingOptions = { options: shownOptions, optionsText: "Name", optionsValue: "ID" }; var bindingKeys = _.keys(allBindings); var handledItems = _.first(bindingKeys, _.indexOf(bindingKeys, "NamedIdOptions") + 1); _.each(handledItems, function (item) { delete allBindings[item]; }); _.extend(newBindingOptions, allBindings); ko.applyBindingsToNode(element, newBindingOptions, viewModel); } };