Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Knockout select binding, not remembering value when options added late

I am using knockout to create a select element, the options have to be set late (the options are set by loaded them from a server). This is causing the initial value to be lost. Below I have some working code it does what I want, but with the loading from server replaced with a static table.

If the line setupSelect(); is moved to the end of the script (this simulates the asynchronous ajax call to the server), then the select asks me to choose.

I think that when there are no choices the value is overwritten, then the choices arrive, but the value is now null.

It looks like I know what the problem is, but don't know how to get it to work.

Can you tell me how to get it to work?

<!DOCTYPE HTML>
<html>
  <head>
    <title></title>
    <script src="http://cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-min.js" ></script>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js" ></script>
  </head>
  <body>
   <p>
      Your thing:
      <select data-bind="options:      (function(){return $root.select1.rows;})(), 
                         optionsText:  function(item){return item.name;},
                         optionsValue: function(item){return item.id;},
                         value: selectedThing1, 
                         optionsCaption: 'Choose...'">
      </select>

      <span data-bind="visible: selectedThing1">
        You have chosen a thing with id
        <span data-bind="text: selectedThing1() ? 
                         selectedThing1() : 
                         'unknown'">
        </span>.
      </span>
    </p>

    <script type="text/javascript">  
      var viewModel = {
          select: {rows: ko.observableArray() },
          selectedThing : ko.observable() // Nothing selected by default
      };

      function setupSelect(){
      //in the real application rows_raw is populated from a server using $.ajax
          var rows_raw= [
              {name: "a thing",        id:1},
              {name: "one more thing", id:2},
              {name: "another thing",  id:3}
          ];

          $.each(rows_raw, function(index, item){
              viewModel.select.rows.push(item);
          });
      }

      //setupSelect(); //when loading from server (using $.ajax in async mode), then this line is effectivly moved to the end of the script.

      viewModel.selectedThing(2); //set ititial value
      ko.applyBindings(viewModel);

      setupSelect(); //when loading from server (using $.ajax in async mode), then this line is effectivly moved to the end of the script.
    </script>
  </body> 
</html>

You can also see both examples here http://jsfiddle.net/V33NT/1/

like image 375
ctrl-alt-delor Avatar asked Mar 05 '14 13:03

ctrl-alt-delor


People also ask

How do I assign a value to Knockout observable?

To create an observable, assign the ko. observable function to the variable. A default value can be specified in the constructor of the call. Knockout then converts your variable into a function and tracks when the value changes, in order to notify the UI elements associated with the variable.

What is $data in Knockout?

The $data variable is a built-in variable used to refer to the current object being bound. In the example this is the one of the elements in the viewModel.

What is two-way binding in knockout JS?

KO is able to create a two-way binding if you use value to link a form element to an Observable property, so that the changes between them are exchanged among them. If you refer a simple property on ViewModel, KO will set the form element's initial state to property value.

What is binding in Knockout?

Essentially a binding or a data binding is a way to link your ViewModels to your Views(templates) and vice versa. KnockoutJS uses two-way data binding, which means changes to your ViewModel influence the View and changes to your View can influence the ViewModel.

What is options binding in KnockoutJS?

KnockoutJS - Options Binding. This binding is used to define the options for a select element. This can be used for either drop-down list or a multi-select list. This binding cannot be used with anything other than <select> elements.

How do I set the selected value in knockout?

You can even nest options within <optgroup> elements and Knockout will set the selected value appropriately. Normally, when you use the value binding on a <select> element, it means that you want the associated model value to describe which item in the <select> is selected.

Does knockout support drop-down lists?

Knockout has special support for drop-down lists (i.e., <select> elements). The value binding works in conjunction with the options binding to let you read and write values that are arbitrary JavaScript objects, not just string values. This is very useful if you want to let the user select from a set of model objects.

Why should I use textinput instead of valueupdate in knockout?

If you are trying to bind an <input type="text" /> or <textarea> to get instant updates to your viewmodel, use the the textInput binding. It has better support for browser edge cases than any combination of valueUpdate options. Knockout has special support for drop-down lists (i.e., <select> elements).


1 Answers

This is default bahavior: Knockout forces the value to match an existing option, if there is no existings option it unsets the observable.

However there is new setting in KO 3.1. which is called valueAllowUnset and it is addressing exactly this scenario.

From Knockout.js 3.1 Released

  • With this option set to true, Knockout does not force the value to match an existing option.
  • The selection will be set to an empty option in the case of a mismatch, but the value is not overwritten.
  • This is very useful in scenarios where options are lazily loaded and there is an existing value.

So if you upgrade to Knockout.js 3.1 you can write

<select data-bind="options:      (function(){return $root.select2.rows;})(), 
                   optionsText:  function(item){return item.name;},
                   optionsValue: function(item){return item.id;},
                   value: selectedThing2, 
                   valueAllowUnset: true, 
                   optionsCaption: 'Choose...'">

Demo JSFIddle.

like image 191
nemesv Avatar answered Oct 03 '22 12:10

nemesv