4 Replies Latest reply on Apr 12, 2016 7:06 PM by Jim.Marion-Oracle

    Film strip is not working in Promise API calls

    2769831

      Hi all,

       

       

      I need to have a film strip whose data is coming back from an API call. I put the observable array in a  Promise-based API call and the film strip is broken and it becomes a common div without any response.

       

      For example:

       

          var tmp= [

                  { name: 'Hydrogen' },

                  { name: 'Helium' },

                  { name: 'Lithium' },

                  { name: 'Beryllium' },

                  { name: 'Boron' },

                  { name: 'Carbon' },

                  { name: 'Nitrogen' },

                  { name: 'Oxygen' },

                  { name: 'Fluorine' },

                  { name: 'Neon' },

                  { name: 'Sodium' },

                  { name: 'Magnesium' }

              ];

              self.chemicals(tmp);

       

      If I put the above in the main block, it's fine but if I put it in an async Promise-based API call like below, it's broken:

       

      someAPI.getChemical (queryParams)

                  .then(function (c) {

                 var tmp= [

                  { name: 'Hydrogen' },

                  { name: 'Helium' },

                  { name: 'Lithium' },

                  { name: 'Beryllium' },

                  { name: 'Boron' },

                  { name: 'Carbon' },

                  { name: 'Nitrogen' },

                  { name: 'Oxygen' },

                  { name: 'Fluorine' },

                  { name: 'Neon' },

                  { name: 'Sodium' },

                  { name: 'Magnesium' }

              ];

              self.chemicals(tmp);

                  })

       

       

      Can anyone please help? Any comments and opinions are much appreciated!

       

      Regards,

      Quincy

        • 1. Re: Film strip is not working in Promise API calls
          Jim.Marion-Oracle

          I have two suggestions:

           

          #1: Don't replace the array within the observableArray. Yes, sometimes that works. Sometimes it doesn't. It seems to depend on the implementation of the underlying component and how it binds to the observable and the observable's data. Instead, populate the observableArray itself when the promise is fulfilled. My favorite way to do this looks something like this:

           

          ko.utils.arrayPushAll(self.chemicals(), c);
          self.chemicals.valueHasMutated();
          

           

          If you need to manipulate the data returned by the promise, then assign the observable array to a temp variable and then push into the array OUTSIDE of the observable construct. Then invoke valueHasMutated. This will keep ko from attempting to update the DOM on every push.

           

          You can see an example of something like this here: Multi select does not work when binding options from REST call. This example uses window.setTimeout to simulate the asynchronous nature of Ajax, causing the UI to render before the data is ready. jsFiddle here: https://jsfiddle.net/jmarion/8079eeoc/.

           

          #2: Use the if binding to keep the chemicals array out of the DOM until the promise resolves and you have real data. Basically, you wrap the DOM that uses the chemicals binding in an if binding and then ko won't know about/process the binding until AFTER the data is ready.

           

          I like option #1 because I like to make as much of the UI visible/available as soon as possible. But if that doesn't work, then I fall back on option 2.

          1 person found this helpful
          • 2. Re: Film strip is not working in Promise API calls
            2769831

            The solution 2 is working! Let me try solution 1! Thank you very much Jim!

             

            Regards,

            Quincy

            • 3. Re: Film strip is not working in Promise API calls
              2769831

              Hi Jim,

               

              Thank you for your help!

               

              I'm trying the first solution and I have some questions:

               

              When you say "push into the array OUTSIDE of the observable construct", what do you mean by that? Do you mean I do push outside Promise? Or I do ko.utils.arrayPushAll(self.chemicals(), c) in the Promise?

               

              Thank you again for your kind help! I really appreciate it!

               

              Regards,

              Quincy

              1 person found this helpful
              • 4. Re: Re: Film strip is not working in Promise API calls
                Jim.Marion-Oracle

                Great question. Do as you said:

                 

                do ko.utils.arrayPushAll(self.chemicals(), c) in the Promise

                 

                If your Ajax response data matches exactly what your view expects, then you are all set. I didn't want to assume that was the case. If you have to manipulate the data first (using forEach or map or something), that is when my extra piece of advice matters. When pushing data into an observableArray item-by-item, you have two options: use the observableArray methods or work with the raw array directly. If you are only updating one row, then using the observableArray array methods is best because that keeps the view and model in sync. In this case, however, you are pushing all of the data into the array at once. In this case, for performance reasons, "the experts" recommend that you work with the raw array directly. When done, you can trigger the view by invoking valueHasMutated.