Fill dropdown values from data-bind="selectedValue" in knockoutJS - javascript

I am working on "Form Builder" application, where I can set the rules for each control.
I am trying to select the dropdown value from selectedValue which I kept as object. But it is not working.
To understand my question in depth, here is my previous question where you get the clear understanding of my question :
Show/Hide based on Dropdown selection in knockoutJS with model inside another model
Here is my fiddle which I worked out for filling the dropdown values(Current question) :
https://jsfiddle.net/vikash208/6b6ntoam/20/
What I tried :
HTML Code:
<div class="tab-content" data-bind="with: TextBoxModel">
<div class="tab-pane" id="rules" data-bind="with: rules">
<div data-bind="foreach: ruleList">
<div class="form-group col-md-12">
<div class="col-sm-3">
<select class="form-control" data-bind="options: $parent.ruleConditions, optionsText: 'Name', value: selectedCondition, optionsCaption: 'Choose condition'"></select>
</div>
<div class="col-md-5" data-bind="visible: isExpressionValueRequired()">
<input type="text" class="form-control" data-bind="value: expressionValue" />
</div>
</div>
</div>
KnockoutJS of assigning selectedOption:
var RuleConditionArray = {
// Options for "Text Field" under Rules tab
textField: ko.observableArray(
[
{ Name: 'is filled out',
Value: 'isfilledout',
isExpressionValueRequired: false
},
{
Name: 'is not filled out',
Value: 'isnotfilledout',
isExpressionValueRequired: true
}
]
)
};
function Rule() {
var rule = this;
//For Expression input field
rule.expressionValue = ko.observable("");
//Keep track of selected ruleConditions - returns "Condition object"
rule.selectedCondition = ko.observable({ Name: 'is filled out', Value: 'isfilledout', isExpressionValueRequired: false });
//This is computed value to show/hide Expression input field
rule.isExpressionValueRequired = ko.computed(function () {
return this.selectedCondition() && this.selectedCondition().isExpressionValueRequired
}, this);
};
From the above knockoutJS code, I am passing
{
Name: 'is filled out',
Value: 'isfilledout',
isExpressionValueRequired: false
}
object inside selectedCondition. But it is not working.
What I want:
I need to pass object as parameter to select the dropdown Value. Kindly help me to solve this.

Your <select> element has an array of options passed to it by the options binding. Each option represents an instance of an object. When you select an option, the value binding makes sure the object is written to selectedCondition.
The other way around, when you set your selectedCondition, knockout looks within the array of options to see if it's an option that's already known. If it can find it, it will update the UI.
Now, the problem here, is that you're setting the selectedCondition to an object that looks the same as one of the options, but is not one of the actual option instances.
Knockout doesn't use a (deep) object comparison method for looking up the selected object, it just does something along the lines of: options.indexOf(newSelection). Since you've created a new object, this won't work.
Solution
Pass an actual reference to the selectedCondition, like so:
rule.selectedCondition = ko.observable(ruleModel.ruleConditions()[0]);
If you want to add a new option, push it to the options first:
var newOption = {
Name: ko.observable(""),
Value: ko.observable(""),
isExpressionValueRequired: ko.observable(false)
};
ruleModel.ruleConditions.push(newOption);
selectedCondition(newOption);

Related

Knockout: Iterate over object and show value in dropdown

I need some help with js/knockout/databindings.
In my code I am receiving an object like this:
Received object
I wish to show the value in a dropdown, and the key should be stored as the value.
I tried using some knockout data-bind like this:
<select id="routeTable" class="form-control" data-bind="
options: availableRouteTables,
optionsText: availableRouteTables.key,
optionsValue: availableRouteTables.value,
value: selectedRouteTable,
optionsCaption: '- velg rutetabell -'"></select>
I know that "availableRouteTables.key" and "availableRouteTables.value" are wrong, but it was just to explain/show what I want.
As of now the object only looks like this in JS:
this.availableRouteTables = availableRouteTables;
In the dropdown it is shown like this:
Dropdown failure
Can someone help me with identifying the issue and fix it?
thanks a lot!
When you receive the data you have to change it's structure to work with the options binding:
// Convert to array of objects with value and text properties
var availableRouteOptions = [];
for(var key in availableRouteTables) {
availableRouteOptions.push({
value: key,
text: availableRouteTables[key]
});
}
And you have to change your bindings:
options: availableRouteOptions,
optionsText: 'text',
optionsValue: 'value',

Knockout Binding not updating with foreach select options

