How to make ember component fetch data from server. put AJAX call inside the component seems not a good practise to handle this

An Ember Component needs to fetch data from serve, however i think put AJAX call inside the component is not a good practise.

Or use the Route to fetch data, then pass data to component.But the route’s method can’t share easily between different routes.

3 Likes

I’ve debated whether this is good practice or not, ultimately I decided it is. Too many components are self-contained widgets that shouldn’t be route dependent.

2 Likes

I’d need a little more context about the component you are toying around with in this scenario. That said, the idiomatic approach is data down (meaning you would use the route to fetch all the data for a given template/view/controller). Is it possible the component you are building isn’t a standalone widget at all and maybe a view/controller in your app?

@toranb @Ray good point, I didn’t mean to imply it’s always a good practice, just that it can be. In general, if the component is a true “widget” then it’s probably okay to make requests from it, but if it’s not it should go through the route.

An example component where it is ok would be a component to display a twitter feed for a specific keyword or username. The route might determine the keyword or username the feed is for, but I would argue the component itself should handle the rest.

@jthoburn @toranb

I’ve ask the same question on the stackoverflow

In my scenario, i need a modal component to show image list but the question is where is data come from.

The first solution is use Route to request data, and then pass data to component.

The second solution is use Render to add a controller, and then in that controller to send a request to server.

Here is a great article about how to create an ember component using the “data down” method I mentioned above. It’s a great introduction to components in general (a little light on the ember-cli side as the post is from Oct 2014 but it’s still a great resource)

https://haagen-software.no/blog/post/2014_10_31_writing_ember_js_custom_components

@jthoburn I’ve found a similar problem with ‘routes are the answer’ and ‘components are the answer’ mantra to the data down proposition. Often data needs to be dynamic based on user input and since components are isolated they need to get it from somewhere.

I understand why you have resorted to async data requests from your components as there is no good alternative in Ember currently.

A potential solution is to bind component options to route based query params so that the route can fetch new data on QP change and it can propagate down to the component. I say potential because it ends up being a fight between the QP from the route and whatever value the user has input as the route resets the QP which the user may have since changed, and there is also the need to debounce input changes as well so it gets rather messy quickly. Something that should be simple (and is in other frameworks) is a tough nut to crack in Ember and strikes at the heart of the data flow and computed property/observer paradigm in Ember.

These kinds of data flow and event flow problems have driven me to look at alternatives like immutable data and the Flux architecture pattern in React (which is more significant than React IMO).

So TL;DR right now Ember doesn’t have a solid data flow story. It has things that look like they might work but don’t actually get you there in practice. I’ve said my piece on the Ember 2.0 RFC because I was hoping to see a solid architecture that nails the data and event flow problem but its still not there yet IMO.

Honestly I think you’re making a mountain out of a mole hill. I never said I had a problem with components requesting data themselves, just that I debated whether it was a solid practice and came to the conclusion that for certain types of components it is.

Ember’s event flow is second to none, and it’s something I’m probably too well acquainted with. I’m honestly not sure how that’s even debatable, nor am I sure how you think it’s not simple.

A potential solution is to bind component options to route based query params so that the route can fetch new data on QP change and it can propagate down to the component.

This is a horrible idea, one which you found out. Query-params are for saving non-critical route state, you shouldn’t be using queryParams as a hack to manipulate component data.

Typically, you’ve got a defined controller(s) and a defined model(s). You can pass data from these to components directly, and if needed you can do so in a bound way. Let’s say this was my twitter widget.

I could pass statically

{{twitter-timeline username="runspired" keywords="ember components"}}

Or I could pass dynamically

{{input value=searchTerm action="addSearchTerm"}}
{{twitter-timeline username=model.username keywords=searchTerms}}

In the latter case, the “addSearchTerm” action bubbles to my controller where the matching function pushes the value onto the searchTerms array, which updated my twitter component.

What’s awesome about ember’s actions-up data-down approach, is we could easily take this a step further. Let’s say this widget really doesn’t have anything to do with the route other than getting the current username. We could write our component like this.

