Should Ember Components Load Data?

I am new to ember.js and looking to create several reusable components that can be used across various independent but related applications.

Unlike many of the examples I have seen, these components that I want to re-use are not purely UI related. I’d like them to load most of their data by themselves. The data loaded can be tweaked by supplying some options to the component.

This seems like a good idea because:

  1. Users of the components can easily drop them into their templates.
  2. Users of the components only have to supply minimal configuration options. They do not have to get the data/model for the component.

At the same time this seems strange to me when looking at examples of simple Ember.js applications such as the TODO app. In these applications:

  1. A route is responsible for loading the model.
  2. Templates are able to access that data via the controller.

Is it appropriate for Ember Components to do things like loading data? What problems would this cause? Is there another design pattern that would allow me to easily share these complex components across multiple applications without loading the data from the component itself?

Thank you

4 Likes

The ember team does give us some guidance from Ember Data’s Transition.md document, it states:

In general, looking up models directly in a component is an anti-pattern, and you should prefer to pass in any model you need in the template that included the component.

That being said, I think it would really depend upon your use case (they do show you how to inject the store into Components anyway). I can imagine some scenarios where the component retrieving data might make the most sense…

What are you trying to do exactly? That might be the best way to explore wether it may or may not be a good option.

1 Like

Ember.Component is inherited from Ember.View, and a View isn’t a good place for loading data. Instead of it you can use {{render}} helper, which looks in templates like a component, but it give you access to the controller, where manipulation with data should be placed.

You should actually create component by defining what they need, if you load the data yourself, you will enforce the other application to load the data the same way.

But if you set the data on the component attribute, then the component doesn’t need to know how to load the data, but only where to find them in itself.

I hope you understand :slight_smile:

To clarify what I am trying to do, here is an example:

I want to provide a library that I will distribute to my team. Lets say the library provides a file called team.js.

With my little Ember knowledge I imagine my library would allow a user do do something like this in their template:

{{team-profile user=someUser expanded=true}}

This would render the expanded profile for the given user. In doing so it would have to request data from a service.

I believe @jamalsoueidan comment expresses my thinking here. I want the component to encapsulate the “how do I load the profile” information. I don’t want the users of my component to be concerned with that. They should only be concerned with providing the minimal parameters required. I don’t want the users to have the option of loading the data in a different way.

Is this a good use case for a Ember Component? Is there another way to do this that would be better but still meet my requirements of allowing the users of the library to provide the minimal information to load a user profile without being concerned with how that is done?

Look at the Ember.Select component.

http://emberjs.com/api/classes/Ember.Select.html

@jamalsoueidan can you elaborate. Are you suggesting instead of using Ember Components I use a view and provide an API such as:

{{view Team.Profile user=someUser expanded=true}}

What is the difference between this and using an Ember Component. Why would I choose 1 over the other.

Thank you.

No I didn’t mean that.

You should always use Component :smile:

I just wanted you to look at SELECT example and sourcecode, because Ember.Select requires some data before it can render, and it can be used across all application, if you want your component to work across multi application, you can see how they have done it.

You should never let the component load the data, it should ONLY receive them from the attributes.

I can see some components loading data, but that would probably be third-party API calls, like Twitter, Facebook, Github…

For example:

{{latest-tweets username='knownasilya' limit=5}}

Obviously you wouldn’t want to have the user specify the data, since this always calls Twitter’s API. If the component is dealing with the app’s data, then pass it in…

@knownasilya your example is very similar to the components I want to build.

If the component is dealing with the app’s data, then pass it in…

That seems like a good rule to follow. I am learning towards it being OK for components to load data in these cases where we want to hide the details of getting the data from the user. If it is app data, we should pass it in explicitly as you state.

I’ve got exactly the same problem. I’ve got a YouTube-like app with “widgets” that display e.g. the latest videos, related videos, most popular videos etc. These “widgets” can be used in different routes/templates.

I’d like to be able to just put this widget in a template and not have to modify route’s controller at all:

{{videos-related tags=tags limit=5}}
{{videos-popular limit=10}}

and so on. If loading data in component is not a good practice, whenever I add such component to a template, I’d have to add code that loads necessary data to the route’s controller as well.

2 Likes

Did anybody come to a consensus on this? I’m battling the same issues at the moment.

{{live-search}} Self fetching search results, makes sense

{{live-search result=results}} Doesn’t make sense

Components are for easy-reuse, but if the logic that fetches the results sits outside of the component, then its not very re-usable.

Stumped :slight_smile:

Components should not be responsible for fetching data. They should only consume data via the component’s interface, manipulate the data, and possibly send named actions which can be handled by the controller or route.

I think there is not a standard answer for this question. Usually you should load data in route, wrap data in controller and pass data to template and component. Only in some rare case when using this way is difficult (maybe due to framework limitation), you can consider loading data in component.

Let’s give a dropdown menu which displays users list as an example. In normal case your load users in route and pass it to component like:

{{users-dropdown users=users selection=assignee}}

But in an application there’re many places that need to use this dropdown, and most of the case it just display all users. So you think maybe it’s better to put “load users” logic into component, then you can move “loading users” logic from various routes to a single component and just use:

{{users-dropdown selection=assignee}}