I have two selections, the seconds options options are dependent on the first selection.
Both of the selection boxes NEED to have a class attached to them, so I can not use the options attribute. The only way I have found to do this is using the foreach method.
I need to track both of the selected values at any time, but the second selection does not update its value when repopulated with new data. Please see the fiddle.
Another requirement is if the original option passed in through the viewmodel creation for the second selection is there, I would like that to be the default when populated. e.g in the fiddle when changing from WPA-PSK to none and then back I would like the default selection to be MIX(passed in) rather than AES.
Fiddle: Link to Fiddle
function ViewModel(security) {
var self = this;
self.authenticationMode = ko.observable(security.authenticationMode);
self.encryptionType = ko.observable(security.encryptionType);
self.authenticationModes = ko.observableArray([{translationText: "None", mode: "NONE", translationClass: "T_WPA-PSK"},
{translationText: "Open", mode: "OPEN", translationClass: "T_WPA-PSK"},
{translationText: "WPA-PSK", mode: "WPA-PSK", translationClass: "T_WPA-PSK"}]);
self.encryptionTypes = ko.computed(function () {
console.log(self.authenticationMode());
if (self.authenticationMode() === 'OPEN')
return [{translationText: "WEP", type: 'WEP', translationClass: "T_WEP"}];
if (self.authenticationMode() === 'WPA-PSK')
return [{translationText: "AES", type: 'AES', translationClass: "T_AES"},
{translationText: "MIX", type: 'MIX', translationClass: "T_MIX"}];
return [];
});
}
ko.applyBindings(new ViewModel({authenticationMode: 'WPA-PSK', encryptionType: 'MIX'}));
<select data-bind="value: authenticationMode, foreach: authenticationModes">
<option data-bind="text: translationText, value: mode, attr: { class: translationClass }"></option>
</select>
<select data-bind="value: encryptionType, foreach: encryptionTypes">
<option data-bind="text: translationText, value: type, attr: { class: translationClass }"></option>
</select>
<p>Selected Authentication: <b data-bind="text:authenticationMode"></b></p>
<p>Selected Encryption Type: <b data-bind="text:encryptionType"></b></p>
Fiddle Using Options - Another way I found using options to set the class with optionsAfterRender method.
Updated your fiddle:
http://jsfiddle.net/fS8qp/7/
Both requirements are met by adding the following in your viewmodel:
self.defaultEncryptionType = security.encryptionType;
self.authenticationMode.subscribe(function () {
self.encryptionType(self.defaultEncryptionType); // This will select the passed in encryptionType, if it exists in the current list of options
self.encryptionType.valueHasMutated(); // This will trigger an update of the observable
});

Knockout.js - select value not set

I am using knockout.js, and it's not setting the value of an empty option (Four):
<select data-bind="value: item.widgetValue, attr: {id: item.widgetName, name: item.widgetName}, options: item.options, optionsText: ‘label’, optionsValue: ‘value’” id=”fld-“ name=”fld0”>
<option value=”one”>One</option>
<option value=”two”>Two</option>
<option value=”three”>Three</option>
<option value>Four</option>
...
</select>
This is creating a problem: when you're on any option and try to select Four, it selects One; it will only select Four the second time you try to select it.
I have tried changing the knockout data-bind to fix it:
value: $.trim(item.widgetValue)
This allows you to select Four immediately, but incorrectly shows One as being selected after you submit the form with Four selected.
Any ideas as to what could be causing this, or how to fix it?
You shouldn't be manually setting options if you are using the options binding on your select element. If those are being dynamically created by the binding (ie. you are actually using item.options for your source) then check the objects you are binding the select element to -
item.options probably looks like this (missing a value or is somehow not like the other options) -
item.options = [
{ label: 'someLabel1', value: 'someValue1' },
{ label: 'someLabel2', value: 'someValue2' },
{ label: 'someLabel3', 'someValue3' }
];
but should be a more uniform object like this (well defined model) -
function optionModel(label, value) {
var self = this;
self.label = ko.observable(label);
self.value = ko.observable(value);
}
item.options = [
new optionModel('someLabel1', 'someValue1'),
new optionModel('someLabel2', 'someValue2'),
new optionModel('someLabel3', 'someValue3')
];

Knockout select and textbox sharing binding

