Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Non CRUD actions with ember-data

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.

like image 216
rlivsey Avatar asked Mar 14 '12 16:03

rlivsey


2 Answers

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
like image 61
andorov Avatar answered Nov 14 '22 00:11

andorov


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')
like image 36
Halogen Avatar answered Nov 13 '22 23:11

Halogen