A typical scenario in our environment is to allow the user to select a list of options provided by the server (terminals, products, ..), and then present the requested data.
The options provided by the server is in Name, ID format, and thus the following knockout construct is used all to often:
<select data-bind="options: serverOptions, optionsText: 'Name', optionsValue: 'ID', value: selectedOption>
It would be desirable to make a custom bindingHandler, called 'NamedIdOptions' specifying optionsText and optionsValue on the allBindingsAccessor() and then redirect to the standard options binding handler.
i.e.
<select data-bind="NamedIdOptions: serverOptions, value: selectedOption"></select>
Previously, I have made own binding handler which fills in the options my self - however, I would prefer to use the framework provided by the options binding handler.
I have tried different approaches without too much success - the options binding uses allBindings['optionsValue'], and allBindings['optionsText'] to access the value, and it seems that I have no way of setting these. (I would like to avoid applyBindingsToNode approach used in and write something along the lines of:
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);
}
}
However, it seems like I have no possiblity to set anything on the allBindings.
I am not allowed to use
allBindings['_ko_property_writers']['optionsText']("Name" );
allBindings['_ko_property_writers']['optionsValue']("ID" );
I would really prefer to avoid applyBindingsToNode in the init construct as
Knockout - is it possible to bine standard select bindings with a custom binding?
Does anyone now about a simple solution to the problem?
A typical scenario in our environment is to allow the user to select a list of options provided by the server (terminals, products, ..), and then present the requested data.
The options provided by the server is in Name, ID format, and thus the following knockout construct is used all to often:
<select data-bind="options: serverOptions, optionsText: 'Name', optionsValue: 'ID', value: selectedOption>
It would be desirable to make a custom bindingHandler, called 'NamedIdOptions' specifying optionsText and optionsValue on the allBindingsAccessor() and then redirect to the standard options binding handler.
i.e.
<select data-bind="NamedIdOptions: serverOptions, value: selectedOption"></select>
Previously, I have made own binding handler which fills in the options my self - however, I would prefer to use the framework provided by the options binding handler.
I have tried different approaches without too much success - the options binding uses allBindings['optionsValue'], and allBindings['optionsText'] to access the value, and it seems that I have no way of setting these. (I would like to avoid applyBindingsToNode approach used in and write something along the lines of:
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);
}
}
However, it seems like I have no possiblity to set anything on the allBindings.
I am not allowed to use
allBindings['_ko_property_writers']['optionsText']("Name" );
allBindings['_ko_property_writers']['optionsValue']("ID" );
I would really prefer to avoid applyBindingsToNode in the init construct as
Knockout - is it possible to bine standard select bindings with a custom binding?
Does anyone now about a simple solution to the problem?
Share Improve this question edited May 23, 2017 at 12:29 CommunityBot 11 silver badge asked Nov 15, 2013 at 14:09 Jesper KleisJesper Kleis 511 silver badge6 bronze badges 2- I don't know if this exactly answers your question, but if you are creating a re-usable control you can always use putedOptions and putedName and have those be observables that are set to the value of whatever is passed in. – PW Kad Commented Nov 15, 2013 at 14:19
- In older Knockout versions you used to be able to get away with adding optionsText and optionsValue straight to allBindings and it would work. That's what I was doing for exactly the same scenario as you've described. – BrandonLWhite Commented Feb 6, 2014 at 3:22
4 Answers
Reset to default 3You might consider using ko.applyBindingAccessorsToNode. This is how I've started doing it in 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 };
}
}
You can see it in action in this fiddle.
Note: I typically use JSON schema sent from the server (C#, JSON.NET) to automate populating options in the UI from C# attributes or DB schema metadata. I distilled my code and changed it to match what the OP's doing for continuity with the question. But if there is any interest in the JSON schema technique hit me up and I can post it.
Okay - I ended up using apply bindings to node anyway:
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);
}
};
And it seems to work out as expected - I am a bit unsure about the value and selectedOptions - which have 'after' set to options. I guess I am safe when the NamedIdOptions is plaved before the value binding?
Can't you just fake the whole allBindingsAccessor parameter when forwarding the call?
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);
}
Edit: added some more code to bine the existing allBindingsAccessor with the manual fake bindings
I ended up with the following solution, which also allows to make simple dependendt filters - it uses underscore for that, but that is just a matter of convenience:
// 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.puted(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 puted 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 plient 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);
}
};
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1744413143a4572983.html
评论列表(0条)