I have a page with a select and an input-box being bound to the same value. The idea is that normally one would select a value from the select, however, the user should also be able to enter an arbitrary string in the input-box. The problem is that if I enter something not present in the select, because of the binding, the value is set to the first item in the select.
This is the behavior I want to achieve:
User selects value from select
Value is set to selected item.
Input is updated with selected value.
User enters text in input
Value is set to entered text.
Select does not change unless Value is present in the collection of available values.
In other words, what I want is for the last changed control to be the valid Value. But I also want both controls to be up to date as long as a given value is valid for that control.
My code looks like this:
js
var viewModel = { Value: ko.observable('1'), Set: ['1', '2', '3'] };
ko.applyBindings(viewModel);
html
<!-- ko if: Set.length > 1 || (Set.length > 0 && Set[0] != '') -->
<select type="text" class="form-control input-small" data-bind="options: Set, value: Value">
</select>
<!-- /ko -->
<input class="form-control input-small" data-bind="value: Value" style="margin-top: 5px;" />
Here is a jsfiddle showing how the code currently works: http://jsfiddle.net/b2RwG/
[Edit]
I've found a working solution (http://jsfiddle.net/b2RwG/2/), however it's really not pretty, and there has to be a better way to solve this problem.
As you can see I add an inputValue observable that is bound to the text input.
I also add an computed named virtualSet that contains both original items and the new item (from the text input).
I susbcribe to the inputValue so the select will be automatically set when you are typing.
var viewModel = {
inputValue: ko.observable('1'),
Value: ko.observable('1'),
Set: ['1', '2', '3']
};
viewModel.virtualSet = ko.computed({
read: function () {
var vs = this.Set.slice(0);
if (this.inputValue() && this.inputValue().length)
vs.unshift(this.inputValue());
return vs;
},
owner: viewModel
});
viewModel.inputValue.subscribe(function (value) {
viewModel.Value(value);
});
See fiddle
I hope it helps.
You can have the select use a computed observable instead, which updates only if the value makes sense.
I made an example where i added a caption to the select. The result is that it doesn't automatically pick the first value, but instead tries to set undefined value, when it reads a value that isn't included in the Set array.
<select type="text" class="form-control input-small" data-bind="options: Set, value: SelectValue, optionsCaption: 'Other value'"></select>
To do that, a constructor function instead of an object literal will make it easier, because then you can access the Value observable through the self reference.
function ViewModel() {
var self=this;
this.Value = ko.observable('1');
this.Set = ['1', '2', '3'];
this.SelectValue= ko.computed({
read: function() {
var val = self.Value();
return val;
},
write: function(value) {
if(value) self.Value(value);
}
});
}
See http://jsfiddle.net/b2RwG/4/

Set initial dropdown value to viewmodel

I'm having some issues with a dropdown list where I need to pass the initial value to the viewmodel. Just to clarify: I'm working on an edit-form, so the dropdownlist will be populated with an already-selected value.
What I have so far is:
Razor:
<select data-bind="selectedOptions: selectedLength">
// razor code omitted
foreach(var preValue in lengthPreValues)
{
if(lengthPreValues.Contains(preValue.value))
{
<option selected="selected" value='#preValue'>#preValue</ option>
}
else
{
<option value='#preValue'>#preValue</option>
}
}
And my viewmodel looks like this:
var editOfferViewModel = {
// Properties omitted
selectedLength: ko.observable("")
};
ko.applyBindings(editOfferViewModel);
While this definately works when selecting a new value, I'm a bit stuck when it comes to setting the initial value. I was fortunate enough to get some great help from Ryan Niemeyer here on stackoverflow.com with checkboxes and creating custom bindinghandlers, but I'm still
having a hard time to figure it out to be honest.
So, any help and/or hint on this is greatly appreciated!
A common and easy way to do this is to serialize your model values to the page. This would be something like:
var viewModel = {
choices: ko.observableArray(#Html.Raw(Json.Encode(Options))),
selectedChoices: ko.observableArray(#Html.Raw(Json.Encode(SelectedOptions)))
};
Then, just use a standard data-bind on your select like:
data-bind="options: choices, selectedOptions: selectedChoices"
You then don't even need to populate the option elements in Razor.
If your viewModel is built in an external file, then you can just set the value of the observables in your view (after your external script has been loaded)
My data is something like :
dataList = [ {name:'length1',id:1},{name:'length2',id:2},{name:'length3',id:3},{name:'length4',id:4},{name:'length5',id:5} ]
And I have been using that data with dropdown like this :
<select name="xxx" id="xxxid" data-bind="options: dataList, value: selectedLength , optionsText: 'name', optionsValue: 'id', optionsCaption: 'Please Select...'"></select>
<select name="xxx2" id="xxxid2" data-bind="options: dataList, selectedOptions: multiSelectedLength , optionsText: 'name', optionsValue: 'id', optionsCaption: 'Please Select...'" size="5" multiple="true"></select>
var editOfferViewModel = {
selectedLength: ko.observable(),
multiSelectedLength: ko.observableArray()
};
ko.applyBindings(editOfferViewModel);
$(document).ready(function() {
// Set initial value
editOfferViewModel.selectedLength(2);
// Set inital multi value
editOfferViewModel.multiSelectedLength(['2','3']);
});
You can use value property to set initial value.
Here is the working example.

Categories