Why the Ember Components does not fetch the data by themself?


#1

Hi,

lets say I have a user component and I want to display the username on every page. So I need the userdata on every page but if I do this over the route-models I have to fetch the data on all pages. Again and again. On every url I have to create then a route file and paste that user fetch logic in that file. This would be a copy paste disaster.

What would be a good practice in Ember to solve this issue?

I started yesterday with Ember.js please dont throw with stones at me when this is a very stupid question :wink:


Edit: This looks good to me but I think its not the best practice. Too bad because I think in some cases this is the better concept.Code is from StackOverflow.

import Ember from 'ember';

export default Ember.Component.extend({

  store: Ember.inject.service(),

  init() {
    this.initialize();
    this._super();
  },

  initialize() {
    this.todos().then((data) => {
      this.set('todoEntries', data);
    });
  },

  todos() {
    const store = this.get('store');
    return store.findAll('todo');
  },
});

#2

If you have global data in your application (ex: username from the current session) the best option is to create a service, which persist alongside your application and is available from everywhere in it.

You can create a session service which exposes the username property, then inject it in your component or, if you want to create a more decoupled component, inject it in your controller and pass it to the component as parameter.


#3

Okay I should had bring up an other example. You are right that would be the best practice but lets assume then something else:

Lets say I have a component which fetch the latest comments or just some really sepcial data of my app. In my opinion this fetch logic belongs then to the component because it should work on its own, everywhere. It should not be route dependent. Then I could easily reuse this component everywhere on every route/page in my ember app.

If I would put the fetch logic in the route instead then I have to fetch everytime the comments on all pages on my own before my component works.

If I would put it in the route-model, what is then when I need the component on more pages/routes again, for example:

“/” -> Sidebar "/comments" "/details" -> Sidebar

Then I have to get everytime the data on my own in the route-model but route-model-logic is not easy to share over more pages/routes, so it seems that I have to “copy and paste” the fetch logic to that routes where I need the data again. This sounds not very reusable for me but it seems as considered better practice why?


#4

The answer is still to use a service - put the fetch logic in the service


#5

Okay good to know. Thank you. What is exact the difference between my example and your suggestion? The Code looks almost the same :hushed:

Service-Version:

import Ember from 'ember';

export default Ember.Component.extend({
  //will load the service in file /app/services/shopping-cart.js
  cart: Ember.inject.service('shopping-cart')
});

My Version:

import Ember from 'ember';

export default Ember.Component.extend({
    store: Ember.inject.service(),
});

#6

In the first one, you’re injecting the shopping cart service - in the second you’re injecting the store. The store is a default ember service, presumably the shopping cart is something that exists in your app if you are injecting it


#7

Okay, would this mean when I use the store to fetch my data in the component that it is a good way to go? That was my first example (see first post) but you said it isnt a good way to fetch data in the component.

Then you said use a service, I asked what the store is, the result was it is a service as well, so then it seems that I use already a service because the store is the default service. Where is the failure then in my first example?

Edit: I mean this example

import Ember from 'ember';

export default Ember.Component.extend({

  store: Ember.inject.service(),

  init() {
    this.initialize();
    this._super();
  },

  initialize() {
    this.todos().then((data) => {
      this.set('todoEntries', data);
    });
  },

  todos() {
    const store = this.get('store');
    return store.findAll('todo');
  },
});

So as it seems I use already a service but in a component is this legit?


#8

Imagine you have your component rendered 4 times in the first page in different places. With your implementation, you will fire 4 fetch requests for the data, because the store is empty of todos when components initialize.

If you place your “fetching todos” logic in a specific service you will fire only one request in the same scenario.


#9

Good point thank you. But is using a Service in a component still considered as bad practice or would this be okay?

Sounds good and better. I thought the store is a service as well and all components get the same store even in my example.Good to know that it is not the same instance/store.


I am a beginner with Ember.js, sorry if some of the questions are a little bit stupid.


#10

The store service is the same throughout ll your application, you are right. The example was an edge case where multiple instances of the same component will trigger the store to fire an API request.


#11

@TatzyXY I read the thread and I am with you. I don’t understand how wrapping default service (store) in a custom one would solve multiple fetch requests issue.

Regarding your question is it okay to access the store from components, yes and no :slight_smile: Normal practice is to split your components on smart and dumb, smart ones are top level components that do fetching and pass data to nested ones, they don’t render any HTML, they just compose other components. In Ember a Router can play the topmost component’s role.

If you will be wondering how to request a data fetching from deeply nested component then use action. Throw “getComments” action, handle it in the router, trigger “isLoading” property on your model and you will be able to even show loading indicator.