Ember JS and nested routes: the provided route name is invalid - ember.js

I'm currently working my way through my first Ember JS app, using Balint Erdi's excellent book "Rock and Roll with Ember JS"
I'm stumped and would appreciate some help.
I have the following (fairly simple) routes for my app:
/clients
/clients/:slug
/clients/:slug/requests
So for example, I would want a user to be able to view the requests for a specific client by visiting /clients/abc-company/requests
On the main dashboard, inside clients.hbs, I have the following code to link to each specific client (using dynamic segments):
{{#each model as |item|}}
<tr>
<td>{{item.id}}</td>
<td>{{#link-to "clients.client.requests" client}}
{{item.name}}
{{/link-to}}</td>
<td>{{item.contact}}</td>
<td>{{item.email}}</td>
<td>{{item.phone}}</td>
</tr>
{{/each}}
Now, the issue is that Ember doesn't seem to recognize the second link-to parameter (client).
The console is yelling at me with the following error message:
WARNING: This link-to is in an inactive loading state because at least one
of its parameters presently has a null/undefined value, or the provided
route name is invalid.
but I DID pass the parameters, which is supposed to be client!
Here are the relevant files in my code, I'm hoping someone can point out my obvious mistake or find the semi-colon that I forgot to include, or something.
//app/router.js
(...)
Router.map(function() {
this.route('clients', function() {
this.route('client', { path: ':slug' }, function() {
this.route('requests');
});
});
});
//app/routes/clients.js
(...)
var Client = EmberObject.extend({
id: '',
name: '',
contact: '',
email: '',
phone: '',
// create slug from client name
slug: computed('name', function() {
return this.get('name').dasherize();
})
});
// app/routes/clients/client.js <-- see the route DOES exist
(...)
model: function(params) {
// get the slug from clients, return client matching the slug name in the URL
var clients = this.modelFor('clients');
return clients.findBy('slug', params.slug);
}
});
// app/routes/clients/client/requests.js
(...)
model: function() {
return this.modelFor('clients.client');
}
});
Any help would be appreciated, this is my first Ember.js hurdle and I have a feeling that understanding nested routes properly will be very useful in the future :)
But for now I'm just confused.
Thanks in advance!
EDIT: Added some more code from the template, for clarity.

