I am trying to use nested routing to render a collection first. Once the collection is rendered, clicking on an item (using link-to) renders that particular item in the outlet. This all works fine so far.
I am having a problem where refreshing the page doesn't call the 'model' hook of my nested resource though.
From Ember's website at http://emberjs.com/guides/routing/specifying-a-routes-model/:
What happens if the user visits your application directly with a URL
that contains a dynamic segment? For example, they might reload the
page, or send the link to a friend, who clicks on it. At that point,
because we are starting the application up from scratch, the actual
JavaScript model object to display has been lost; all we have is the
ID from the URL.
Luckily, Ember will extract any dynamic segments from the URL for you
and pass them as a hash to the model hook as the first argument
Here is my code:
Admin.Workqueues.App.Router.map(function () {
this.resource('delinquencies', function () {
this.resource('delinquency', {
path: '/:id'
});
});
});
Admin.Workqueues.App.DelinquenciesRoute = Ember.Route.extend({
model: function () {
// Does XHR here and fetches a collection of items to render.
// Returns a promise
}
});
Admin.Workqueues.App.DelinquencyRoute = Ember.Route.extend({
model: function (params) {
debugger; // This doesn't get called
}
});
So with this code, going to /delinquencies lists the entire collection. Clicking on an item opens a delinquency object at /delinquencies/3 but now refreshing the page doesn't call the model hook of delinquency route.
I am not sure what I am missing. Any ideas? If it matters, I am using:
Ember : 1.2.0
Ember Data : 1.0.0-beta.7+canary.f482da04
Handlebars : 1.1.1
You shouldn't define the resource as a child resource but as a separate one.
Admin.Workqueues.App.Router.map(function () {
this.resource('delinquencies', function () {});
this.resource('delinquency', { path: '/:delinquency_id' });
});
The reason your code works right now is because the link-to helper already provides the context so the model hook never gets called.
For the model hook in the DelinquencyRoute you should use something like this:
return this.store.find('delinquency', params.delinquency_id);
For more info, have a look at the guides and getting started tutorial:
http://emberjs.com/guides/routing/defining-your-routes/ (the dynamic segments section answers your question)
Related
I am just starting with ember and trying to do a simple test.
Which, also very simple, got me stuck for some reason and I cant find the answer anywhere.
So I need load data from the server without transition to another route and do it from within a submit action (or any other action for that matter).
I have a simple input form where I type in manually an object ID and
I want it to be loaded say right underneath. Simple enough. Seams to be a three minutes job in angular. Here, I just cant get the hang of communication between route and controller.
So given this little emblem
form submit="submit"
= input type="text" value=oid
button type="submit" Submit
#display
= person
And this route
import Ember from 'ember';
export default Ember.Route.extend({
model: {
person: null
},
actions: {
submit: function() {
var oid = this.controllerFor('application').get('oid');
var person = this.store.find('person', oid);
this.modelFor('application').set('person', person);
}
}
});
This is as far as I could think. I want to click submit with ID of an object and I want that object loaded and displayed in the div#display.
So what am I doing wrong? What is the right way to do it?
First, I don't even know where to put such an action? Controller or route?
If I put it in controller, I don't know how to refresh the model. If I put it in route, I am stuck with the above. Would be also nice to see how to do it if action was placed in the controller.
For simplicity I just do it all in application route, template, controller ...
Thank you
The best place to put your code is on Controller given it responds to UI, so doing that on your controller the code is much more simple.
On this jsfiddle I have put some dummy code which tries to do something what you want to achieve.
//Index Route
App.IndexRoute = Ember.Route.extend({
model: function () {
return ['red', 'yellow', 'blue'];
}
});
//Here my dummy controller.
App.IndexController = Ember.Controller.extend({
oid: 1,
actions: {
submitAction() {
//Here your logic to find record given the input and attach
//the response to the model object as UI is binding to model
//if you add/remove new records they will show up.
//On this example I have added a new object.
this.get('model').addObject('green');
}
}
})
Enjoy!
I'm making a simple web chat system with Ember.
I have a route /chatrooms that lists a few chatrooms, and then I also have /chatrooms/:chatroom_id that should show the actual chatroom with messages.
The second route is within the first one, like this:
this.resource('chatrooms', function() {
this.route('show', {
path: ':chatroom_id'
});
});
When I access /chatrooms, a call is made to the server (/api/chatrooms) is a list of rooms is returned and displayed, like expected.
When I click a room, the application transitions to /chatrooms/id, but no call is made to retrieve the messages (available at /api/chatrooms/id), even when I try to define a model.
I have a similar scenario with the users. A list of users is retrieved, then displayed. When a name is clicked, the profile is shown. No second call is made, but that's okay since Ember knows everything about the user already.
In my current case, when a list is first returned, it includes all the information except the messages. I believe that would be too much otherwise (10 chatrooms * 100 last messages = 1000 elements in my JSON for each request). So I want to call the server for messages only when a chatroom is selected.
Do you know how to do it, or maybe there's something wrong I'm doing in the first place?
Updates
Template code from app/templates/chatrooms.hbs
<h1>Chatrooms</h1>
<ul class="sub-menu nobullet flex mas">
{{#each chatroom in model}}
<li class="mrs">{{link-to chatroom.name "chatrooms.show" chatroom class="pat"}}</li>
{{/each}}
</ul>
{{outlet}}
In this case, model is an array of chatrooms.
My routes:
app/routes/chatrooms.js
export default Ember.Route.extend({
model: function() {
return this.store.find('chatroom');
}
});
app/routes/chatrooms/show.js
export default Ember.Route.extend({
model: function(params) {
return this.store.get('chatroom', params.chatroom_id);
},
actions: {
send: function() {
...
}
}
});
As discussed in this thread, when you link-to a route and the model is already loaded, the model hook of the route is not fired because there’s no need to reload the data.
If you transition to a route and all the context objects -the objects which will serve as models to templates- are passed in, the beforeModel and model hooks will not be called.
Later in the thread balint corrects:
In fact, the beforeModel hook still gets called in that case, it is only the model hook that does not.
If you want to force the model to be reloaded, you can change your link to use the ID instead of the model:
{{link-to chatroom.name "chatrooms.show" chatroom.id class="pat"}}
You could also load the data in the beforeModel or afterModel hooks, or setupController.
Also, in the chatrooms/show route, you are getting the already-loaded model from the Ember Data store rather than loading it from the server. Try this:
return this.store.find('chatroom', params.chatroom_id);
I ended up adding a links property to the JSON response for chatrooms. When the content of a chatroom has to be displayed, the link is used and the messages retrieved. It only requires two requests, and there's not need to preload all the messages from all the chatrooms and no need to make a request for each message.
I'm getting some curious behaviour that I can't figure out the reason for.
This is my router:
App.Router.map(function() {
this.resource('mapPieceSets', { path: '/map-pieces' }, function () {
this.resource('mapPieceSet', { path: '/:mapPieceSet_id' }, function () {
this.resource('mapPiece', { path: '/:mapPiece_id' });
});
});
});
I reload the app from the home page #/ then navigate down to the mapPiece route, I get these URLs requested:
[Domain]/api/mapPieceSets/
[Domain]/api/mapPieces/1/
[Domain]/api/mapPieces/2/
And it all works fine (mapPieceSets returns a list of mapPieceSet which have a list of mapPiece against them)
However, if I reload the whilst on a mapPiece routed page, then I get this URL:
[Domain]/api/mapPieceSets/
[Domain]/api/mapPieceSets/?mapPieceSet_id=1
[Domain]/api/mapPieces/?mapPiece_id=1
So switching from /:id= to ?id=, which isn't working on my end points (that's a side issue which I need to resolve), but just wondering why the URLs changed what they're requesting, and why we get a request to mapPieceSets/?mapPieceSet_id=1 when the whole of that object is returned within the response from mapPieceSets/
(If you need any other snippets of code from my app, let me know and I can put them in)
This is a fairly common confusion. When you're in your app navigating around you're often using a link-to which is then telling ember to use the specified model when visiting the route. When you're refreshing the page, Ember has to divine the models using the url /apimapPieceSets/3/2. At that point it will go to each route MapPieceSetsRoute, MapPieceSetRoute, and MapPieceRoute and hit each model hook passing in any associated params. So what you need to tell Ember how to do, is how to load a mapPieceSet, and mapPiece properly. You'll need to setup a model hook for both of those.
App.MapPieceSetsRoute = Em.Route.extend({
// I don't know if you're using Ember Data, but I'm going to assume you are
model: function(params){
return this.store.find('mapPieceSet', params.mapPieceSet_id);
}
});
From what you said, it sounds like the model is already available client side from the mapPieceSets. In that case, you can use the modelFor method to get a parent's route's model and get your model.
App.MapPieceSetsRoute = Em.Route.extend({
// I don't know if you're using Ember Data, but I'm going to assume you are
model: function(params){
return this.modelFor('mapPieceSets').get('properyWithMapPieces').findBy('id', params.mapPieceSet_id);
}
});
i'm currently building an ember app using Yeoman Ember Generator.
this is my template folder structure looks like:
template
|---requisitions
|---draft.hbs
|---pending.hbs
|---waiting.hbs
requisitions.hbs
app.hbs
application.hbs
this is my router.js
Metabuyer.Router.map(function () {
this.route('app');
this.resource('requisitions', function(){
this.resource('draft');
this.resource('pending');
this.resource('waiting');
});
});
in my DS.Store , i have Requisition model which working just fine.
Metabuyer.RequisitionsRoute = Ember.Route.extend({
model: function () {
return this.store.findAll('requisition');
}
});
Draft, pending and waiting route share the same requisition model but filter it based on their needs, like below
Metabuyer.DraftRoute = Ember.Route.extend({
model: function(params){
var filterResult = this.store.filter('requisition', function(requisition){
return requisition.get('state') === 'draft';
});
console.log(test);
return filterResult;
});
}
});
My problem is.
When i use this.resource('draft') in my router nothing is being rendered in my page (blank page), but in my console, the filtered objects are being returned.
if i used this.route('draft') the page is rendered, but the content of the page are not filtered, or should i say, my Metabuyer.DraftRoute is not being called.
Thank you so much for your help, :'(
http://emberjs.com/guides/routing/defining-your-routes/
Routes nested under a resource take the name of the resource plus their name as their route name.
An index route is also needed on parent routes.
So navigating to /requistions loads up the RequesitionsRoute and RequisitionsIndexRoute you need to set the model on RequisitionsIndexRoute and use RequisitionsIndexControlleretc. You will need to rename requisitions.hbs to index.hbs and move it to the requisitions directory.
You also need to prefix your draft route object name with the parent so DraftRoute becomes RequisitionsDraftRoute and the same for controllers, views etc.
I have a two page Ember.js application using ember-data to wrap a simple RESTful API. The main page is a list of products, and the other page is a product details page for one product. The data is loaded via an API that only has an "index" request, /api/products.
The above works fine when navigating the site via the main page, however I'm not sure how best to handle navigating directly to the product details page. I need ember-data to request all products and keep these products client-side so that as the user navigates the simple site it doesn't make any more requests back to the API for products. However, the ProductIndexView and ProductIndexController in my application should preferably only see the one record.
Is there a good way to handle this in Ember.js? I know that I could add a computed property to the controller that filters down the full list and then pass that into the view template. However, I'd rather the view and controller not know about the full list.
You need to nest all your routes in a resources that fetches all products.
Something like this:
App.Route.map(function() {
this.resource('products', { path: '/' }, function() {
this.route('index');
this.resource('product', { path:'/:product_id'} );
});
});
App.ProductsRoute = Ember.Route.extend({
model: function() {
return App.Product.find({});
}
});
App.ProductsIndexRoute = Ember.Route.extend({
model: function() {
return this.modelFor('products');
};
});
Use the products/index template to display all products.
Use the product template to display a product detail.
Notice the {} I included in App.Product.find({}). This enforces ember-data to return a promise. This is necessary to make the product route wait for all products to arrive from the server before being called.