12
votes

Say I have the following ember-data model:

App.Person = DS.Model.extend({
    firstName: DS.attr('string'),
    lastName:  DS.attr('string'),
    starred:   DS.attr('boolean')
});

This communicates with a Rails app with the following pretty standard CRUD API:

GET    /people    - get a list of people
POST   /people    - create a new person
GET    /people/id - get a specific person
PUT    /people/id - update a specific person
DELETE /people/id - delete a specific person

This all maps to Ember-Data with the standard Store/Adapter.

However, lets say that in order to "star" or "unstar" a person, the API doesn't let us do this by the standard update action. There's a specific API endpoint for this action:

POST   /people/id/star   - mark a person as "starred"
POST   /people/id/unstar - mark a person as "unstarred"

How do I fit this API in with Ember Data?

It looks like I'd need to extend DS.Store and DS.RESTAdapter somehow, but I'm not sure of the best approach to make them aware of these different actions. It also feels a bit wrong that a generic Adapter for the app has to be aware of starring people.

Note that I have no control over the API, so I can't make POST /people/id aware of "starring" so that it would fit in with a standard update.

5
Could you accept my answer? The currently accepted one is no longer accurateandorov

5 Answers

-1
votes

In the discussion group, Yehuda mentioned this isn't possible yet.

10
votes

Been a while and it may not have been possible at the time, but you can call the ajax method of your adapter directly:

YourApp.Store.prototype.adapter.ajax(url, action, {data: hashOfParams})

For example:

YourApp.Store.prototype.adapter.ajax('/products', 'GET', {data: {ids: [1,2,3]}})

For your question:

YourApp.Store.prototype.adapter.ajax('/people' + id + '/star','POST')

Edit - using buildURL is useful, particularly if you've set a namespace on your adapter:

url = YourApp.Store.prototype.adapter.buildURL('people',id) + '/star'

Edit 2 - You can also get the adapter with container.lookup('adapter:application'), which is useful if you don't have global access to the app (ex ES6 modules / ember-cli)

Edit 3 - The above refers to an outdated version of Ember / Ember-CLI. Now I define this function in a mixin -

  customAjax: (method, type, id, action, hash = null) ->
    #note that you can now also get the adapter from the store -
    #adapter = @store.adapterFor('application')
    adapter = @container.lookup('adapter:application')
    url = adapter.buildURL(type, id) + '/' + action
    hash['data'] = $.extend({}, hash) if hash #because rails
    adapter.ajax(url, method, hash).then (result) =>
      return result

And call it like so -

@customAjax('PUT', 'modelClass', modelId, 'nonCRUDActionName', optionalHashOfAdditionalData).then (response) =>
  #do something with response
2
votes

I went through the same issue, and solved it using a modified version of what is explained in here: Non-crud rails actions in Ember.js

Basically, you need to use a Jquery AJAX call, the important part for me was to change the contentType in order to get the auth data to be sent in the request headers, rather than in form-data (the default, if you include data in your call). Also, make sure to give the proper context in the AJAX part, otherwise 'this' won't work in the 'success' callback:

App.ItemController = Ember.ObjectController.extend
    actions:
        starItem: ->
            controller = this
            item = @get 'model'
            id = item.id
            token = @auth.get 'authToken'
            $.ajax
                type: 'PUT'
                context: controller
                data: '{ "auth_token": "' + token + '"}'
                url: '/api/v1/items/' + id + '/star'
                contentType: 'application/json; charset=UTF-8'
                dataType: 'json'
                success: ->
                    if @get 'item.starred_by_user'
                        @set 'model.starred_by_user', false
                        item.decrementProperty('total_stars') 
                    else 
                        @set 'model.starred_by_user', true
                        item.incrementProperty('total_stars')
0
votes

Maybe a good start: https://github.com/beautifulnode/ember.request

Did not tryed it yet, but seems promising in your case...

0
votes

I think, that in CQRS therms star and unstar are commands. So you should declare model PersonStar and work with it.