5 Replies Latest reply on Apr 12, 2016 6:25 PM by John 'JB' Brock-Oracle

    Multi select does not work when binding options from REST call

    2930948

      Hello,

       

      I am having trouble with multi select. The problem is when I get the data for the options from the REST call then upon selecting the option it shows it's value in the select and not the label.

      Here is some example code for the problem (for the simplicity of the example let's assume that the view model is properly bind to the select element, $ variable is jQuery object, url is variable containing the endpoint url of the server, the REST call is successful and the returned data is in following format:

           [{value: '1', label:'Label for item 1'}, {value: '2', label: 'Label for item 2'}, {value: '3', label: 'Label for item 3'}]

      ):

       

      HTML snippet:

      <label for="select">Select with options array</label>
      <select id="select" data-bind="ojComponent: {component: 'ojSelect', options: browsers, multiple: true,
                                                   rootAttributes: {style:'max-width:20em'}}">
      </select>
      

       

       

      and Javascript

      function selectModel () {
          var self = this;
          this.browsers = ko.observableArray([]);
           $.getJSON(url, function(data) {
                self.browsers(data);
           });
        }
      

      The problem is that in this example the options in the select are correct - Label for item 1, Label for item 2, .... But when one of them is selected then inside the select box it's value is shown eg. '1' instead of the label. This is problem only with multi select and when setting options in the callback from the REST call, single select works fine.

       

      Any ideas how to solve this?

       

      Thank you

      Vasek Stebra

        • 1. Re: Multi select does not work when binding options from REST call
          John 'JB' Brock-Oracle

          Hi Vasek,

           

          Changing your data to look like this:

          [

          {"value": "1", "label":"Label for item 1"},

          {"value": "2", "label": "Label for item 2"},

          {"value": "3", "label": "Label for item 3"}

          ]

           

          Corrects the behavior in my test code.  What you have shown is not valid JSON.

           

          Hope that helps

          • 2. Re: Re: Multi select does not work when binding options from REST call
            2930948

            Hi John,

             

            thank you for your reply. Yes what I shown is not valid JSON, what I shown is the JSON already converted to Javascript array - I apologize for misunderstanding - didn't realize that. However in my code I use valid JSON (generated by json_encode function in PHP) and it still does not work as expected.

             

            Let's take the JSON out of the game and try this simpler example, which simulates the delay when doing the REST call and also does not work as expected. After the 2 second, the options for the select are set correctly, but again when one of them is selected it's value instead of the label appears in the select box.

             

            <label for="select">Select with options array</label>  
            <select id="select" data-bind="ojComponent: {component: 'ojSelect', options: browsers, multiple: true,  
                                                         rootAttributes: {style:'max-width:20em'}}">  
            </select>
            

             

            function selectModel () {  
                 var self = this;  
                 this.browsers = ko.observableArray([]);  
                 window.setTimeout(function(){
                      var data = [{value: '1', label:'Label for item 1'}, {value: '2', label: 'Label for item 2'}, {value: '3', label: 'Label for item 3'}];
                      self.browsers(data);
                 }, 2000);
             }  
            

             

            Thank you

            Vasek

            • 3. Re: Re: Multi select does not work when binding options from REST call
              Jim.Marion-Oracle

              I put together a jsfiddle with your example and then started fiddling with it. It is very interesting that just commenting out the window.setTimeout makes it behave properly. This makes me question the way the bindings and observables behave.

               

              I noticed that you are completely replacing the array referenced by the observable. In most cases, that seems to be acceptable. I have trouble with that sometimes, though, so I use the ko helper methods to update the originally bound array rather than replace the array. Here is a jsFiddle that uses ko.utils.arrayPushAll and valueHasMutated to push the Ajax response into the observable's existing array. This version works the way you expect:

               

              https://jsfiddle.net/jmarion/8079eeoc/

              • 4. Re: Multi select does not work when binding options from REST call
                2930948

                Hello Jim,

                 

                thank you, your solution works great for me.

                • 5. Re: Re: Multi select does not work when binding options from REST call
                  John 'JB' Brock-Oracle

                  The other option for trying to control when the DOM is loaded versus when the data is ready, is to use a Knockout virtual "if" binding.

                   

                  Using your code, it would look like this in the HTML

                   

                  <!-- ko if: dataReady -->
                  <label for="select">Select with options array</label>   
                  <select id="select" data-bind="ojComponent: {component: 'ojSelect', options: browsers, multiple: true,   
                                                              rootAttributes: {style:'max-width:20em'}}">   
                  </select>
                  <!-- /ko -->
                  

                   

                   

                  Then in the JavaScript you add the ko observable for dataReady and initialize it to false.  Once the data has been returned, you set the value to true.  I modified your getJSON call to use the Promise ( .then ) provided by jQuery as well in the code below.

                   

                  function selectModel () {  
                      var self = this;
                       self.browsers = ko.observableArray([]);  
                       self.dataReady = ko.observable(false);
                  
                       $.getJSON(url).then(function(data) {  
                            self.browsers(data);
                            self.dataReady(true);  
                       });  
                    }  
                  

                   

                  Hope this helps others down the road.  It's a really common use case.