Maybe I am misunderstand how this works. I want to use knockout.js to populate a select element options. I am using the following markup to achieve this:
<select data-bind="options: type_options, optionsText: function(item) {
return item.text;
}, optionsValue: function(item) {
return item.value;
}, optionsCaption:'Select a type...',
value: type">
Here is the relevant model code:
var myModel = {
type: ko.observable(),
type_options: ko.observableArray([
{text: "String 1", value:1},
{text: "String 2", value:2},
{text: "String 3", value: 3},
{text: "String 4", value: 4},
{text: "String 5", value: 5}
]),
}
Now the drop down renders correctly, with all the correct text and values, but when I select the an option from the drop down it doesn't set the value of 'type' correctly.
For instance if I selected the option labeled "String 4", and run the following command in the browser:
myModel.type()
I would expect it to return the value "4". Instead i get the object entire object:
Object
text: "String 4"
value: 4
__proto__: Object
My question is how do i get knockout to set the value of type based on the option's value attribute, instead of the entire object?
Well, you should be able to pass the text for the variable in you options array instead of a function. I don't know if that's what's causing the issue but your markup would look better like
<select data-bind="options: type_options, optionsText: 'text', optionsValue: 'value', optionsCaption:'Select a type...', value: type"></select>
That should get you what you want, see fiddle for full example.
Related
I have a multiselect dropdown bound to an array of objects ($scope.selectedProperties):
<select id="propertyDdl" multiple="multiple"
ng-model="selectedProperties"
ng-options="account.property_name group by account.client_name for account in accountInfo">
</select>
(accountInfo is a scope property containing an array of objects):
[{client_name:"Client 1", is_demo:false, property_name:"Prop 1"},
{client_name:"Client 2", is_demo:false, property_name:"Prop 2" },
{client_name:"Client 3", is_demo:false, property_name:"Prop 3" }]
on a certain event, I store the array of selected items:
$scope.updateSessionProperties = function () {
CachingService.cachedSelectedProperties = $scope.selectedProperties;
};
...Then reapply them later:
if(CachingService.cachedSelectedProperties && CachingService.cachedSelectedProperties.length>0){
$scope.selectedProperties = CachingService.cachedSelectedProperties;
}
the array of objects (selectedProperties) it is bound to is structured identical to accountInfo:
[{client_name:"Client 1", is_demo:false, property_name:"Prop 1"},
{client_name:"Client 2", is_demo:false, property_name:"Prop 2" }]
with no luck... the dropdown list displays "none selected" even though the scope property it is bound to is the identical object. I have tried executing $scope.$apply() as well, but the select element will not rebind.
After reading through ng-options, I would suggest the following:
<select
id="propertyDdl"
multiple="multiple"
ng-model="userSelectedProperties"
ng-options="account.property_name group by account.client_name for account in selectedProperties">
</select>
i have a drop down of states and their ID:
<select data-bind="options: States, optionsText: 'text', value: SelectedState"></select>
Javascript
function ViewModel() {
this.States = ko.observableArray(states);
this.SelectedState = ko.observable(usersState);
};
var states = [
{ value: 10, text: "California" },
{ value: 3, text: "New York" },
{ value: 9, text: "Florida" }
];
ko.applyBindings(new ViewModel());
usersState is a variable that may or maynot contain the users info. By default its null. But if the user has already logged in then it should populate with the users selected state. For this example, the users has logged in and their select state is 9 for florida.
so i declared usersState = 9; at the top.
What i am trying to do is simply auto select Florida in the drop down based on the users info.
not sure why its not selecting it. Here is my fiddle: http://jsfiddle.net/neosketo/sw9dzjk1/2/
The SelectedState refers to a state object. Your initial selection is a number. You'll have to find the state object corresponding to the number:
var usersState = 9;
// This method finds an object by value property
var findStateById = function(states, id) {
return states.find(function(state) {
return state.value === id;
});
};
function ViewModel() {
this.States = ko.observableArray(states);
// In this example, SelectedState is an object with a value and text property
this.SelectedState = ko.observable(findStateById(states, usersState));
};
// Test data
var states = [{
value: 10,
text: "California"
}, {
value: 3,
text: "New York"
}, {
value: 9,
text: "Florida"
}];
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select data-bind="options: States, optionsText: 'text', value: SelectedState"></select>
(Note that I used Array.prototype.find which isn't supported by all browsers)
Alternatively, you could use the optionsValue option to tell knockout to use the value property to match selection to options. Personally, I prefer using the actual object: knockout being able to work with references to real instances rather than using strings makes developing easier.
var usersState = 9;
function ViewModel() {
this.States = ko.observableArray(states);
// In this example, SelectedState is a number
this.SelectedState = ko.observable(usersState);
};
var states = [{
value: 10,
text: "California"
}, {
value: 3,
text: "New York"
}, {
value: 9,
text: "Florida"
}];
ko.applyBindings(new ViewModel());
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select data-bind="options: States,
optionsText: 'text',
optionsValue: 'value',
value: SelectedState"></select>
Scenario: I make a request to the server for a part. It gives me this back (it's pseudo, but represents what I'm looking at):
{
PartNumber : "XYZ",
Description: "ABCFOO",
ProductClass: "Widget",
FieldList:[
{Name: "PROGRAM TYPE", Value: "Program3"},
{Name: "SHIP", Value: false},
{Name: "NOTES", Value: "SomeValue1"}
],
MoreStuff : [{
...
}]
}
Note the FieldList list of elements, that's the focus here.
The server also gave me a list of certain fields, their types and default values. It looks like this:
[
{FieldName : "PROGRAM TYPE", FieldType: "List", Defaults: [{Name:"Program 1", Value: "Program1"},{Name:"Program 2", Value: "Program2"},{Name:"Program 3", Value: "Program3"}]},
{FieldName : "SHIP", FieldType : "Boolean", Defaults: []},
{FieldName : "NOTES", FieldType: "TextArea", Defaults: []}
]
That comes in a seperate REST call, and the prior to loading my Part. I use it to create part of the HTML page for the Part. You can see they're similarly related to the FieldList section from when I ask for Part.
From that "list of fields" and defaults -- I generate the appropriate HTML elements on the page. If it's a Boolean field type, I create a checkbox - if it's a list, I create a SELECT (with options given in Defaults), TextArea is a text-area, etc. That all works fine. It ends up looking like:
<input data-bind="textInput: PartNumber"/>
<textarea data-bind="textInput: Description"></textarea>
<!-- generating fieldlist - i create a pseudo attr because the field name can have spaces-->
<select field_label="PROGRAM TYPE"> <!-- how the heck do i bind to this??-->
<option value="Program1">Program 1</option>
<option value="Program2">Program 2</option>
<option value="Program3">Program 3</option>
</select >
<input type="checkbox" field_label="SHIP" value="true"/> <!-- or this, how to bind to it?!-->
<!-- end of field list generation -->
Now I take the object (the part I'm given) and put that into my ViewModel - that is all working just swimmingly. I make it easy and just use ko.mapping.fromJS(rest_data); Works just fine.
Data binding is ducky -- for what I am able to bind it to. My issue comes from -- how the heck do I map my FieldList to the HTML I generated for the fields the server gave me?. My data / my viewmodel object has FieldList in it, with a buncha stuff I want to map to that generated stuff. The only real "key" I have is the self-created field_label I have, because the server's FieldName can have spaces.
So I guess what I'm asking is, I have that array of FieldList from my part. I have the whole Part object in my view model and it's all fine. How do I take that FieldList and map it into my self-generated set of fields from the other object (ie., take the FieldList name and tie it to the element with field_label of the same value?)
Spelled out - it'd be like: How to map FieldList with Name of "PROGRAM TYPE" to HTML element having field_label of "PROGRAM TYPE".
I begin to think something like this might be the direction I should be going:
http://jsfiddle.net/MhdZp/128/
but it goes over my head.
One way to approach this:
function Option(definition) {
this.definition = definition;
this.value = ko.observable();
this.templateName = 'input-template-' + definition.FieldType;
}
function ViewModel() {
var self = this;
// from REST call
var fieldDefinition = [{
FieldName: "PROGRAM TYPE",
FieldType: "List",
Defaults: [
{ Name: "Program 1", Value: "Program1" },
{ Name: "Program 2", Value: "Program2" },
{ Name: "Program 3", Value: "Program3" }
]
}];
self.options = ko.observableArray();
// for the sake of the example
self.options.push(new Option(fieldDefinition[0]));
// methods
self.optionByName = function (name) {
return ko.utils.arrayFirst(self.options(), function (option) {
return option.Name = name;
});
};
// poor man's init, imagine 2nd rest call instead
self.optionByName("PROGRAM TYPE").value("Program3");
}
and
<script type="text/html" id="input-template-List">
<label data-bind="text: definition.FieldName"></label>
<select data-bind="
value: value,
options: definition.Defaults,
optionsText: 'Name',
optionsValue: 'Value',
optionsCaption: 'Please select...'
"></select>
</script>
and
<div data-bind="foreach: options">
<div data-bind="template: templateName"></div>
</div>
Add more templates as needed, this should be very easy to extend.
jsFiddle: http://jsfiddle.net/0nxt2zte/
I have a DoJo FilteringSelect created from <select>.
How I can make one (not selected) option as disabled dynamically?
Thanks
UPD: is it possible to REMOVE this element when FilteringSelect was created by PARSE?
Looks like there's a "disabled" attribute on __SelectOption. When you pass in options, try setting "disabled" to true.
new dijit.form.Select({
id: 's2',
options: [
{label: 'this is disabled', value: 1, disabled:true},
{label: 'this is enabled', value: 1}
]
}
See http://jsfiddle.net/ur87d/
Edit: You said dynamically... So you'll want to use updateOption or removeOption.
dojo.ready(function() {
var s2 = new dijit.form.Select({
id: 's2',
options: [{label: 'one', value: 1},{label: 'two', value: 2}]
});
s2.updateOption({label: 'one-updated', value: 1, disabled: true});
s2.placeAt(dojo.body());
});
See http://jsfiddle.net/ur87d/1/
It's not easy to make option disabled, but it's easy to make it invisible.
Bind your FilterSelect to an ItemFileWriteStore. The options are from items in the store.
You can dynamically add/remove items from the store.
So that your disabled options will not appear in the drop down.
Fiddle: http://jsfiddle.net/tdRCB/3/
I have an observableArray in my viewModel named filterInfo.
I have many html-controls to create filters (inputs, selects, etc)
What is the best way to get filterInfo containing all values in my html controls?
For example:
I have in input value 123 and in select value 1, so I need, that my filterArray contains two elements:
[{field: 'title', value: '123'}, {field: 'type', value: 1}]
If my input is empty and I selected in dropdown list only second element, resulting array will be:
[{field: 'type', value: 2}]
Thanks
I have written sample for your example.
http://jsfiddle.net/ivanov_vitaly/tdRCB/6/