EmberJS {{#linkTo}} a model that isn't loaded yet - ember.js

I am working on my first significant Ember.js app, and have been running into some roadblocks. After hitting one-too-many show-stopping bugs with ember-data, I have decided to roll my own models using Ember.Object (for now at least - ember-data looks like it will be real awesome, real soon).
My basic model structure is:
Album (has photo album-specific attrs, and references a collection of images)
Images (an ArrayProxy collection of Image models, which tracks collection-level attrs like 'currentImage', and 'nextImage')
Image
I have a {{#linkTo}} helper in my template that is supposed to allow the user to click to the next image in the set, like so:
<button>
{{#linkTo image controllers.images.nextImage}}
Next Image
{{/linkTo}}
</button>
This template has the context of the AlbumController, which needs: ["images"]. controllers.images.nextImage is a computed property, that figures out ID of the currently-displayed image, then uses it to find the Image model for the next model in the Images ArrayProxy collection.
My problem is this:
Upon page load, I receive an error message Uncaught Error: assertion failed: Cannot call get with 'id' on an undefined object.
I'm assuming this is because the {{#linkTo}} helper is trying to get the id property from the return of controllers.image.nextImage, which is a computed property that relies on the Images collection being loaded from the server. This async behaviour is being handled with promises behind-the-scenes, but the {{#linkTo}} helper seems to required a valid context to be returned immediately upon page load.
My questions are these:
Has anybody else had to handle this kind of situation, where they're not using ember-data and had to use {{#linkTo}} helpers with computed properties that weren't immediately available?
Can anyone suggest workarounds that don't fight Ember's way of doing things?
Some of my thoughts are:
make the computed property return a dummy context, that somehow gets replaced with a valid model after load
use an {{#action}} helper instead of {{#linkTo}}
I have written up a JSBin example which is mostly code-complete, except I couldn't manipulate the hash URL to trigger the nested routes, so I had to do everything in the application template.

Related

Using transitionToRoute, passing a route without a dynamic segment the current model causes error

I have a route defined in the Router as:
...
this.resource('cart', {path: 'my/cart'});
...
MyApp.CartRoute = Ember.Route.extend({
model: function(params) {
return this.store.find('cart');
}
});
In my CartController (ArrayController) I have this line of code in an action (addToCart):
this.transitionToRoute('cart', this.get('model'));
In some other route's template, I call that action:
<button {{action 'addToCart' product target='controllers.cart'}}>Add To Cart</button>
When that button is clicked, I get this error:
Uncaught Error: More context objects were passed than there are dynamic segments for the route: cart
To my understanding, Ember should have recognized that I was passing it a model and skipped the model hook, which is what I want.
This can be fixed by adding a random, useless dynamic segment to my route definition then defining serialize in my CartRoute. However, if there is a better way, I would rather do that.
Am I going about this the wrong way?
In
this.transitionToRoute('cart', this.get('model'));
you're sending some data object to the route cart which doesn't have a dynamic segment. The error message should disappear with the line
this.transitionToRoute('cart');
That said, I've got a feeling that the architecture of your application might be improvable. But I'd need more code to be sure. Anyway, you should have a look if putting the action handler somewhere else and using bubbling actions without the target could be an option. Again, that's just a feeling, I don't know enough of your code to be sure.
A little late to the game here, but it sounds like you may want to have your cart be (/be managed by) a service, rather than being fetched as a model for a bunch of different specific routes. In fact, a shopping cart is the specific example currently used on the Ember docs page explaining the use of Services: https://guides.emberjs.com/release/services/#toc_defining-services
This would allow you to manage the fetching and local caching of the cart at the service level. You'd then either inject that service directly into the relevant controllers, or inject the service into the route, and have the route return the service-managed cart object as it's model.

EmberJS Loading Substate: What logic does it use to climb the route hierarchy to find the loading template?

I'm trying to wrap my head around Ember's loading substate. According to the Guides...
http://emberjs.com/guides/routing/loading-and-error-substates/
...the loading substate will show while the model is being resolved, and if a loading template doesn't exist for the current route, it will climb the route hierarchy until it finds a 'loading' template. So I defined a 'loading' template, but it doesn't always show.
Here an example:
http://emberjs.jsbin.com/junop/2
First, when the page loads, the loading template doesn't show even when the ApplicationRoute's model is being resolved. Then if you go to 'books', the loading template will show. So going from 'index' to 'books' will always show the loading template. But them going to a specific 'book' (e.g. Three Little Pigs in my example) doesn't show the loading template. Finally, going to 'book' from 'books' doesn't show the loading template, even though it will if you go from 'index' to 'books'. I can't make sense of all of this. What logic does Ember use to climb the hierarchy to show the loading template?
There are two different things related to loading. The loading route vs the loading action.
Each resource with at least one child (the root included, aka application route) has a loading route. Ember won't hit the loading route unless the parent resource has already been rendered. This means it's less useful for the first load, and more useful for transitions.
http://jsbin.com/cajivava/5/edit
No worry, there are loading actions! Each time a model hook is taking some time to resolve, it will send an action to that route, which will traverse up the router until handeled (or if you handle it, then return true, which will send it up the tree). This can be super useful for magical loading, or for first time loads. Just remember (and I show it in this example, that if you don't handle the loading action at a particular route it will be passed up to application).
http://jsbin.com/cajivava/6/edit
Now you can just hack it into a different place in the application route, since it's a special route that's only ever loaded once. A mixture of the two is usually the best plan if you have a long loading app in the beginning.
http://jsbin.com/cajivava/4/edit
It's easiest to think about transitions in the sense of the outlets. The section to be rendered is stalled, so the outlet where it's going to rendered into, transitions to the loading route. Once it's complete, it navigates to books index.
In the example below, try clicking on cows. It's a resource 2 deep and books resolves immediately (the resource in the middle), but the transition is stalled on cows, so our outlet where we're going to render all of the newness transitions to the loading route.
http://jsbin.com/buyaxipowi/1/edit
Individual loading routes housed in their own resource require the resource itself to be resolved before you can hit it's loading route:
http://jsbin.com/niwerikuli/1/edit

Ember.js: {{render}} does set 'content', but does not set 'model'

From my main handlebars dashboard template I am rendering multiple templates in a loop, setting the model as following
{{#each forecastDispatch in forecastDispatches}}
{{render "balance.forecastDispatch" forecastDispatch}}
{{/each}}
The dashboard combines multiple models and renders them on the same page/route.
From logging to the console, I can see that the BalanceForecastDispatchController controller gets instantiated correctly for each render call and the forecastDispatch model of type DispatchType is set as content, but not as model, model is still undefined. As the model is not populated properly, passing data to lower level components does not work. I thought that the model is just an alias/proxy for content, hence I am quite surprised.
What am I missing here? Any help is really appreciated, I am trying to solve this for quite some time now, but cannot find the culprit.
The issue was caused by snippets in my Handlebars template which were not commented out properly. As a result, one extra instance of the BalanceForecastDispatchController was created by Ember. This controller had off course no model/content assigned to it. In the Chrome Ember-inspector the controller was shown without a model.

Cannot read property 'container' of null when using linkTo helper in an Ember template

I am creating an Ember application as an add-on to some HTML returned from the server. I need this HTML so that the site can be indexed by search engines, and also to speed up the initial page rendering for the users.
So my application consists of several Ember Views, appended to different DOM elements of the HTML generated by the server. I don't use master templates for routes, so I set renderTemplate function of each route to do nothing.
My Ember App is bound to body element and I can successfully append a custom view to an element down the tree. It works:
In this JSFiddle three last elements of the list are appended by Ember
But when I try to use linkTo helper in my template, I hit an error:
Uncaught TypeError: Cannot read property 'container' of null ember-latest.js:32224
which is in this function:
router: Ember.computed(function() {
return get(this, 'controller').container.lookup('router:main');
}),
In this JS fiddle I just add linkTo to the template, and it breaks everything
In general, can Ember work this way - having many Views scattered
over the HTML rendered by the server?
How can the example code be
fixed?
I've fixed your fiddle here, Check it out.
Seems like you are starter to Ember,
So here are some tips for you,
You should have an application template, which will be the root template and on which all the templates will be rendered.
You shouldn't access views using this.container.lookup, that is for debugging only.
You shouldn't append views to the DOM, it's the job of the framework to do.
By default your application will be appended to the body of the html, if you want it to be appended elsewhere, give the rootElement property when creating the application. Refer here for configuring your application.
The rootElement can be either a DOM element or a jQuery-compatible selector string. Note that views appended to the DOM outside the root element will not receive events. If you specify a custom root element, make sure you only append views inside it!
Don't access any controllers globally like App.itemsController.set("content", model), if you want to access another controller inside a route, use this.controllerFor, and to access inside another controller, use needs.
You need not create any controller instance like App.itemsController=Ember.ArrayController.extend({}).create();
The framework will take care of all these.
I found that I need to additionally bind the view and the container together to make this fiddle work
App.itemsView.set("controller", App.itemsController);
App.itemsController.set("container", this.container);
So the resulting working code snippet is here:
http://jsfiddle.net/ddegtyarev/6cBRx/6/
Again, let me reiterate that I'm building an hybrid Ember application - i.e. I have some HTML returned right from the server, and some appended by multiple Ember views in multiple places. This is why I have to manually create the views and bind them with controllers etc.

Ember View loses content

I am working on a bit of code to show a D3 chart using Ember fixture data as the source for the data. The charts show up just fine on the first load of the page but if I move to a different route & then return to the first route (the one with the working chart) the chart simply disappears. So I guess my question is, how do I get the chart to appear every time the route is visited? Here is what my Route and View code looks like, will add more code if requested.
Updated with JS Bin: http://jsbin.com/ihapaz/2/edit
The route may not be getting any data when you return back to it. When using linkTo the model hooks are not fired. The setupController is called directly with the model that you pass to linkTo. However if the route depends on the model hook having fired to load it data then the view would be empty as it wouldn't have any data to render.
That's the only thing that comes to mind from the above code. Try posting a jsbin if this doesn't work.
Edit: Post jsbin
After looking at your jsbin I realized my earlier answer was incorrect. In general it is true that linkTo skips the model hook, but only if dynamic segments are present. Your route has no dynamic segments. Hence the model hook will always be called, so the view/chart is getting data correctly.
The error is in your implementation of render. The purpose of the render method is to push strings of html onto the DOM. This isn't the appropriate place for custom DOM insertion. For that you need to put things on the didInsertElement method.
I made these changes, renaming render to didInsertElement and the corresponding updateChart. Here's the updated jsbin.