So i am new to Ember but have SPA/JavaScript familiarity. I am trying to understand the best practices in Ember for pulling multiple data sources with the context of a single page.
It shows a dashboard of mutiple data soruces that are consumed to generate a single page. I need a format similar to this for my app. And while this example does a great job laying out the routing and formatting it doesn’t show the data (as it uses websockets.io in a controller for the twitter stuff, and the rest is dummy data.
So lets take a single example. In the header you can see badges indicating how many messages a user has. Building the back-end API for that is simple, and I understand how to call the API using ember-data. But all the examples I have seen and documentation I have read show that Ember recommends populating your model objects in the routes class which seems to be tied to a URL. I wouldn’t think i would want each route defined in the app to re-call the API needed for that badge (even if in a parent function/class way).
So what’s the correct way in Ember to have the main window populated based on the URL, but have the data retrieval in the header be dynamic as well?
I think what you’re asking is how to share data between routes?
In the badge case you would probably handle the badge data in the user route and nest other routes that have that top bar under the user route. If you need access to the badge data in a sub route you can use model for.
You can also share data via the store and manage when it gets reloaded with the cache policy. For instance you could have your route always request badges from the server but have the cache policy just load once and never again.
Services is another option, by storing some data on your service and injecting it where it is needed.
But how do I engage the user route when the URL is for some other process?
To help make this more concrete, if the main panel is showing a list of tweets that I have retrieved, the URL (to make browser bookmarks work) would probably be something like /twitter.
How do I engage both the user route (or even a header route) and the twitter route on the same URL?
I know Ember can support loading multiple source of information into a single page. And the scenario I am trying to describe (and what is demo’d by the example site) is really common for people to want to build. No doubt in my mind that Ember supports it, but documentation in this area is really thin, and I can’t find any good examples.
Is inheritance really the only way to do this? That’s really ugly if I have multiple configurations of where I want to pull the data and where I don’t. Doesn’t seem very flexible and creates unnecessary duplication of routes to deal with multiple parents. (Ones with and without the common formats.).
Is there no way to use composition instead of inheritance to solve this?
Nested routes is not about inheritance it’s about having a route like this users/1/posts this will hit both the users route and the posts route ie the posts route is nested under the user route. If this structure doesn’t make sense for you also use services or the store cache policy as mentioned in my original answer.
The easiest way to think of a service is as a singleton that gets injected by ember into your component, controller, or route. You can do what you wish in your singleton, in your case share a single data source across multiple components, controllers, or routes.
So this would be taking the service, and injecting it where needed, and having the service does the calls to this.store (using ember-data) to do the data retrieval?
That seems like it would work, and is better than putting it into every route (or the base route).
My only question is, isn’t this common enough that the base data retrieval practices from ember should support this. The documentation still seems to imply that all data retrieval should be done via the routes, and no where else. This seems like a case where the best practice is actually to break the opinionated approach of the framework.
The ember opinionated way is probably using nested routes seeing as ember is extremely route centric and the modelFor only works for models in the hierarchy. Personally this has worked the best for me; I almost never use a service for this use.
I agree, but the route specific piece doesn’t provide good support for loading data for the common header/footer that is potentially used by every page as those data points are needed by every URL, not just an single one.
This really does feel like a hole in the framework that I am surprised no one else has struggled with.
Perhaps I don’t understand your use case well enough. The situation you just described would be solved by having the header and footer data load in the top level route. Then all other routes can access that data by using modelFor, if the data is needed by a component it would then be passed into the component in the template.
My concern is that with putting that header and nav data calls into the main application.js route, then they are executed on every URL, even if never displayed (or at least the promises are created, I need to test if it’s truly lazy loading the ajax or not).
This means that on a mobile device where those components are hidden and not shown, I am still incurring the lookup cost. Additionally, that might represent multiple disparate lookups that now I have to combine into a single place, which reduces my ability to test and my modularity.
I never said it couldn’t be done, as this thread has shown multiple ways to do it, I acknowledged that at the top. But I was looking for confirmation that this is really the best practice for doing this in Ember and what other options might exist. Best option so far seems to be a service that is injected, I was just remarking that the best option seems to go against the framework opinionated nature.
Forgive my continued pursuit of this idea but I’m worried you may have some misconceptions about the way routes work. If this doesn’t provide any additional clarification then disregard no condescension intended.
As far as I know Ember doesn’t reload the parent models every time you visit a subroute so you shouldn’t need to worry about hitting the server for the badge info every time you visit a subroute. Ember does eager load the model data in the route though. This is to say that it will resolve any promise returned by the model hook before displaying the route. The eager loading principle does seem to be a key component of embers opinionated architecture. The idea being that all route data is loaded prior to trying to display it. If you desire to load your model data lazily after the route has been displayed then there isn’t really a whole lot built into the framework to help you with that, this could be what you’re noting as absent? If you need to conditionally load / access data in this world, eg your mobile example, you can put logic in the model hook to do that.
So in this case I think what you allude to about doing the loading in an async basis is closer to what I would have wanted.
This is due to the fact that not only do I want to hold up the entire page from loading for these badge counts, but I also need to ability to update them without having to re-execute the route (think of a polling option). It’s similar to the unread message counter that happens on this site on your profile icon. I don’t want to have to reload the page to have that updated, but the app does have to go back to the DB to find out that it happened.
So yes, the async loading support is exactly the problem. For most of my stuff, it’s only the user-auth stuff that I would want eager loaded, the rest would all be lazy-loaded.
I know from reading I have done since originally posting this that routable components are suppposed to include async loading with them to support exactly this. They will allow me to create components that are specialized to the data retrieval that isn’t URL related and keep it out of the parent router. But they problem is that they aren’t available yet (at least not in GA).
Does anyone have an ETA on when those will land? I am having a hard time making Ember work for my needs without that key function.