Accessing a route directly via URL, the data is not populated


#1

I have a nested route setup as I would like to have my templates nested as well. The route setup looks like this:

...
  this.route('posts', function() {
    this.route('post', {path: ':post_id'}, function() {
      this.route('comments',  {path: 'comments'}, function() {
        this.route('comment',  {path: ':comment_id'});
      });
    });
  });
...

Potentially, my URL could look something like this: /posts/:post_id/comments/:comment_id

If I navigate via {{link-to}} then I have no problem, however, if I go directly to a specific URL via the browser, that’s when things going wrong. Imagine that my comments page lists the comments associated with the post (post_id) I’m looking at. The problem is going there directly, like typing in /posts/123/comments/56 doesn’t load the list of comments, only the comment itself. Here is how my routes are setup:

// routes/posts.js
...
model() {
	return this.get('store').findAll('post');
}
...

// routes/posts/post.js
...
model(params) {
	return this.get('store').findRecord('post', params.post_id);
}
...

// routes/posts/post/comments.js
...
model(params) {
	let post=this.modelFor('posts/post');
	return post.get('comments');

}
...

// routes/posts/post/comments/comment.js
...
model(params) {
	let post=this.modelFor('posts/post');

	return post.get('comments').then(function(results){
		return results.store.findRecord('comment', params.comment_id);
	});
}
...

How do I populate/hydrate the post’s comments list when I go directly to a comment in that post URL rather than navigating there via link-to?

If I type in /posts/123/comments/56, no list is displayed from the comments.js template. If I type in /posts/123/comments, only the first comment is displayed from the comments.js. Any thoughts?


#2

I use dot notation when using modelFor(), perhaps that’s related…

let post=this.modelFor('posts/post');
let post=this.modelFor('posts.post');

Also, when using the {{link-to}} links, are you passing in the model/post or id?

And, in your comment route, you don’t need to fetch all the comments first. This should work if your model’s / API’s are setup properly:

// routes/posts/post/comments/comment.js
model(params) {
    return this.store.findRecord('comment', params.comment_id);
}

Finally, in your “expected” URL’s, you include “comments” while your router.js defines that section of the path as “comment” (singular). Ex: /posts/123/comment/56 and /posts/123/comments


#3

Hey thanks for the response. I updated the routes, that was an error while modifying on here, it was supposed to be comments not comment (in response to your last point).

I’ll try the . notation in modalFor to see if that makes a difference.

In regards to comment.js, I was trying to get the hasMany relationship from posts. I figured that was the best way to retrieve only the comments for that post. Maybe the parent routes (like posts, post) is not getting called?

I tried your suggestion for comment.js and it works via link-to but putting it in the browser directly seems to not load any of the comments except the first one (and only 1), which isn’t associated with the post I was testing.


#4

Depends, if you’re putting these model hooks in the index route or not. Ex:

routes/posts/post/comments.js will load when visiting a child route routes/posts/post/comments/index.js will not load when visiting a child route

In regards to the comment route, using the stores findRecord() using the id (which I had suggested) is the most direct way to load the specific comment. You don’t need to get the related comments first, if you know the specific id you’re looking for. However, if you want to “warm” the data store you could load all the post comments first but then filter/find the specific one instead of requesting it from the API again. Ex:

// routes/posts/post/comments/comment.js
model(params) {
    let post = this.modelFor('posts.post');
    return post.get('comments').then( comments => {
        return comments.findBy('id', params.comment_id);
    });
}

#5

I think I understand your comment about index.js. I don’t have an index.js, but if I did I assume it wouldn’t be called if I was calling say: routes/posts/post/comments/comment.js

It makes sense, for example, if I go to the URL posts/4/comments/5, that it would give me the comment with ID 5 (out of all the comments) because I’m looking in the store for the comment with that ID using findRecord (i.e. the route routes/posts/post/comments/comment.js). It doesn’t matter that the post ID is 4 – maybe it’s not even reading it at all. However, the list of comments for the post with ID 4 is not populated. So in the nested templates (i.e. with route routes/posts/post/comments.js), it sometimes displays the first comment (i.e. comment with ID 1 even if the posts doesn’t have a comment with ID 1) or populated incompletely (it feels unpredictable, not reliable). If I get to this point via link-to, the correct information is displayed – however if I were to type the same address manually, it would be incomplete as mentioned.


#6

When I go to the URL https://example.com/posts/4/comments/5 how do I ensure that all the parent routes in these nested routes populate their data such that the nested templates get all the data they need?

The following would all need to be triggered

routes/posts/post/comments/comment.js
routes/posts/post/comments.js
routes/posts/post.js
routes/posts.js

So that their corresponding templates have the data populated too. It probably works when using link-to because I’ve travelled all the way to the comment.js route, but going directly there doesn’t trigger the other data to load. Is there a way to do that?