Shrabanee
Shrabanee

Reputation: 2766

Why subscribe event of dropdown is being called on load knockoutJs?

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 subscribefor 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

Answers (2)

user3297291
user3297291

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

A.Sri
A.Sri

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

Related Questions