Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

findRecord for side loading payloads expects not primary records were arrays #3805

Closed
novtor opened this issue Sep 29, 2015 · 15 comments · Fixed by #4314
Closed

findRecord for side loading payloads expects not primary records were arrays #3805

novtor opened this issue Sep 29, 2015 · 15 comments · Fixed by #4314

Comments

@novtor
Copy link

novtor commented Sep 29, 2015

I have two models, let's say Car and Human. The Car model has a human owner in it:

Car = DS.Model({
     .....
     owner: DS.belongsTo('human', {async: false});
});

When I load a car:

store.findRecord('car', id);

I see the following error in the console:
Error occured. Transition: Transition error: TypeError: arrayHash.forEach is not a function
at ember$data$lib$serializers$json$serializer$$default.extend._normalizeArray (http://localhost:9000/helios/bower_components/ember-data/ember-data.js:11413:19)
at ember$data$lib$serializers$json$serializer$$default.extend._normalizeResponse (http://localhost:9000/helios/bower_components/ember-data/ember-data.js:11535:38)
at ember$data$lib$system$serializer$$default.extend.normalizeSingleResponse (http://localhost:9000/helios/bower_components/ember-data/ember-data.js:9738:21)
at ember$data$lib$system$serializer$$default.extend.normalizeFindRecordResponse (http://localhost:9000/helios/bower_components/ember-data/ember-data.js:9595:45)
at ember$data$lib$system$serializer$$default.extend.normalizeResponse (http://localhost:9000/helios/bower_components/ember-data/ember-data.js:9563:53)
at ember$data$lib$system$store$serializer$response$$normalizeResponseHelper (http://localhost:9000/helios/bower_components/ember-data/ember-data.js:3529:43)
at http://localhost:9000/helios/bower_components/ember-data/ember-data.js:3729:25
at Object.Backburner.run (http://localhost:9000/helios/bower_components/ember/ember.debug.js:284:25)
at ember$data$lib$system$store$$Service.extend._adapterRun (http://localhost:9000/helios/bower_components/ember-data/ember-data.js:9104:33)
at http://localhost:9000/helios/bower_components/ember-data/ember-data.js:3728:22

My payload looks like:

{
  human: {
    id: 1,
    ....
  },
  car: {
   ....
   owner: 1
  }
}

Debugging the ember-data code I saw:
line 11520:

      if (isPrimary && Ember.typeOf(value) !== 'array') {
          ........
      }

So AFAIU findRecord expects the sideloaded entities were arrays in payload. So it expects something like:

{
  humans: [{   // ARRAY !!!!
   id: 1,
   ....
  }],
  car: {
   ....
   owner: 1
  }
}

Modifying the payload representation in this manner solves the issue.
It used to work with 1.13.
Ember-data v2.0.0 (2.0.1 also tested)

@HeroicEric
Copy link
Sponsor Member

@novtor which serializer are you using?

@pangratz
Copy link
Member

pangratz commented Oct 6, 2015

@novtor Are you sure this worked in 1.13? I cannot reproduce: http://emberjs.jsbin.com/didero/edit?html,js,output. If you un-comment authors: [...] in the mock response, then it works.


If the sideloading of a single resource should be supported, a fix would be to make sure the value is an array before this line. Something like:

value = Ember.makeArray(value);
let { data, included } = this._normalizeArray(store, typeName, value, prop);

@novtor
Copy link
Author

novtor commented Oct 7, 2015

@HeroicEric it is RESTSerializer
I reproduced the issue but now I am not sure if it is a bug:
http://emberjs.jsbin.com/yobefefure/edit?html,js,output
Just change the ember version to see the stacktrace I sent in my first post.
In fact, on this particular route I did not use this relationship that is why it used to work with 1.13, if you put book.author.name in the template it does not work with 1.13 neigther even if the stacktrace is different.

@pangratz
Copy link
Member

pangratz commented Oct 7, 2015

Just change the ember version to see the stacktrace I sent in my first post.

To what versions of ember and ember-data exactly?

@novtor
Copy link
Author

novtor commented Oct 7, 2015

2.0.0 both

@pangratz
Copy link
Member

pangratz commented Oct 7, 2015

It used to work with 1.13.

OK, you said that sideloading single resources worked in 1.13, right? Can you create a working JSBin with 1.13? This would help greatly in tracking down when this regression has been introduced.

@novtor
Copy link
Author

novtor commented Oct 7, 2015

@pangratz , this is the one that I have posted:
http://emberjs.jsbin.com/yobefefure/edit?html,js,output
it works with 1.13.
I will try to explain more clearly. I have a code similar to the jsbin I provided. On some route, book is loaded by id and its author is sideloaded as 'author' but in the template of this route, the relationship author is not used, so book.author is never called on this route (it is called on another route, but on that route both books and authors are always loaded as arrays so that is why I have never had issues with 1.13). Now with 2.0.0 the sideloaded 'author' raises an error even if it is never used. As I said I am not sure it is a bug, perhaps it is an expected behavior. Just I think the error message in 1.13 was more clear.

1.13.x - "... async: false relationship 'author' on 'book' is not loaded.. " (or something similar)
2.0.0 " .... arrayHash.forEach is not a function..". With lots of requests and sideloadings it is not easy to figure out which server response causing the problem.

@HeroicEric
Copy link
Sponsor Member

@novtor async became the default for associations in ember-data 2.0 #3220

You can get the old behavior back by setting async to false on the relationship in your model.

I agree that the error message could be improved. What do you think would have been a good message to see?

@pangratz
Copy link
Member

pangratz commented Oct 7, 2015

@novtor Oh, ok. Now I understand. Thanks for the clarification.

@novtor
Copy link
Author

novtor commented Oct 7, 2015

@HeroicEric , @pangratz thank you for your replies.
Concerning the issue. As it always happen, there are two ways:

  1. Let ember-data the possibility to deserialize the 'author' as a single object. Because, anyway, the deserializer finds the author in the payload so IMHO if it finds it, it should be able to handle it.
  2. Deserializer should only look for 'authors' in the payload for the secondary records so in case it does not find it the message would be quite similar to 1.13 messge:

"sync loading of the relation 'author' failed on the model 'book', the expected item was abscent in the payload 'authors'"

I think the first solution is better but perhaps is more difficult to acheive. The second one will not be very efficient in case of irregular inflectors usage, someone can for example do:

Ember.Inflector.inflector.irregular('author", "author")

Which will make the second solution to fall into the same problem as described in the issue

@cjc343
Copy link

cjc343 commented Feb 5, 2016

I am also seeing this when trying to upgrade from 1.13.X to 2.3.0. As described, @novtor's jsbin works with 1.13, but change the tags to 2.3.0 and I hit an arrayHash.forEach is not a function due to _normalizeArray getting called for the sideloaded author: http://emberjs.jsbin.com/lunibiceso/edit?html,js,console,output

@kulbida
Copy link

kulbida commented Feb 21, 2016

I'm having exactly the same issue....

@cmccand
Copy link

cmccand commented Mar 23, 2016

Anyone have any fixes for this?

@Phoxly
Copy link

Phoxly commented Apr 8, 2016

Having the exact same issue. It's halted our migration from 1.13 to 2.0.

@fivetanley
Copy link
Member

As a workaround you could override normalizeFindRecordResponse to change it to the expected

export default DS.RESTSerializer.extend({
    normalizeFindRecordResponse: function(store, primaryModelClass, payload, id, requestType) {
      if ('author' in payload) {
        payload.authors = Ember.makeArray(payload.author);
        delete payload.author;
      }
      return this._super(store, primaryModelClass, payload, id, requestType);
    }
});

http://emberjs.jsbin.com/cijone/1/edit?html,js,console,output

fivetanley pushed a commit to fivetanley/data that referenced this issue Apr 8, 2016
Before, `_normalizeArray` would call `forEach` even if the object wasn't
an array. We guard against it by using `Ember.makeArray`.

fixes emberjs#3805

emberjs#3805
fivetanley pushed a commit to fivetanley/data that referenced this issue Apr 8, 2016
Before, `_normalizeArray` would call `forEach` even if the object wasn't
an array. We guard against it by using `Ember.makeArray`.

fixes emberjs#3805

emberjs#3805
@bmac bmac closed this as completed in #4314 Apr 9, 2016
bmac pushed a commit that referenced this issue Apr 12, 2016
Before, `_normalizeArray` would call `forEach` even if the object wasn't
an array. We guard against it by using `Ember.makeArray`.

fixes #3805

#3805
(cherry picked from commit c68d0ef)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants