Search code examples
ember.jsember-data

Emberjs not converting JSON to model array


From my Emberjs I am making a custom request to an explore route on my Rails server:

GET http://localhost:3000/explore

I see my JSON response in my Google Chrome network inspector, however, my page isn't rendering anything.

To make the custom request, I have a book adapter:

Book Adapter

import ApplicationAdapter from './application';
import Ember from 'ember';

export default ApplicationAdapter.extend({
  apiManager: Ember.inject.service(),

  findPublishedBooks: function(store, type) {
    let jwt = this.get('apiManager').get('jwt');

    return Ember.RSVP.resolve(
      Ember.$.ajax({
        type: "GET",
        url: this.get('apiManager').requestURL('explore'),
        dataType: 'json',
        headers: {"Authorization": "Bearer " + jwt}
      })
    );
  }
});

Explore Route

model() {
  const adapter = this.get('store').adapterFor('book');
  return adapter.findPublishedBooks();
}

On my Rails side, I have this for my Explore action:

Rails Explore Action

def explore
  books = Book.where(published: true)

  if books.count > 0
    render json: books
  else
    return nil
  end
end

I know I must be doing something wrong, probably on my Ember side.

Update

Explore template

<div class="explore">
  {{#search-field searchAction=(action "searchBooks") clearAction=(action "clearSearchBooks")}}{{/search-field}}
  {{#book-grid books=model class="book-grid" isSearching=isSearching}}{{/book-grid}}
</div>

Book-Grid Template

{{#each books as |book|}}
  {{#link-to "books.book" book}}
    <div class="book">
      <div class="book-cover">
        <img src={{book.cover.cover.url}} alt="{{book.title}} book cover image" width=173 height=231>
      </div>
      <div class="caption">
        {{book.title}}<br>
        <label>by {{book.author.username}}</label>
      </div>
    </div>
  {{/link-to}}
{{else}}
  {{#if isSearching}}
    <label>No search results.</label>
  {{else}}
    <label>There are no published books at the moment.</label>
  {{/if}}
{{/each}}

Solution

  • I think you need to understand that with your code you basically work around the ember-data store. For ember-data to work you have to keep all your data in the store, which means you can't just manually call your adapter. You always have to call the store, and the store will call the adapter.

    So this is an anti pattern:

    const adapter = this.get('store').adapterFor('book');
    return adapter.findPublishedBooks();
    

    Because it won't push the data to the store, or serialize them.

    What you instead should do is to use the query function on the store.

    You can call store.query('myModel', anything) if you want a server-side filtered list. The second variable will directly be passed to the adapter.

    So you would call return store.query('book', { published:true }) in your route, and then implement it in your BookAdapter with something like this:

    apiManager: Ember.inject.service(),
    
    query: function(store, type, query) {
      if(query.published) {
        let jwt = this.get('apiManager').get('jwt');
    
        return Ember.RSVP.resolve(
          Ember.$.ajax({
            type: "GET",
            url: this.get('apiManager').requestURL('explore'),
            dataType: 'json',
            headers: {"Authorization": "Bearer " + jwt}
          })
        );
      }
    }
    

    Also make sure you return the right data structure. ember-data expects a JSONAPI response, unless you changed that in your serializer.