23

I am using the Ember-Data Rest-Adapter and the JSON returned from my server looks basically like the one in the Active Model Serializers Documentation

{
  "meta": { "total": 10 },
  "posts": [
    { "title": "Post 1", "body": "Hello!" },
    { "title": "Post 2", "body": "Goodbye!" }
  ]
}

Fetching the data from the server works but unfortunately I am not able to figure out where I can access the meta information from my JSON response.

Based on my research in ember-data's github issue, support for meta information seems to be implemented with commit 1787bff.

But even with the test cases I was not able to figure out how to access the meta information.

App.PostController = Ember.ArrayController.extend({
   ....
   requestSearchData: function(searchParams){
      posts = App.Post.find(searchParams);
      this.set('content', posts);
      // don't know how to access meta["total"]
      // but I want to do something like this:
      // this.set('totalCount', meta["total"])
   }
})

Can anybody of you shed some light on this for me, please? I am aware that the Ember api is moving fast but I am sure I am just missing a small part and that this is actually possible.

Michael Klein
  • 785
  • 6
  • 14

3 Answers3

7

I found a cleaner approach for extracting meta information from the server response with ember-data.

We have to tell the serializer which meta-information to expect (in this case pagination):

 App.serializer = DS.RESTSerializer.create();

 App.serializer.configure({ pagination: 'pagination' });

 App.CustomAdapter = DS.RESTAdapter.extend({
   serializer: App.serializer
 });

 App.Store = DS.Store.extend({
   adapter: 'App.CustomAdapter'
 });

After that every time the server sends a meta-property with a pagination object this object will be added to the store's TypeMaps property for the requested Model-Class.

For example with the following response:

  {
    'meta': {'pagination': { 'page': 1, 'total': 10 } },
    'posts':[
      ...
    ]
  }

The TypeMap for the App.Post-Model would include the pagination object after the posts have loaded.

You can't observe the TypeMaps-property of the store directly so I added an computed property to the PostsController to have access to the requests pagination meta information:

 App.PostsController = Ember.ArrayController.extend({
    pagination: function () {
      if (this.get('model.isLoaded')) {
        modelType = this.get('model.type');
        this.get('store').typeMapFor(modelType).metadata.pagination
      }
    }.property('model.isLoaded')
 });

I really don't think that's a great solution to the meta information problem but this is the best solution I was able to come up with yet with Ember-Data. Maybe this will be easier in the future.

Michael Klein
  • 785
  • 6
  • 14
  • One gotcha I've found is that if a metadata value is not returned from the server or it is a blank string then Ember will keep the old value around. I had to resort to returning a string such as "none" instead of a blank value. – Kevin Ansfield Sep 03 '13 at 17:01
6

I figured out a way to access the meta information passed in a response. But unfortunately it really does not seem to be supported by ember-data out of the box and I am writing the meta information to a global variable that I am then accessing via bindings in the requesting controller.

I ended up customizing the serializer the RESTAdapter is using:

App.CustomRESTSerializer = DS.RESTSerializer.extend({
  extractMeta: function(loader, type, json) {
    var meta;
    meta = json[this.configOption(type, 'meta')];
    if (!meta) { return; }
    Ember.set('App.metaDataForLastRequest', meta);
    this._super(loader, type, json);
  }
});

App.Store = DS.Store.extend({
  revision: 11,
  adapter: DS.RESTAdapter.create({
    bulkCommit: false,
    serializer: App.CustomRESTSerializer
  })
});

I am aware that this is not particularly pretty and actually think that this is against what ember-data expects us to do but fot the time being it's working correctly.

I will try to get this working with ember-data in a better way and submit a pull request when it is working or open an issue on github when anybody else is interested in getting this to work.

If anybody finds a more clean solution to this please let me know.

Michael Klein
  • 785
  • 6
  • 14
  • Hi Michael, how are you accessing the global variable "via bindings in the requesting controller?" If I wanted access to App.metaDataForLastRequest.current_user in my ApplicationController, would I do something like: currentUserBinding: 'App.metaDataForLastRequest.current_user' and then use the currentUser variable from within the controller and views? Thanks for any help you can give, or if you've found a better way to do it! – Jack Johnson Apr 16 '13 at 23:48
  • 1
    I posted up my previous comment as a question: http://stackoverflow.com/questions/16070390/ember-js-current-user-accessing-global-variable-from-controller . Any help would be appreciated! – Jack Johnson Apr 17 '13 at 21:33
2

I think the immediate fix for you would be to attach totalCount to your model(recordArray), see this thread.

Another way to go would be to create your own adapter:

DS.Adapter.create({
  find: function (store, type, id) {
    $.ajax({
      url:      type.url,
      dataType: 'jsonp',
      context:  store,
      success:  function(response){
        this.load(type, id, response.data);
      }
    });
  },
  findAll: function(store, type) {
    $.ajax({
      url:      type.url,
      dataType: 'jsonp',
      context:  store,
      success:  function(response){
        this.loadMany(type, response.data);
      }
    });
  }
});

Response parameter in success callback in findAll method, should be an object that you need:

response: {
  meta: {
    totalCount: 10
  },
  posts: [{}, {}]
}

Hope this helps.

Alex Navasardyan
  • 536
  • 5
  • 12