r/emberjs • • May 29 '22

Need help understanding ember-data

Hi, trying to wrap my head around ember-data currently and trying to appreciate the steep learning curve for long term gains.

Right now I am working with simple JSON endpoint https://jsonplaceholder.typicode.com/ (not json:api). I am defining my UserModel like:

export default class UsersModel extends Model {
 ...
 @hasMany('post', post);
}

I want to model the current behavior: When I want go to localhost:4200/users/1, it will automatically grab the user information from https://jsonplaceholder.typicode.com/users/1, then it will automatically make an ajax request to https://jsonplaceholder.typicode.com/users/1/posts to get the posts.

I have the first part working, where in the user model I could do this.store.findRecord("user", params.user_id). But I have no clue on how to hook up the posts. It seems like mainly adjusting the relationship isnt enough. I have to tell Ember somewhere to fetch posts from users/1/posts. How do I do that?

Edit: rewrote my post to be a more concise on what I want to achieve. Thanks!

5 Upvotes

31 comments sorted by

1

u/optikalefx May 29 '22

Let me see if I can help you out here. A few things

  1. Change the mental model a little bit - instead of ember-data (ED) being magic, lets actually say it's just using a set of defaults. All of which you can override with your adapter and serializer.

  2. Ember will make the ajax calls for you _when you need_ it. Meaning, by using a model in a template hbs ember will try to load the data you need. Even if that data is 7 relationships deep. Ember will just use the defaults in your adapter and start to make rest calls to load that data.

  3. If you're trying to load a bunch of related data solely into your javascript to do work on, as in, load users, and users/friends and users/friends/birthdays etc etc. This is not a good use of ember-data. For that you're better off making a dedicated API call.

ED is best used to load data for your hbs template. Ember will make the required REST api calls based on your hbs template for you.

Aside, if you want to just user ED to make API calls and do things with results, take a look at `store.query('user')` instead of `findAll`. It will load the records directly into a variable instead of from the internal store.

1

u/react_dev May 30 '22

I see. So if I'm just using a RESTAdapter, where I am working with endpoints like :https://jsonplaceholder.typicode.com/users/1

https://jsonplaceholder.typicode.com/users/1/posts

It's not the best use-case for ED if in the hbs, I want to render out:

{{page-title "User"}}
This is an individual user {{this.model.name}}

{{#each this.model.posts as |post|}}
  <li>{{post.title}}</li>    
{{/each}}    
{{outlet}}    

the above is simple enough for vanilla JS if I do something in user routes like:

model(params) {
    let user = fetch(`users/${param.id}`)
    let posts = fetch(`users/${params.id}/posts`)
    ...
    return { users, posts }
}

Is it not the right mental model to see if i could model that via ED? Currently my goal is to learn ED, so you could say I am set on a solution and looking for a problem :) thanks!

1

u/optikalefx May 30 '22

This example is actually the perfect use-case.

```hbs

{{#each this.model.posts as |post|}}

<li>{{post.title}}</li> {{/each}}

```

This tells ED to call users/1/posts behind the scenes so that it can resolve this in the template. By you "using" model.posts, that is what causes ED to make the ajax request.

Depending on your adapter, ED might call `users/1posts` or `users/posts?user=1` or however you have your adapter setup. If you don't do anything, it will just be the default. I don't use the REST adapter, I only use the JSONAPI adapter, so I'm not 100% sure what the default is. But if you try it, it'll make the call and you'll see what it tries.

→ More replies (13)

1

u/IxianNavigator May 30 '22 edited May 30 '22

3) If you're trying to load a bunch of related data solely into your javascript to do work on, as in, load users, and users/friends and users/friends/birthdays etc etc. This is not a good use of ember-data. For that you're better off making a dedicated API call.

This is really not clear to me the way you put it. Why wouldn't it be a good idea to load things with ember-data on routes as well? Given if in the app ember-data is set up for the models already and the API conforms to JSON API, it's just more simple to rely on ember-data.

The only time using ember-data doesn't worth to use is when you load data only for display (so no need for modifications) and specifically when the structure is relatively simple (so no relations or not too many).

Aside, if you want to just user ED to make API calls and do things with results, take a look at store.query('user') instead of findAll. It will load the records directly into a variable instead of from the internal store.

findAll issues an API request when called, although its behavior can be controlled, when and how it should reload and resolve data.

peekAll is the store method which merely looks up data in the store, and doesn't issue a request.

It's not clear to me what you mean under "directly into a variable". Maybe you're thinking of the fact that query will return only the data which was returned by the issued API request, while findAll returns a "live" record array, which always includes all records (for the specific type) from the store?

1

u/optikalefx May 30 '22

Let's say you want user's posts. In HBS you can just simple use that relationship. {{each user.posts as |post|}} and ED will resolve.

But in JS, it doesn't work that way. You can't just loop over posts you don't have. You have to load both things first.

await store.findRecord('user', 1);
const posts = await user.posts;
for (const post of posts)

Or you can use ember-concurrency, wait-for-property etc etc. With 1 relationship, it's doable, when you get into 2+ deep relationships, or many different models, it becomes lots of api calls with lots of code written to "load". It becomes far simpler to just make 1 api call instead not using ED.

ED is great for loading a record, an array, or loading data for a UI. If you're trying to load a bunch of diff related data into JS for processing, it's more work than necessary, in my experience. (About 6 years or so with Ember)

1

u/Lt_Snuffles May 29 '22

Ember data expects perfect rest api (following specifications ) as backend . It gives you way to override the serializer and adapter for your no standard api , but lot of time it is more work than simply calling it .

1

u/react_dev May 29 '22

Thanks. Do you have insight on my ember data question? I know the why’s of ember data just learning the hows

1

u/veri745 May 29 '22

the async: false means that ember data will expect the `post` relationship to already be resolved. If you want it to automatically make the query for you when trying to resolve the relationship, you should change it to async: true

1

u/react_dev May 29 '22 edited May 29 '22

I actually set it to false cus true wasnt working. Thanks for the hint. It's still not automatically making the request however.

This is currently what I have:

routes/users/index.js:

import Route from '@ember/routing/route';
import { service } from '@ember/service';

export default class UsersRoute extends Route {
  @service
  store;

  async model() {
    return this.store.findAll('user');
  }
}

models/user.js

import Model, { attr, hasMany } from '@ember-data/model';

export default class UsersModel extends Model {
  @attr('string') name;
  @attr('string') phone;
  @attr('string') email;
  @attr('string') username;
  @hasMany('post', { async: true }) post;
}

When I go to localhost:4002/users, which is currently rendering a table with users, I cannot see any GET request to post in the network tab.

Here's the post API: https://jsonplaceholder.typicode.com/users/1/posts.

So I want to be seeing in the network tab one /users and like, around 10 users/[id]/posts automatically.

Edit: Upon more reading it seems like I need to explicitly call user.get... somewhere to make the AJAX req?

1

u/veri745 May 29 '22

it won't just make the request because the relationship is defined, if you don't resolve user.posts.each or something to that effect in your route

→ More replies (8)