1

Let me explain shortly what I mean. I'm writing an app that makes use of Ember and Rails. Every employee can give other co-workers a kudo. He can't know how many others received. I can only know how many left kudos I have to give and how many I received from others.

But I can't really understand how to communicate with Rails. As a picture is worth a thousand words, please take a look at this: kudos app http://wstaw.org/m/2013/03/25/kudos.png

When the app starts I load a list of users pointing to /users. Current user is at /currentuser. Current user differs in that it has additional fields like kudosReceived and kudosLeft.

Let's suppose I'd like to add Joe a kudo. I click on a button, and then, what? Suppose that when I go to /kudos/userid rails will do its magic. It just adds Joe a kudo. But I'd also like to get the updated current user data (remember, it is at /currentuser).

What should I send? Should I first use CREATE or UPDATE method on the controller? How can I obtain current user new data? In a callback? Or should I use /currentuser with GET. But when?

This is what I have now, but I'm completely unsure how correct it is

// application.handlebars
<h1>My Kudos</h1>

<div id="kudos-content">
  <ul id="kudos-list">
    {{#with controllers.current}}
    <li>
      <p>{{firstName}} {{lastName}}</p>
      <p><img {{bindAttr src="imageUrl"}}  /></p>
      <p>received: {{kudosReceived}}</p>
      <p>left: {{kudosLeft}}</p>
    </li>
    {{/with}}
    {{#each user in controllers.users}}
    <li>
        <p>{{user.firstName}} {{user.lastName}}</p>
        <img {{bindAttr src="user.imageUrl"}}  />
        <a href="#" {{action "addKudo" user.id target="controller.controllers.kudo"}}>(+)</a>
    </li>
    {{/each}}
  </ul>
</div>

Sks.KudoController = Ember.ObjectController.extend({
    addKudo: function(userId) {
      // create a new record on a local transaction
      this.transaction = this.get('store').transaction();
      // don't really know what next
    }
});

The next problem is how to use /kudo/userid in {{action}}

wryrych
  • 1,755
  • 3
  • 19
  • 31

1 Answers1

1

It sounds like it isn't necessary to care about the Kudo in the client as anything but a counter. The client doesn't know where any particular Kudo came from or anything else about it. In that case, it should be sufficient to treat Kudo as nothing more than a -1 on the number of Kudos the current user has left.

If this is the case, then creating a DS.Model class for Kudo, worrying about relationships, storing everything about a Kudo in the store, and posting JSON to the server is 100% overkill. This is ESPECIALLY true if the server doesn't expect JSON for a Kudo and simply creates a Kudo server-side for a target user, or increments a counter, or whatever.

If simply hitting that URL is all that is necessary, and it sounds like it is, then I'd suggest simply using jQuery.ajax() to tap the URL. I'd also assume that a successful response means that the current user has one less kudosLeft, and I'd simply handle that in the success callback, rather than worrying about syncing the currentUser model to the server. If that isn't ideal, you can call reload on the currentUser to fetch the new data manually, assuming the server has been keeping track of kudosLeft on the server-side model.

First, add needs: ['current'] to your KudoController so you have access to the currentUser.

Then in addKudo

addKudo: function(user) {
    jQuery.post('/kudo/' + user.get('id'), jQuery.proxy(function () {
          this.decrementProperty('controllers.current.kudosLeft');
        }, this));
}

This assumes that CurrentController is an ObjectController so that it passes the set through to its content.

Finally, update your {{action}} to include the full User object instead of just the id:

<a href="#" {{action "addKudo" user target="controller.controllers.kudo"}}>(+)</a>

A few notes:

(1) It is possible using this scheme for the client to become out of sync with the server, if for instance, the user has another copy of the app open and sends Kudos there. This may or may not be a problem. In Stack Overflow, for instance, the UI dynamically updates when votes or accepted answer status changes. It might be accepted in your case to simply not care about keeping every little thing synced if it isn't of significance. What you might do in this case is have your /kudos endpoint return a particular status code if the client attempts to send a kudo when the user has none left. The callback on the ajax call could test for that status and trigger a reload on the user, or just reset the kudosLeft property to 0.

(2) Setting the kudosLeft property like this will mark the record as dirty and thus will cause an update when the store is committed. What you can do is have a computed property, such as displayKudosLeft that is initially set to the kudosLeft attribute, but can be changed by the app to update the display without marking the record as dirty.

As for when to use ember-data, and when to use $.ajax, well, that depends. How large is your API and how RESTful is it? Do you use active_model_serializers, which play nicely with ember-data? Do you have a lot of relationships that need to be reflected on the client? Are you doing a lot of "one-off" updating of attributes, or hitting API end points that don't actually expect to receive JSON in order to have their effect?

There are huge apps (or, at least one) that don't use ember-data and do just fine. Discourse, for instance, uses a thin wrapper around $.ajax, and it does just fine.

Christopher Swasey
  • 9,842
  • 1
  • 29
  • 23
  • What do you mean by 'no actual Kudo resource exists' or 'modelling'? Also will decrementProperty update it in the database? Sorry If I didn't make it clear but I'd like to receive updated kudosLeft when POST is successful. Could you also tell me when should I just use jQuery Ajax methods and when Ember functionality? – wryrych Mar 25 '13 at 18:24
  • By "no actual kudo resource exists" I mean if the Rails app isn't creating a `Kudo` record, and is instead setting a property on a User object. By "doesn't need to be modeled" I mean that if the only data you actually care about on the client is the `kudosLeft` property of the current user, then creating a model for `Kudo`, storing it in the store, and posting JSON is 100% overkill. "Modeling" a concept means to treat it as a fully realizable "object" in and of itself, with its own class and properties. If a `Kudo` has a class and instances, it is modeled. If it is simply reflected in a.. – Christopher Swasey Mar 25 '13 at 18:30
  • counter, then it is not modeled. When you should use $.ajax and when you should use ember-data has a lot to do with the complexity of your data, what you're trying to accomplish, what your API looks like, etc. There are HUGE apps (such as Discourse) which ONLY wrap $.ajax for their server interactions. As for receiving an updated kudosLeft property, you can call `reload` on the current user record, if you're using ember-data, to fetch the updated record. My answer assumes that interpreting a succesful post to kudos is assurance enough that `kudosLeft` is simply one less than what it was before – Christopher Swasey Mar 25 '13 at 18:34
  • Thank you very much, Christopher! What books/resources do you mean should I read to get a better handle of it? I have almost finished my Rails training but still have not so good understanding of its MVC or RESTful apps. – wryrych Mar 25 '13 at 18:44
  • I've updated my answer to clarify with some of these points. A good resource is stack overflow, believe it or not. Questions like http://stackoverflow.com/questions/671118/what-exactly-is-restful-programming are great for getting a general overview of these concepts that tend to get conflated with very specific feature implementations. Maintaining a RESTful API is one of the best things you can do to integrate cleanly with ember-data. – Christopher Swasey Mar 25 '13 at 18:58
  • Brilliant! As for user.incrementProperty('kudosReceived'); I don't think I should do this. As I wrote /me shouldn't know how many other co-workers received - it is not displayed in the UI. I think that request to POST should do the trick. Or did I missed the point? – wryrych Mar 25 '13 at 19:16
  • Oh sorry, I kept in mind that the user wouldn't know WHO sent the kudos and skimmed over the fact that they wouldn't know how many. I'll remove that part of my answer. – Christopher Swasey Mar 25 '13 at 19:20