As I understand from the EmberJS Guide to Routing, you should specify the model you want a route to load in the Route's model hook. The model hook may return a promise, and if it does, the route will pause until the promise resolves.
Therein lies my problem: this approach works fantastically under normal use cases (user triggers a transition from any other route into the route in question.) The problem arises if the user is currently at the route in question.
If the user triggers a page refresh (using the browser's refresh button, or ctrl+r or whatever other trigger there might be, the promise in the model hook causes the user to sit at a blank white page until that promise returns. In large dataset cases, this can be a handful of seconds, which does not make for a great user experience.
So, how do I resolve this issue?
The only solution I have developed is to trigger the data load in the route's activate hook, and manually set the controller's model when that promise returns. I don't like doing this, because I'm circumventing the entirety of Ember's model framework.
I would like the application template to render before the model hook hangs the page, at a bare minimum. Any guidance on how to resolve this would be greatly appreciated.
In case the context is necessary: as the tags imply, I am using Ember-Data. I'm utilizing the RESTAdapter almost entirely out-of-the-box, unmodified.
Routes have sub-states that can be used to render a temporary template while the model is loading. See: http://guides.emberjs.com/v1.10.0/routing/loading-and-error-substates/
The first load/initial blank page is a UX problem that will be solved by Fast Boot, see: http://emberjs.com/blog/2014/12/22/inside-fastboot-the-road-to-server-side-rendering.html
Fast boot is already available through one of Ember's branches, I don't know the name.
Related
router.js
this.route('claim', function() {
this.route('new');
this.route('edit', { path: '/:claim_id' });
});
claim.new -> redirect to claim.edit with an empty model. this.transitionTo('claim.edit', model);
Initially, because the model is not saved we get: index.html#/claim/null
When we save the model, it continues to display as index.html#/claim/null instead of say index.html#/claim/87
Is there a way to force the url to refresh?
The only way to force the URL to refresh is to force the route/page to refresh. You can use the refresh method in your route to do that.
To understand why it doesn't update automatically, you have to understand how routes work and how Ember is separate from Ember Data (or any data persistence library).
First, you have to remember that Ember has no knowledge of Ember Data and the specifics of its object model. It just sees plain Ember objects. Ember Data models are more important than other models to you, but Ember isn't aware of that distinction.
Second, the routing in Ember is binding/observer aware. It doesn't watch for changes and update URL state accordingly. It calculates the URL once using the serializer hook, then it leaves it as that. It's not going to recalculate if your model changes in any way. It will only recalculate when it's refreshed.
Short story long, the part of your application that changes the ID of your model and the part that calculates the URL to use aren't aware of each other. You'll have to manually sync them up. Using the refresh method is probably easiest way to do that.
UPDATE: As Kitler pointed out, you can also transition to your route using the new model. It won't fire the model hook so it won't have to do any asynchronous work, but it will still update the URL.
What is a good way to show a reusable loading screen whilst controllers are performing time consuming actions such as server queries. Ember provides shared loading route functionality for route transitions perhaps someone has been able to leverage these in controllers as well?
My current thinking is to implement actions in the ApplicationController to show and hide a loading div. Controllers can then call these before and after time consuming actions. Perhaps someone has a better solution?
I believe you have a few options. I would do one of these two:
Define a loading route/template. The loading template will be rendered into the outlet of the parent route and will be replaced by the current route's contents when the transition is complete. I did this once where I used a modal dialog to display a loading message.
Define actions in ApplicationRoute. Actions bubble from a controller, to the matching route, then up the route chain. If you define actions in the topmost route, you can send a message from any controller that will be caught. I currently have startLoading and stopLoading actions in my ApplicationRoute.
I am getting started with Ember, and Django Rest Framework and I can't seem to peice together how to connect a model so that Ember can use the data in that model and create a simple drop down box. I have one model that I am starting with that is as such:
id
name
security
status
All I want to achieve is allowing Ember to use the data in this model and create a dropdown like so.
<select id="model">
<option value="model.ID">model.Name</option>
</select>
Can anyone help me with this? I am complete new to Ember and Django Rest.
Without going into a ton of detail, I've created a mini example of what you're looking for
http://emberjs.jsbin.com/Ozimatuj/2/edit
You'll note that I'm using mockjax, so instead of hitting any real endpoint, it's all mocked. Additionally I'd recommend using a client side record management solution (such as ember-data or ember-model). That's another discussion though.
In the application route (which correlates with the root of your app) it hits the model hook (which should return the model associated with that route. I'm returning a POJO of the users. That model is being assigned as the content of the application controller (automatically generated). The the application template is being built, and it's being backed by the application controller. Inside the application template we create an instance of ember select, and we tell it that the content backing it is model (which is the model/content in the application controller). We also say, use bind the user model (you could do id) and the name to the value and the label respectively.
I then bound the value of the select to selectedPerson, so anytime the value changes, the selectedPerson updates, the template which talks about that person will update. Magic. Ember does the rest.
This is a really broad question, so if you have any other questions, please ask a specific question, and I'd really recommend going through the getting started guide, it's really short, but will give you a decent foundation of terminology and methodology of Ember. http://emberjs.com/guides/getting-started/
For Ember Data I'd do a quick read the of the transition document for ED 1.0 beta.
https://github.com/emberjs/data/blob/master/TRANSITION.md
DS.DjangoRESTSerializer = DS.RESTSerializer.extend();
DS.DjangoRESTAdapter = DS.RESTAdapter.extend({
defaultSerializer: "DS/djangoREST"
});
I am in the painfully slow process of learning Ember and find the guides and documentation severely limited when it comes to non-trivial examples.
To my question now.
Given the following router definition:
App.Router.map(function () {
this.resource('home', function() {
this.resource('weather', function() {
this.route('site', {path: '/:weather_site'});
});
});
When the user enters the home.weather.index route the model hook executes and fetches all weather information from the server. When the user then enters home.weather.some_site the model hook of the new route would bring weather data for that particular site from the server. How can this redundancy be prevented? In other words, how can controllers communicate data to each other? Should I use the setupController hook in each Route to achieve this or are there better ways?
Are there any good resources to help me understand the data flow in and out of Ember? Are there any good resources to help me learn Ember faster than digging into the source?
How can this redundancy be prevented?
It's not clear if/how/why the model hook for home/weather/index would fetch all weather information from the server. If that's desired behavior, then totally agreed there is no reason to fetch the same data when user enters home/weather/site.
If you are using ember-data there should be no redundancy since:
when user visits home/weather/index App.Site.find() fetches all weather data
when they visit home/weather/site App.Site.find(weather_site) returns site from cache and does not make a new request to the server
if user visits home/weather/site directly (or refreshes browser) then App.Site.find(weather_site) will call server and fetch just data for that site.
So unless there is something I'm missing there is no redundancy to prevent.
In other words, how can controllers communicate data to each other?
Seems unnecessary, but since you ask: Controllers communicate with one another via bindings. You declare a dependency from one controller to another via the controller's needs property, then Ember inject's a that dependency automatically. See the ember guide dependencies-between-controllers for more on how this works.
Should I use the setupController hook in each Route to achieve this or are there better ways?
That shouldn't be necessary in this case.
Are there any good resources to help me understand the data flow in and out of Ember?
Not clear what you mean by data flow. Best guess is that you'd learn a lot from Luke Melia's gothamjs presentation
Are there any good resources to help me learn Ember faster than digging into the source?
Reading the source is always a good option, but there are many other resources. I recommend checking out embercasts and ember weekly
Problem:
I have a conceptual problem with re-using an old controller instance when the user re-enters the route. In many examples, the controller stores the state of user interaction in instance variables. When the user re-enters the controller those should be reset, but there is no clear mechanism for doing so.
So, there must be a good reason to use a single controller instance. What is it?
How I arrived at this issue:
I ran into a bug in out Ember app, where the controller keeps local state that got out of sync based on the user actions elsewhere. The controller maintains a state whether the user is editing the "name" of a "case". When the controller is instantiated, that is set to "true", but when the user cancels, it's set to "false". Then the user goes away to a different route and comes back to the same route. I get the same controller instance that already has "false" for editing the name. I would expect to have "true" instead.
Ember v.s. Rails:
I'm coming from Rails perspective where controller is instantiated for every request. There is no shared controller state between requests. All instance variables are local to the request, so they can be used safely.
Back to the question:
Why does ember use the same controller instance on separate visits to a route?
The role of a controller in ember and in rails are very different. In rails, as you know, the controller is used as a conduit for the request to prepare the data for presentation. If you consider the use case in ember, this is a role that is primarily played by the router.
In your example you mention that the edit state has gotten out of sync. You can solve this problem in a variety of ways, for example you could move the edit state to the model, so instead of isEditing, you'd call model.isEditing (or just isEditing if you are using an ObjectController). Another option would be to reset the state of the controller when entering the route.
Another example which should illustrate this difference is a save button on a form. Initially you might think "Oh, I'm going to put a save action on my controller". This is logical, when you're thinking like a rails developer, but saving a model is the responsibility of the router (especially if it results in changing the current route as this is something that's intentionally hard to do from a controller).
In ember the role of a controller is as a conduit between the model and the view. The fact that controller instances are re-used is a little confusing, but really isn't that important. When you change or return to a route the model/context of the controller is different, and that is where the state of the view should live... in the model.
Although controllers are usually singleton, when you use {{render}} with a specified model you will get a newly instantiated controller. See rc2 release notes under "Using Render with Multiple Models". I'm new to Ember and this difference confused the heck out of me.
Using a singleton controller when trying to keep per-model-instance user interaction state seems tricky. As jonnii said, you can put the state on the model, and that works but it cruds up the data model. The other thing I have tried is to put a map in the controller (keyed by model, value is an object of user interaction state) but this seems like a clumsy reimplementation of something I'd expect the framework to provide. Still working on this...