{{input value=searchTerm action="addSearchTerm"}}
<ul class="tweets">
  {{#each tweet in tweets}}
    {{render "tweet" tweet}}
  {{/each}}
</ul>

Now our keyword input is part of the component itself, and when it triggers addSearchTerm our component can handle that action itself. No need for any other components / controllers / route to know about the need or logic of doing so.

You’ve been on quite a campaign against DDAU, and while I don’t think you don’t have a valid point of view to offer Ember, you’re suggesting a proven (and honestly amazing) design pattern is broken.

Ember is adopting fewer two way bindings after observing how coders use components and the performance / usability / maintainability costs therein, as well as looking at how well React has done with a unidirectional data flow.

That said, I’d like to discuss the below.

From another thread

I think the solution is to follow a unidirectional approach, not a DDAU approach. That is for state changes your components invoke semantic actions at the top level which then are delivered by a dispatcher to subscribed stores.

I think I now understand why you were trying to hack around with QueryParams. There are limited cases in which I would find this very useful, but I wouldn’t put it past the core team to add the ability to do something similar to this in 2.0.

Why wouldn’t I put it past them? Because with complex interfaces sometimes you do need a button to affect the state of multiple components, and since data only flows down, sometimes you’ll just want to target the closest route with an action that’s going to affect the state of multiple components, and having an “action” subscription program such as this is a lot safer than the pattern many use today of allowing components to alter data upwards.

That said, if that action is simply alerting to a change in data the route knows about, just letting the event bubble up, and the data change propagate down will have you covered.

A Parting Note on Events

I think you meant to say actions, not events. They are different, but intrinsically connected. That said, actions should go up the same way that events go up, for the same reason that DOM events bubble through the hierarchy Actions bubble through the View/Component hierarchy.

A simple example of this is the ember-mobiletouch library I’ve built as an addon, it bubbles taps and gestures through Ember’s event system, and let’s gestures trigger actions. By doing it this way, the component or view closest to the event each get’s it’s turn to handle the gesture.

Bubbling actions like events let’s us build layered, complex components that contain their own eco-systems without needing to wire up everything at the top level or expose more than a simple api. This is ideal for most of the “ambitious” apps I’ve worked on.

I guess I feel data flow is an important concern and has been a considerable source of grief because the Ember way is CPs and observers which I have found less than robust.

I beg to differ, more on why below.

I think you mis-understood. If you have a QP that is say a search param and you have an input, select or checkbox bound to it to allow parametric search, that is an intended use of query params. An index route will reflect what is in the query params on the controller and also fetch data and set it on the controller. Components updates the DOM based any data it is bound to off the controller, including components that may be bound to QPs for parametric search. Since the mutation of the QP(s) is via a component via binding and/or component to controller via an action you have to deal with the UI maintaining a mutated value, debouncing changes, and then the unexpected thing of the route updating the QP on the transition being resolved which the component may also be bound to. The route updating the QP will reset what the user has entered in the UI in the interim, so to ‘get around that’ you create another set of temporary properties in the component and observers to propagate the change from a temporary property to the QP, but then you have to deal with ‘is the UI authoritive right now or is it the controller/route’, but since an observer firing doesn’t provide context you also have to setup and maintain some state somewhere to also flag that. Its very complicated compared with a non CP/observer data flow approach.

I again I think you have it wrong. I 100% support data down. I think its absolutely the best architecture. I am cautious on state changing actions up (not UI events up).

My big problem with Ember is with propagation of state via properties and observers as it is not orchestrated through a top down flow. Propagation of property changes and observers occur under the covers outside the control of my app. Changes don’t occur as a group either, so I have to guard against partial state and all manner of potential feedbacks both direct and indirect. Take the case of an invoice line item, which has unit price, qty, ext price, markup %, margin, sell price. And all are editable except ext price. Show me how Ember makes that great. I can tell you its a nightmare to get it right so the computations work properly and the UI inputs works properly and feedback doesn’t kill it. Templates force CPs and/or observers into my app, and give the illusion of wiring things up declaratively in a top down fashion but the actual property invalidation and propagation of observers and chains occurs through hidden Ember channels. Ember decides that getting a property causes a get of dependant properties which then recompute and fire off observers in random fashion which may then cause chain reaction of other observers and property changes firing. It is not the same as passing data down React style, NOTHING like it and pretty much equivalent to random state propagation.

I support the component changes in 2.0, they just don’t go far enough to supporting a data flow that is deterministic and easy to reason about IMO. As long as CP’s and observers are in the picture it is difficult to orchestrate state change in a reliable and sensible fashion.

I was referring to actions as per the Flux terminology, not Ember actions or UI events, UI events bubbling is perfectly fine as its not mutating application state. In a Flux unidirectional flow architecture components respond to UI events (or a childs UI events) and use action creators to mutatate state. Those actions are fed into the dispatcher at the top to update state via stores, then the application component render flow happens, followed by DOM update, repeating ad infinitum. This way state change is DELIBERATE, mediated and traceable, and truely top down, not side effect driven via properties and observers firing in random orders with no context.

@ahacking The more I think about this, the more I think you are absolutely right. I have three component types that would benefit massively from the flux architecture: modals, carousels and selectables. In all cases, DDAU isn’t enough on it’s own.

With modals, I often have to hook into something on the route controller if I want to provide a “close” action from within the yield (or use the _yield hack below).

With selectables, I have to carefully clone the data that goes in to make sure it doesn’t accidentally screw up the data elsewhere, especially when it comes to having sorted arrays.

The biggest use case though are carousels, for which I’ve overridden the _yield method on component to make component an available keyword just to deal with action targeting.

For instance, imagine we had this in the template for a route.

{{#carousel-component}}
 {{#carousel-slide}}
    {{next-button}}
  {{/carousel-slide}}
{{/carousel-component}}

Next is supposed to send the “nextPage” action to the carousel-component, but because of yields, it sends it to the route controller instead which can only then trigger nextPage on the carousel via property binding.

With a component such as this, I often want to validate whether the next-button should be disabled, or wait for a promise to resolve before continuing (validation/saving).

By overriding _yield I can target nextPage’s action to component, which sends it to the carousel-slide. Then within carousel-slide I can pass that on to component as well to reach the carousel-component, which itself then checks to see if a method has been provided for validateContinue and if so waits to execute nextSlide until after validateContinue has run.

It’s a good flow, but getting to it was hackish, and it would be much nicer to have a single “nextPage” handler that could then notify the appropriate subscribers.

1 Like

@jthoburn going off the original topic, but this is made much easier with the new block params feature as you can yield the component to send actions to it:

{{#carousel-component as |carousel|}}
  {{#carousel-slide}}
    {{next-button target=carousel}}
  {{/caousel-slide}}
{{/carousel-component}}

An alternative is a pattern for sub-components to know about their parent components:

// components/carousel-component/component.js
export default Ember.Component.extend({
  isCarousel: true
});

Then in the child component we can find the parent and hook it up:

// components/carousel-slide/component.js
export default Ember.Component.extend({
  carousel: null,

  setupParent: function() {
    var parent = this.nearestWithProperty("isCarousel");
    this.set("carousel", parent);
  }.on("init"),

  actions: {
    nextPage: function() {
      this.get("carousel").send("nextPage");
    }
  }
})

I definitely agree that there’s stuff that Ember can/should be doing to make things like this easier though.

1 Like

Fortunately the new component block syntax allows yielding block parameters so you won’t need the _yield hack to target the component or indeed use props defined on the component anymore.

I’ll be working with Stefan and others to draft an Ember guide on data and action flow so feedback like you have just provided is really useful and confirms there is room to do better. It will take a lot of community review and feedback to get it polished but should result in a useful reference pattern moving forward for 2.0.

2 Likes

Personally I would like to see more separation in the components so they are data and context unaware. Currently I don’t feel like that is always possible as data down isn’t always a plausible solution. Many times you need to take actions when no observable data has happened. In my other discussion, I would love to have data handled on the route, but it isn’t possible because sometimes data isn’t passed down as a result of an action being called. Also in the other discussion, there are times when a component (our Stripe component) needs to have actions taken that aren’t data dependent at all.

With that said, I love the idea of having components that are context and data unaware, that can be shared and reused on any app, but with the Ember 2.0 changes coming and everything becoming a component, I don’t see how that will be possible anymore with a “data down actions up” only approach. Either callbacks will need to be passed in with the actions for things that don’t require data or could have no updated data, or ember is going to need to update its approach to communicating with components and their children when it’s not data bound.

1 Like

This wycats quote captures what makes sense to me:

I really think it’s important to differentiate between “I know what component I’m going to but I have work to do before I can show it” (async component) and “I have to do some work to do before I even know where I’m going” (route).

So does the component know where it’s going?

  • If so, do requests in the component.

  • Otherwise, go to the route.