Ember.RSVP.All error Array Methods must be provided an Array

Crosspost from SO

I’m trying to save a model then upon successful save save some additional models related to the intial model. These are in standard input fields that I can’t/don’t know how to bind to ED so I’m just using standard jQuery selectors to find the inputs and save the models. All models are saving properly but Ember.RSVP.all is hitting the REJECT callback due to an Array Methods must be provided an Array error. I’m not sure exactly where I’m supposed to be providing an array…

I’ve inspected various elements along the way and var promises is an array of promises as expected. The only thing I can think of is the results value being passed into my resolve callback is supposed to be an array? If that’s the case I’m not sure how to make it an array other than maybe just passing in Ember.A() or []

Here is the relevant code in my controller.

submit: function () {
   var self = this;
   var division = this.get('model');
   var isNew = division.get('isNew');

   if(isNew) {
      division.setProperties({
         vendor: self.get('parentVendor'),
         vendorCategory: self.get('selectedCategory'),
         isActive: true,
         createdBy: 1,
         createdDate: moment().format()
      });
   } else {
      division.setProperties({
         vendorCategory: self.get('selectedCategory'),
         modifiedBy: 1,
         modifiedDate: moment().format()
      });
   }

   division.validate().then(function () {
      if (division.get('isValid')) {
         division.save().then(function(result) {

            /*
               this bit of hackishness loops through each of the attribute input
               fields, creates a new DS.model for that attribute, saves the model,
               then returns the resulting promise to the promises array so they
               can be processed by RSVP.all to ensure that all the attributes 
               save properly.
             */

            // map the promises array
            var promises = $('input.attribute').map(function () {
               var elem = $(this)[0];
               var vendorTypeAttrId = $(elem).attr('data-attribute-id'),
                  attrValue = $(elem).val();

               // create the model
               var model = self.store.createRecord('vendor-division-attribute', {
                  division: result,
                  vendorTypeAttribute: self.get('categoryAttributes')
                                           .findBy('id', vendorTypeAttrId),
                  value: attrValue,
                  createdBy: 1,
                  createdDate: moment().format()
               });

               // return the save promise
               var p = model.save();
               return p;
            });

            // Make sure all promises are resolved before continuing
            Ember.RSVP.all(promises).then(function (results) {
               // it worked! keep calm and move along
               self.send('closeModal');
               var msg = isNew ? division.get('name') + ' Created!' : 'Changes saved!';
               Bootstrap.NM.push(msg, 'success');
            }).catch(function(reason) {
               // something went wrong - display the error
               if (typeof reason.error !== 'undefined') {
                  // DSP error
                  if (Ember.keys(reason.error).length > 0) {
                     error = reason.error[0].message;
                  } else {
                     error = 'An error occured while saving your changes';
                  }
               } else if (typeof reason.message !== 'undefined') {
                  error = reason.message;
               } else {
                  error = reason;
               }
               console.log(error);
               Bootstrap.NM.push('Error saving Vendor Division: ' + error, 'danger');
            });
            
         }, function(reason) {
            var error;
            if (typeof reason.error !== 'undefined') {
               // DSP error
               if (Ember.keys(reason.error).length > 0) {
                  error = reason.error[0].message;
               } else {
                  error = 'An error occured while saving your changes';
               }
            } else if (typeof reason.message !== 'undefined') {
               error = reason.message;
            } else {
               error = reason;
            }
            console.log(error);
            Bootstrap.NM.push('Error saving Vendor Division: ' + error, 'danger');
         });
      }
   });
}

Resolved on SO.

Your promises array isn’t really an array. jQuery returns an array-like object, not a true array. Try running this code in your console and you’ll see:

var a = $(‘a’).map(function() { return 1; }); console.log(Array.isArray(a)); console.log(a instanceof Array); Luckily, jQuery provides a nice makeArray function to solve this problem. After you declare promises, but before you call Ember.RSVP.all, insert this line:

promises = $.makeArray(promises);

So the issue was actually with $('input.attribute').map(function () { ... } The $.map docs do state that if traversing objects the resulting array isn’t really an array, but an array-like object. Good to know!