Knockout Foreach Javascript function - javascript

Is it possible to declare a javascript function in your knockout foreach binding? I want to generate a list from a javascript function outside of my view model.
<select class="form-control" data-bind="foreach: { data: function() { // return list values } }">
<option data-bind="text: Value, attr: { value: Value }"></option>
</select>

No quite sure what you are trying to achieve with <option data-bind="text: Value, attr: { value: Value }"></option>.
Have a look at my fiddle for a few different setups. It contains usages of both options and foreach bindings on a select element.
To answer the question, you provide a function in your viewModel to provide the desired data. As long as your viewModel can "access" the data, the function will provide it to the binding. If the data were to be a simple list of literals, then all that is required :
Javascript:
var listOfLiterals = function() { return ["One", "Two", "Three"] };
var viewModel = {
selectedLiteral: ko.observable(),
getLiterals: function() {
return listOfLiterals();
}
}
HTML:
<select data-bind="options: getLiterals(), value: selectedLiteral"></select>

Related

How to bind a multi-select element with object array?

I have a element , which I would like to bind with a model to get/set the value and to populate it from a list using angular 1
The way I have it, I am able to bind it from UI to model, not vice versa, what am I doing wrong?
HTML:
<div ng-controller="MyCtrl">
<select class="form-control"
ng-options="o as o.prop for o in brands"
ng-model="selectedBrands"
multiple="multiple"
>
</select>
</div>
JS:
var myApp = angular.module('myApp', []);
function MyCtrl($scope) {
$scope.brands = [{
prop: 1
}, {
prop: 2
}, {
prop: 3
}];
$scope.selectedBrands = [{
prop: 2
}];
}
JSFiddle:
http://jsfiddle.net/4L8b7cke/
Reuse $scope.brands elements, do not create new for selected items, because ngModel watches the model by reference.
$scope.selectedBrands = [$scope.brands[0],$scope.brands[1]];
Update:
By default, ngModel watches the model by reference, not value. This is important to know when binding the select to a model that is an object or a collection.
From ngOptions documentation

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

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);

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 won't handle numeric array for multiple select list

I am trying to get a numeric array to bind to my select element. I am using a numeric array because that is what the server is returning. I have a jsFiddle setup demonstrating the problem.
HTML
<select data-bind="selectedOptions: EnvironmentIds" id="EnvironmentIds" multiple="multiple" name="EnvironmentIds">
<option selected="selected" value="1">Hosting</option>
<option value="2">Internal</option>
</select>
Script
function IncidentViewModel() {
var self = this;
//Properties
self.EnvironmentIds = ko.observableArray([1]);
}
var incidentViewModel = new IncidentViewModel();
ko.applyBindings(incidentViewModel);
If you simply switch to a string array like so:
ko.observableArray(["1"])
Then the code works as expected. However, I don't want to use a string array since that isn't the type of the data. Is there a way around this or have I overlooked something simple?
Knockout's options binding uses ko.selectExtensions to give option elements a non-string value. A simple custom binding can do this to the existing options.
ko.bindingHandlers.makeOptionsNumeric = {
init: function(element) {
ko.utils.arrayForEach(element.options, function(option) {
ko.selectExtensions.writeValue(option, +option.value);
});
}
};
Add this to the bindings for your select element.
data-bind="makeOptionsNumeric, selectedOptions: EnvironmentIds"
The order is important. makeOptionsNumeric must run before selectedOptions. If you're not yet using Knockout 3.0, you'll need to give some dummy value to the binding (for example makeOptionsNumeric: {}).
jsFiddle: http://jsfiddle.net/mbest/gUdPq/
JS:
function IncidentViewModel() {
var self = this;
//Properties
self.Environments = [{id: 1, name: 'Hosting'}, {id: 2, name: 'Internal'}]
self.EnvironmentIds = ko.observableArray([1]);
}
var incidentViewModel = new IncidentViewModel();
ko.applyBindings(incidentViewModel);
HTML:
<select data-bind="options: Environments,
optionsText: 'name',
optionsValue: 'id',
selectedOptions: EnvironmentIds" id="EnvironmentIds" multiple="multiple" name="EnvironmentIds">
</select>

Categories