Reputation: 2766
This may be a basic question but I am unable to get any solution.
I am trying to show a list of formats in a dropdown. I am using knockout binding to do so. I want to show a specific value initially
.
When user selects some other value from the dropdown it should show the selected value (which is working fine).
On selecting any option I want to get the selected value so that I can send it to the server. I am using subscribe
for that.
Below is my code:
var obj = this;
obj.formatArray = ko.observableArray([{'text' : "MM/DD/YYYY"},
{'text' : "MM-DD-YYYY"},
{'text' : "MM.DD.YYYY"},
{'text' : "DD/MM/YYYY"},
{'text' : "DD-MM-YYYY"},
{'text' : "DD.MM.YYYY"}]);
var format1 = 'DD-MM-YYYY';
obj.formatArrayDefault = ko.observable({text :format1});
obj.formatArrayDefault.subscribe(function(newValue){
alert("default value " + format1);
alert("new value in subscribe " + newValue.text);
});
ko.applyBindings(obj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select class="" data-bind="options:formatArray(),optionsText:'text',value:formatArrayDefault"></select>
You can run the above snippet and check, alert
inside subscribe
is coming on loading and changing the value to be displayed to the first value in the array
.
Why subscribe is called on load and why the value is changed to first value?
What mistake I am doing?
If the question is duplicate can someone give link to the solution (I searched here but could not get any solution).
Upvotes: 1
Views: 1181
Reputation: 23372
Your array contains plain objets. In javascript, object comparison works like this:
var obj1 = { a: 1 };
var obj2 = { a: 1 };
console.log(obj1 === obj2); // false
console.log(obj1 === obj1); // true
It checks the actual object instance that is referenced by a variable, not the way it looks. Many libraries contain a "deepEquals" function that you can use to check if two objects actually look similar, but knockout doesn't use such a mechanism.
So when you set your initial selection, its value might be similar to the object at formatArray()[0]
, but knockout won't be able to mach the two together. I.e.:
obj.formatArrayDefault() === obj.formatArray()[1]; // false
Now, when applying bindings, knockout tries to solve the mismatch between selection and source data. It can't find an <option>
element to match with the selection that is passed to the value
binding.
It has to render a selected option, so it defaults to the first one. This is when your initial object gets replaced by formatArray()[0]
and the subscription is triggered.
Here's a solution: we put an actual reference to the first object in our initial value!
var obj = this;
obj.formatArray = ko.observableArray([{'text' : "MM/DD/YYYY"},
{'text' : "MM-DD-YYYY"},
{'text' : "MM.DD.YYYY"},
{'text' : "DD/MM/YYYY"},
{'text' : "DD-MM-YYYY"},
{'text' : "DD.MM.YYYY"}]);
var format1 = 'DD-MM-YYYY';
obj.formatArrayDefault = ko.observable(obj.formatArray()[1]);
obj.formatArrayDefault.subscribe(function(newValue){
console.log("default value " + format1);
console.log("new value in subscribe " + newValue.text);
});
ko.applyBindings(obj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select class="" data-bind="options:formatArray(),optionsText:'text',value:formatArrayDefault"></select>
Upvotes: 1
Reputation: 311
if you just want the value of the dropdown after any change.
just take a observable and set it as value it the drop down and initially it will be blank and in your subscribe function just add a line of comparision if the value of drop down is empty dont call the alert function.
var obj = this;
obj.formatArray = ko.observableArray([{'text' : "MM/DD/YYYY"},
{'text' : "MM-DD-YYYY"},
{'text' : "MM.DD.YYYY"},
{'text' : "DD/MM/YYYY"},
{'text' : "DD-MM-YYYY"},
{'text' : "DD.MM.YYYY"}]);
var format1 = 'DD-MM-YYYY';
obj.initialLoad = true;
obj.formatArrayDefault = ko.observable(format1);
obj.formatArrayDefault.subscribe(function(newValue){
if(obj.initialLoad) {
obj.initialLoad = false;
}
else {
alert("Hello");
}
});
ko.applyBindings(obj);
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<select class="projectUserAccess workflowSetOptions" data-bind="options:formatArray(),optionsText:'text',value:formatArrayDefault"></select>
===EDIT===
$(document).ready(function(){
var model = {
selectedType: 2 //default Type
};
var viewModel = {
selectedType: ko.observable(model.selectedType)
, availableTypes: ko.observableArray([
{ Label: 'Type1', Code: 1 }
, { Label: 'Type2', Code: 2 }
, { Label: 'Type3', Code: 3 }
])
};
ko.applyBindings(viewModel, $("#content")[0]);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="content">
<select data-bind="value: selectedType, options: availableTypes, optionsText: 'Label', optionsValue: 'Code', optionsCaption: 'Please select...'"></select><br/>
You've selected Type: <span data-bind="text: selectedType"></span>
</div>
Upvotes: 2