Annnnd I figured it out.
Lesson 1: When Ember complains that the model you want is invalid and you think you created the route, make sure you actually import the route at the top of the route file (import Route from '#ember/routing/route';)
Mine was missing for some reason and I didn't see it
Lesson 2: In the template file, the parameter needed by #link-to isn't actually the name of the route/model. It's the name of whatever you choose to call your iteration in the each loop.
This is wrong:
{{#each model as |item|}}
<tr>
<td>{{item.id}}</td>
<td>{{#link-to "clients.client.requests" client}}
{{/each}}
This is correct:
{{#each model as |client|}}
<tr>
<td>{{client.id}}</td>
<td>{{#link-to "clients.client.requests" client}}
I have no idea if this information will ever help anyone. In any case, I shall proceed to self high-five myself.
EDIT: Looks like i have to wait 2 days before I can accept my own answer.
Cheers!

Related

Refresh a rendered template when the model is returning store.queryRecord

I have a route that displays 5 categories. Each category is intended to only have one child. I created a child route and pass the selected category type id as queryparamter. In the child route model() I use the parameter with store.queryRecord() to query the backend to either return the record that matches that type OR nothing. This seems to work fine as long as a record exists. The problem I am running into is if I select a category that doesn’t have a child record. When nothing is returned from queryRecord the template continues to display the previous data. I can see the network request completing successfully and it returns an empty array. If I refresh the page the template correctly shows that there is no model data.
I’ve been struggling all day trying to find a way to refresh the template when the model no longer has a record. I have a feeling I am going at this backwards, I would be grateful for any pointers.
Parent:
export default Ember.Route.extend(AuthenticatedRouteMixin,{
user: Ember.inject.service('user'),
model() {
var user = this.get('user');
return this.store.findAll('strategic-priority',{ location: user.get('selectedLocationId'), year: user.get('selectedYearId') });
}
});
HBS
{{#each model as |strategic-priority|}}
{{#link-to 'priority-area.goal' (query-params priorityArea=strategic-priority.id) class="list-group-item"}} {{strategic-priority.label}} - {{strategic-priority.text}} {{/link-to}}
{{/each}}
Child:
export default Ember.Route.extend({
user: Ember.inject.service('user'),
queryParams: {
priorityArea: {
refreshModel: true,
replace: false,
}
},
model(params) {
Ember.Logger.debug(params); //I see this is in the console so I know this code is being called each time
var user = this.get('user');
return this.store.queryRecord('goal',{ location: user.get('selectedLocationId'), year: user.get('selectedYearId'),priority: params.priorityArea});
}
});
What you could try is to wrap the template that displays a category with {{#if hasModel}}. So something like
{{#if hasModel}}
... your template ...
{{/if}}
and then in the controller for your route
hasModel: Ember.computed.notEmpty('model')
I don’t know if this is the correct answer but my workaround was to change the model to use store.query rather than queryRecord. In my template I did a {{#each model as |xx|}} even though I only expected one record.
I was also able to avoid the situation in most cases by using hasMany. It is a bit cumbersome to gather the data on the backend (at least at my skill level with php and zf2) but in the end it seems to work pretty nice.
Thanks for advice.

cant get the model value in template

just started to play with emberjs.dont know if this is stupid question or good one, but I am stuck in this for some days and cant figure out why.
in my router.js
Router.map(function() {
this.resource('posts', {path: '/'});
..........
});
.....
this.resource('post', {path: 'posts/:post_id'});
});
and in the route folder i have posts.js setup as following.it has simple js varible used to hold id , title, and body of articles.
export default Ember.Route.extend({
model: function(){
var posts = [
{
id:'1',
title:'lipsome vesicle',
body:"A liposome is a spherical vesicle"},
{
id:'2',
title:'another lipsome vesicle',
body:"A liposome is a another vesicle"}
]
//console.log(posts)
return posts;
}
});
In posts.hbs, the title of each post is shown as link to the to post. of course by looping through each model as |post| and print link-to post.title.
my post.js file simply get the model for posts and returns it.
export default Ember.Route.extend({
model: function(params) {
return this.modelFor('posts').findBy('id', params.post_id);
}
});
In my template for post.hbs I wanted to simply show the title and body for the post .It would have been more redable if it was like post.title or something like that. but i saw some tutorials that does following.
<h1>{{title}}</h1>
<h1>{{body}}</h1>
the url goes to the localhost:4200/post/1
but I cannot see the title and body
when I checked the value in console for
this.modelFor('posts').findBy('id', params.post_id).title , it prints the title
but view is blank.
I have read somewhere, that the controller is the one that is responsible to get the value from model. in that case too, I dont know how to access the returned model in that controller.
I have watched many tutorials, including the raffler example by railscast, since I have background in rails. but those tuts including lot other seems preety outdated. So for all new learners this is frustating and confusing too. Is there good fresh resources except the emberjs guide?
since the title was being printed in console, after many trials when i tried this trick, i finally managed to get my title in individual post.using Ember inspector plug in when i clicked $E console gave me all the post.so i put that object in array and returned that array.
model: function(params) {
// console.log("hell")
console.log(this.modelFor('posts').findBy('id', params.post_id).title);
var post = [this.modelFor('posts').findBy('id', params.post_id)];
return post;
}
then in my view I looped through the array as:
<ul>
{{#each model as |post|}}
<li>{{post.title}}</li>
{{/each}}
</ul>
but I would like to know better way for this.

trying to figure out how to set up this link in Ember

I'm looking at Ember to see whether it is suitable. One issue that came up is that we have many 'narrow' api calls - these calls return a list with the minimal data to create a list and then the user clicks on a link which goes to the detail view. Due to how link-to helper works, this will bypass the model method in the route. This question has the same issue: Transition from one route to another with a different model in Emberjs But I honestly don't understand the answer he provided. Specifically, he provides this code:
<a {{bindAttr href="somePropertyInYourModel"}}>{{someTextProperty}}</a>
and says:
The property somePropertyInYourModel is a property containing the url to the new page. If the url is in the ember routes it will be as if you where typing that address in the address bar and pressing enter, but without the full reload of the page.
I don't really understand what he's saying (my fault on this). I tried putting in <a {{bindAttr href="{{post}}"}}>{{someTextProperty}}</a> and <a {{bindAttr href="{{post}}"}}>{{someTextProperty}}</a>
but to no avail. Say I have this model:
Hex.Post = Ember.Object.extend({
id: null,
body: null,
isEnabled: null,
createdAt: null
});
How could I get this to work? What is he telling us to do?
thx for help, ember looks really cool but has a lot to know
edit #1
Here's the whole Router list. I want to have a posts view and when the user clicks, it goes to the post view which will be populated to the right. The problem is that the link-to bypasses the model so we really need to reload the model at that point. This would allow us to repurpose much of our existing api. Thx for help
Hex.Router.map(function() {
// put your routes here
this.resource('index', { path: '/' });
this.resource('users', { path: 'users' });
this.resource('loginslogouts', { path: 'loginslogouts' });
this.resource('locations', { path: 'locations' });
this.resource('flaggedcontent', { path: 'flaggedcontent' });
this.resource('posts', function(){
this.resource('post', { path: ':post_id' });
});
this.resource('comments', { path: 'comments' });
});
ahhh, send the id instead of the model, that will retrigger the model hook. Sending a model to the hook makes ember think you have the model, sending an id tells ember to hit the model hook with that id.
{{#link-to 'post' post.id}}{{post.name}}{{/link-to}}

Nested routes causing data reload/replacement?

Posted this on the emberjs forums, but SO seems more appropriate.
Hi! I have two routes called classyears and classyear. They're nested like so:
this.resource('classyears', function(){
this.resource('classyear', { path: '/classyear/:classyear_id'});
});
Posterkiosk.ClassyearsRoute = Ember.Route.extend({
model: function() {
return Posterkiosk.Classyear.find();
}
});
Posterkiosk.ClassyearRoute = Ember.Route.extend({
model: function(model) {
return Posterkiosk.Classyear.find(model.classyear_id);
}
});
My templates are:
Classyears:
<div class="yearList">
{{#each item in model}}
{{#linkTo 'classyear' item}}{{item.id}}{{/linkTo}}
{{/each}}
</div>
{{outlet}}
Classyear:
<div class="transformContainer">
{{trigger sizeComposites}}
{{name}}
{{#each students}}
{{partial student}}
{{/each}}
</div>
(The "trigger" helper is from another SO post. The issue was happening prior to adding it, though)
I'm using the Ember-model RESTAdapter. When I load /classyear/:classyear_id, it looks like classyear is rendering its data twice. Once with the correctly-loaded data, and once with no data loaded. The order appears to be random. If the no-data option happens last, it wipes out the correctly-loaded data, leaving a blank page. Vice-versa, and the page content displays just fine.
Any thoughts?
/edit 2: More info:
It looks as though the 0-record reply is from classyears loading. So, it's likely that the zero-record reply is actually just zero records in my hasMany field "students".
If I load /classyears (no class year specified), it only loads once, to get the class year options. If I then click on a class year, it doesn't reload classyears unless I refresh the page, at which time, it loads both, and if the classyears load (a findall) finishes second, it displays no data on the page (other than the classyears template, correctly populated, at the top).
So... maybe my classyears model isn't handling the hasMany field correctly?
I feel like I'm getting closer, but still not sure what's up.
First of all you need to specify a model for a Student, like so:
Posterkiosk.Student = Ember.Model.extend({
id: Ember.attr(),
name: Ember.attr(),
imageUrl: Ember.attr(),
gradyear: Ember.attr()
});
Posterkiosk.Student.adapter = fixtureAdapter;
Now, in your example you are setting the key for the has many to students, but students is an array of objects, not id's, so create a property called student_ids, and pass an array of ids, now that is your key.
Posterkiosk.Classyear = Ember.Model.extend({
students: Ember.hasMany('Posterkiosk.Student', {key: 'student_ids'})
});
If you set embedded: true, then your Classyears server response should come back like this:
{
classyears: [
{..},
{..}
],
students: [
{..},
{..}
]
}
Otherwise, EM would make a separate call to the endpoint on the Student model, and get that data based on the student_ids property.
See the working jsbin.
Tip: RC.7+ removed the underscore from partials, plus the partial name should be in quotes..

EmberJS Dynamic segment error

As most Emberists would know, I am in the middle of tearing my hair apart at the moment, trying to overcome this vertical wall that EmberJS has so that I can get to the paradise at it's peak.
Here is what I have at the moment:
<script type="text/x-handlebars" data-template-name="dogs">
<h2> Pick a stack to view its cards </h2>
<ul class="nav">
{{#each model}}
<li>{{#linkTo 'dog' this}} {{name}} {{/linkTo}}</li>
{{/each}}
</script>
My routes are defined like this :
App.Router.map(function(){
this.resource('dogs);
this.resource('dog', '/:dog_id');
});
And accordingly, the Model hook for DogRoute is defined as this :
App.DogRoute = EmberRoute.Extend({
model: function(params){
return App.Dog.find(params.id);
}
});
And finally the model is pretty basic in itself:
App.Dog = DS.Model.extend({
name = DS.attr('string')
});
DS is a bunch of fixtures in my case, so I am not going to bother writing this down. However, this doesn't work and I don't know why. Here is the error I keep getting when I visit dogs route, and expect a bunch of links to dog being rendered:
ember-...rc.5.js (line 356)
uncaught exception: More context objects were passed than there are dynamic segments for the route: dog
Can anybody point out what is being done wrong here ?
Note: If I remove the dynamic segments and simply render a dog route inside of my dogs handlebar, (this from handlebar gets taken off) then the links (dog names) do get rendered. However, I need these routes to be dynamic segments and not just hyperlinks with unique ids autogenerated by ember/handlebars.
Your router declaration is wrong. It should me more like this:
App.Router.map(function() {
this.resource('dogs');
this.resource('dog', {path: '/:dog_id'});
});
The error you get
ember-...rc.5.js (line 356) uncaught exception: More context objects were passed than there are dynamic segments for the route: dog
might be related to your route mappings. You should rather define the routes like this:
App.Router.map(function() {
this.resource('dogs', function() {
this.resource('dog', {path: '/:dog_id'});
});
});
And furthermore since this is ember's default behaviour, (see here under dynamic models):
App.DogRoute = EmberRoute.Extend({
model: function(params){
return App.Dog.find(params.id);
}
});
you don't need to define it explicitly, so you can remove it.
The rest seams to be correct as far I can see.
I've also put togheter a working jsbin, have a look.
Hope it helps.