I was wondering if I could get some design help for my ember app.
Here are my three (partial) models:
Payment
- belongsTo Group
- belongsTo User
Group
- hasMany Payments
- hasMany Users
User
- hasMany Payments
- belongsTo Group
I would like to make a table that lists each user in the group and the payment for each user that coordinates with the group. I am currently using links with ember-data. Each user of the group has at most one payment for that group/user combination. I was wondering what the best way to get that payment would be.
I currently use the link groups/:id/users
to get the users for the group. If I wanted to get the payment that belongs to both the user and the group would a link like groups/:group_id/users/:user_id/payment
be an acceptable option? Is there a better way?
Thanks for the help!
In my (limited) experience with Ember Data, youâll save yourself some heartache if you do your sorting/filtering/organizing on the client.
/payments
/groups
/users
/payments/:id
/groups/:id
/users/:id
Once you have all the relevant records in the store, you can use find() to display them how you want, eg:
this.store.find(âpaymentsâ, {/some query here/});
Since youâve defined the relationships in your model already, you can query the store in a manner similar to a database. I know itâs not a very rich REST API, and you can write your own adapter that works the way youâre describing, but this will work out of the box and give you a little more flexibility.
Hope this helps!
Cool thanks Kyle!
I think I will have a computed property in the user controller (which needs group controller) that uses @store.find(âpaymentâ, { user_id: X, group_id: Y }) that is bound by user_id and group_id.
Is there any difference between @store.findQuery and@store.find for this type of lookup? I assume that neither method will search the cache (if given an object as params) but Iâm not to worried about that initially.
find() overloads based on what you pass into it.
find("someModel") // Returns all records
find("someModel", 1) // Returns a single record because you passed in an integer
find("someModel", {name: "Steve"}) // Runs a query because you passed in an object
find() wraps the find(), findAll(), and findQuery() methods into one rad super-method.
When you search the store, youâre always searching the cache first. It only sends off a request when it doesnât find something it needs.
Just a note on âneedsâ- youâre often better off getting data from another controller (or searching the store directly) via the setupController hook on the route. When you âneedâ a controller, youâre capturing its state once, the first time the controller is initialized. The setupController hook fires every time the route is accessed. IMO, this is the more common requirement- âneedsâ has a place, but itâs been more of a corner-case for my stuff.
1 Like
Nice. Thanks again Kyle!
I have a quick questions about needs
. Right now to build my table, I loop through each group.users
and use the UserController
to form all the fields I need (for example Payment like we talked about earlier). For the UserController
to know the current Group, I set the model for the GroupController
and then the UserController
needs the GroupController
and therefore I know the Group. Is this the correct approach? It currently works but I would like to do things by the book.
The book is still being written, and Iâm pretty fresh at this myself, but hereâs my take:
I really only get a lot of use out of the model hook if the template is describing a single, simple thing. Otherwise, Iâll do something like this:
App.AdminRoute = Ember.Route.extend({
setupController: function(controller, model){
this.store.find("image").then(function(images){
controller.set("images", images);
});
this.store.find("pitch").then(function(pitches){
controller.set("pitches", pitches);
});
this.store.find("contact").then(function(contacts){
controller.set("contacts", contacts);
});
actions: {
saveRecord: function(record){
record.save();
}
}
});
You can then refer to these in templates like you would if they were on the model or directly on the controller:
{{#each pitches}}
{{name}}
{{/each}}
Itâs a little bit more DRY to refer to other routeâs models and controllers (which you can even still do with this method), but IMO this offers a little more code clarity and a little less automagic while still maintaining good separation of concerns.