I am currently learning Ember and I am making a simple app, but I have encountered a strange problem. I have a route setup and it only pulls the data when I reload the page. Here is my code:
// Start Ember application
App = Ember.Application.create({
LOG_TRANSITIONS: true
});
// Router paths
App.Router.map(function () {
// Homepage
this.resource('index', { path: '/' });
// Book view
this.resource('book', { path: '/book/:id/:version' });
});
// Homepage route
App.IndexRoute = Ember.Route.extend({
model: function () {
// Get all the books
return Ember.$.getJSON('/books');
}
});
// Single book view
App.BookRoute = Ember.Route.extend({
model: function (params) {
// Get a single book
return Ember.$.getJSON('/book?id=' + params.id + '&version=' + params.version);
}
});
When I go to /#/book/1/1 by clicking from the homepage, the page is blank. When I just refresh the page when I'm on there it loads the data and everything works.
Why is this? What can I do to make it work when the user clicks from the homepage?
Thank you everyone for your suggestions. I figured it out with this code here:
App.BookRoute = Ember.Route.extend({
setupController: function (controller,model) {
// Get a single book
Ember.$.getJSON('/book?id=' + model.id + '&version=' + model.version,
function (data) {
controller.set('model',data);
});
}
});
I used setupController instead of model.
you should pass a model in link-to. paste your template code to check how you are creating links. check this for more info http://emberjs.com/guides/templates/links/
If you use link-to helper you must not set context model in this one... you simply need to set an id,
{{#each book in books}}
{{#link-to "book" book.id}}{{book.name}}{{/link-to}}
{{/each}}
and the model of the next route will be request.
The Ember link-to helper doesn't execute the model callback. I've dealt with a situation very much like this recently, and here's how I solved it:
// In the index template
{{#each book in model}}
{{#link-to 'book' book}}{{book.name}}{{/link-to}}
{{/each}}
When you click on the link, you'll get a blank page like you are now, because the BookRoute model callback isn't fired. However, the BookController#init function will be fired. You can use this to go and grab the rest of the details about the book there.
App.BookController = Ember.Controller.extend({
init: function() {
this._super();
// Replace doesNotHaveAllInfo with your check
if (doesNotHaveAllInfo) {
this.set('model', Ember.$.getJSON('/book?id=' + this.get('model.id') + '&version=' + this.get('model.version')));
}
},
});
This way, if the book is loaded through link-to, the new details will be fetched. Note that this will require that the books loaded on the index route contain at least id and version.
Alternatively, if you refresh the page, the model callback will occur, and your doesNotHaveAllInfo check will return false. Whether you click on a link-to or refresh the page, you should see your data.
I'd also recommend abstracting the Ember.$.getJSON code into a reusable method, perhaps on the model, like this:
App.Book.reopenClass({
find: function(id, version) {
return Ember.$.getJSON('/book?id=' + id + '&version=' + version);
}
});
You can then use App.Book.find() to return your model in both your route and your BookController.
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!
Here's an image that illustrates the design I was given and need to develop in ember. I'm a bit at a loss on how to handle the routing and implementation in ember.
So to explain this simplified example, say I have a search results page (results are being returned from the back end), where as you would expect the search results aren't always going to be the same. However, when I click on one of the search results, I need to be able to open the product's page as a nested route.
This raises a few problems given that someone returning at a later time will likely not get the same list of products. How would I handle the routing, something like this?
Router.map(function() {
this.resource('search', function(){
this.route('product', {route: ':productID'}, function())
});
});
I'm also not sure how to setup it up the heirarchy in terms of containerview, views, components, etc.
Help?
I think you need to create routes like that
this.resource('products', function() {
this.resource('product',{path: '/:id'});
});
After that you need to create 2 routes ProductsRoute and ProductRoute.
App.ProductsRoute = Ember.Route.extend({
model: function() {
return this.get('store').find('product');
}
});
App.ProductRoute = Ember.Route.extend({
model: function(product) {
return this.get('store').find('product', product.id);
}
});
When you will try to open page /products you will get all products. If you will use RestAdapter it sends request to your REST API /products
I think you need to create action in ProductsController to search products:
App.ProductsController = Ember.ArrayController.extend({
actions: {
searchProducts: function() {
var queryText = this.get('queryText');
if (queryText) {
return this.get('store').filter('product', {query: queryText}, function(item) {
return true;
});
}
}
}
});
This action sends request to your api with /products?query=queryText
In your products template create search form and use something like that {{action 'searchProduct'}} for Search button. Also in your template you need to show all products for that use {{#each item in model}} and to create link to each product use {{#link-to 'product' item}}. I think it will be best practice to create component for your search box http://guides.emberjs.com/v1.10.0/components/.
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 list of categories that are loaded on the application route:
ApplicationRoute = Em.Route.extend({
model: function() {
return Em.RSVP.hash({
collections: this.store.find('collection'),
categories: this.store.find('category'),
links: this.store.find('link')
});
}
});
This triggers the categories index action on the server, as expected. I am doing this because I need to load a list of categories for a menu.
I also have a category route:
Router.map(function() {
this.route('category', { path: '/categories/:category_id' });
});
CategoryRoute = Em.Route.extend({
model: function(params) {
this.store.find('category', params.category_id);
}
});
Since there's already a list of categories in the store, there's no request to the server. However, here I want to show more detailed information. I want to send a request to the server's show action.
I am probably not thinking in "the ember way", but it seems to me that I either have to, somehow, force the server request in the route, or create a separate model for the menu categories.
How should one go about this, with ember-data?
Notes:
ember.js-1.1.2 and
ember-data-1.0.0.beta.3
I ended up using a separate CategoriesList model. Still, this feels suboptimal.
my question is a little bit general. What is the best concept for route and controller with findQuery in ember.
I have api with data filtering. Data request is executed by
this.store.findQuery('dataModel', {"q": JSON.stringify({"filters": filters})});
after that I show them in table view. The filter is updated by form views in a template.
My current solution:
Form views set controller parameters and a button call action from controller. Controller action loads parameter, executes findQuery and set('content',data).
In most cases I saw concept with a defining model: function() .. in the Route and setupController: function(controller, model) with controller.set('content',model). I like this "set" because 'content' is RecordArray (not PromiseArray) and I can easily use that for datatables and another JavaScript plugins. I think my solution isn't good.
I think your concept is correct, I have been using the following flow:
In your router:
App.Router.map(function() {
this.resource('search', { path: '/query/:filters' });
});
App.SearchRoute = Ember.Route.extend({
model: function(params) {
return this.store.findQuery('dataModel', {"q": JSON.stringify({"filters": params.filters})});
});
In your html, just bind the action which will lead to the new Search Route,
something like below :
<button {{action "doSearch"}}>Search</button>
In your controller:
App.SearchController = Ember.ArrayController.extend({
...
actions: {
doSearch: function() {
var query = buildYourQueryObject();
this.transitionToRoute("search", query);
}
}
Upon clicking on the button, the app will transition into your search route, and "query" will be serialized and sent into the Route, and the Route.model() will attempt to be populated based on the serialized parameters provided.
Note: The code has been simplified, you might need to add more stuff in order to make it work