Actually this is a “bottom up” way to build application. And I call the traditional Ember way a “top down” way, which always loading data from route, then flows to controller, and template/view/component.

This users dropdown works fine until one day you meet a new requirement: Some dropdown should only display a filtered users list while others display a full list, you start to add additional parameters to make the component reusable. Then new requirement come again…

This happened on my application and now most of the time I put loading data logic in routes. I think it has several benefits:

  1. When you want to check data level logic, you only need to look at the route code. No need to think about the data is loading in route or controller or component. Much maintainable.
  2. Component only handle UI level logic, it only care how to use data but not how to get them. It also makes component logic simpler.

What about when a component need to manipulate data (like search/filter)? I suggest sending action to route/controller instead of implementing logic in component. Let route/controller handle all data level logic, when the data is changed, it flows to component via data binding.

2 Likes

I don’t like when people make broad statements “You should never do X, you should ONLY do Y” without making an argument as if their word was the absolute truth.

Rants aside, I’ll give the consultant answer, it depends. Components landed quite late in Ember and I don’t think the initial guidance holds true once we put them to test. Old examples, like the TODO application didn’t consider components. There’s an obvious use case, where components like a select or data-grid can receive all their data, but there’s a whole different category of components that are better off abstracting their consumers about responsibilities like loading additional data.

When I’m thinking of a component, I need to ask myself, who’s better suited to load the data. Let’s give two examples:

  1. I’ve a component responsible to render a time tag. It needs just needs a date. In that case, it doesn’t make sense for it to know where that date is coming from. Simple interface {{time dateTime=dateTimeFromOuterContext}}
  2. We have a component to load “twitters” with a particular hashtag. The component knows how to render tweets, display a header, paginate to load additional tweets, etc. The route is loading blog posts (or whatever that page is doing). Now we have a third responsibility, loading the tweets from twitter’s API. The question is, where would it make more sense to put this? IMO, it makes more sense for the component to do it, it feels outside of the responsibility of the Router, who knows about blogposts, but not about tweets, if the Router had to know tweets, it feels like it’s crossing concerns. With that in mind, the following interface makes sense {{recent-tweets hashTag=hashTagPropInOuterContext}}

As a side note, I would normally split data access from either routers or component. We get that for free with the store if using data-access, but for any external API I would rely on a service for any data access. So, in reality, the twitterService is the one responsibly of loading tweets, but the recent-tweets component knows when to load it and who to ask for it.

5 Likes

@MiguelMadero I completely concur with your thoughts. Pretty much exactly the same conclusion I (and some others) have made… think ahead, let’s not stay in the past :wink:

Here my thoughts which pretty much reflect the way you are thinking and provide some insights/ideas on how we could architect this with Ember: http://infomatrix-blog.herokuapp.com/post/thoughts-on-ember-20

Thinking about Web components, they have made it clear that a component doesn’t even have to be visual but could be entirely data-centric or even just act as a communication bridge between various components. Hence there should be no requirement that a component be linked to either a template or data service.

Component (template + data service)

Multiple routers and Data providers…

It would make sense to extract the Router as a separate component much like in AngularJS. A Data Service could use its own router to route to and get specific data. A component could be router/state “aware” without using the application url router.

The app router could (by default) be an UrlRouter, an extension of a more generic StateRouter. A Controller could still (optionally) bridge the gap between route and component or component and data service.

For one way presentation of data to the component, such a controller would be a Presenter.

Various Component-data architectures that could/should all be possible within this architecture. Introduce the level of indirection/complexity that you like!!

Example Component-Data scenarios:

Component <-> Controller <-> DataService <-> Router <-> Model <-> Data adapter

Component <- Presenter <- DataService <-> Model <-> Data adapter

Component <-> DataService <-> Router <-> Model <-> Data adapter

Component <-> Router <-> Model <-> Data adapter

Component <-> UrlRouter <-> Model <-> Data adapter

Component <-> DataService <-> Model <-> Data adapter

Component <-> DataService <-> Data adapter

Component <-> DataService

think ahead, let’s not stay in the past wink

While it’s good to think of the future and contribute to it. I’m actually building apps today.

The idea of having multiple routers sounds really similar to what I’ve been exploring around nested-routers.

I think having services removes the need for this notion of a DataService, since that would simply be another type of service. For those using Ember-data it could be as simple as injecting the store into their components.

1 Like

There’s a newer, consolidated answer from the Learning Team in this post.

I wasn’t able to pull up that link… why is it an anti pattern? I’ve been loading data from components for a long time and haven’t noticed any code smells from it.

Note that this thread is 10 years old. While the “happy path” is still often loading data via the router APIs there’s nothing wrong with loading data in components. I think the reason it was discouraged in the past is that there can be a lot of async complexities which people do not take into account (loading states, errors, retries, rendering cascade and performance, etc). The router is built to handle those, but if you load data in components you have to BYO.

In modern Javascript/Ember we have some better tools for dealing with those concerns. Resources, for example, are great for abstracting some of the async lifecycle concerns. That said you still have to be more conscious about loading data in components. Soon the router will probably just be components anyway so this distinction will dissolve further.

TLDR: there’s nothing wrong with loading data in components but you have to be aware what you’re signing up for when you do.

1 Like