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.
Related
I have an Ember app that uses server-side storage. In the products route, I have a list of products that I display in my product route (which are fetched in the usual way upon entering the route)
{{#each item in sortedProducts}}
{{/each}}
....
fetch
App.ProductRoute = Ember.Route.extend({
model: function(){
return Ember.RSVP.hash({
store: this.store.find('product'),
//other code ommitted
})
}
})
In the controller, I do the sorting
sortProperties: ['date:desc'] //dated added
sortedProducts: Ember.computed.sort('model', 'sortProperties'),
This works, however, I give the user the option to filter the records displayed. Upon clicking a button, an action is called that queries the server for a subset of records (it doesn't just filter the records that are already in the store cache)
actions: {
filterByPriceAndColor: function(){
this.store.find('product', {price: pricevariable, color: colorvariable});
}
}
This queries and returns the desired records, but the list on the page isn't updated i.e. the list on the page still displays all the records that are fetched upon application load.
Question: how do I get the page to update with the new records fetched from the server without a route change, (and will the solution integrate with the computed sort that already exists for ordering the entries by date)
To update your model from an action (or anywhere else) you simple need to set a new value for it and Ember will to the hard work for you.
In your case it should look like this:
actions: {
filterByPriceAndColor: function() {
var promise = this.store.find('product', {price: pricevariable, color: colorvariable});
var self = this;
promise.then(function(data) {
self.set('model', data);
});
}
}
Here is a JSBin demonstrating how it works: http://emberjs.jsbin.com/walunokaro/3/edit
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 am working on a mobile application with Ember. I want to make the user experience as good as possible and try to take into account that on mobile the connection is not always as good, that is why I want to utilize the loading routes with a loading spinner. Unfortunately in one case it is not behaving as I would expect:
In my Nested route Setup:
UserRoute:
UserIndexRoute (=Profile)
UserFriendsRoute
On the UserRoute I only load a small version (=different model) of the user. In 95% of the cases this model is already loaded when I want to navigate there. And in the Subroutes (e.g. UserIndexRoute and UserFriendsRoute I only need the full user.
What I want to achieve is that the UserRoute with its template is directly rendered when navigating to e.g. UserIndexRoute and then in the outlet for the Index part I want the UserLoadingView to be rendered. But the rendering always waits for all promises to be resolved and the UserLoadingView is never shown.
How can I force Ember to render the UserRoute and then the UserLoadingView in the outlet until the UserIndexRoute Model is resolved?
How I implemented it:
afterModel: function(model, transition){
var _this = this,
params = Ember.get(transition, 'params.user');
this.get('store').find('user', params.user_id).then(function(user){
_this.transitionTo('user.profile', user);
});
}
Don't use the index route for fetching the full model, just use it as a means for redirection.
Do something like this:
UserRoute:
UserIndexRoute
UserFooIndexRoute (=Profile) (Naming is up to you)
UserFriendsRoute
Then hook up your index route to fetch the full model and transition to FooIndex when it's completed getting the model, this depends on it being a route with a dynamic segment (:id).
App.UserIndexRoute = Em.Route.extend({
redirect: function(){
var self = this;
fetchTheFullModel.then(function(model){
self.transitionTo('user.fooIndex', model);
}
}
});
If it isn't like that you can do just transition to the other route after the transition and page has finished rendering.
App.UserIndexRoute = Em.Route.extend({
redirect: function(model, transition) {
var self = this;
transition.then(function(){
Ember.run.scheduleOnce('afterRender', function(){
self.transitionTo('user.fooIndex');
});
});
}
});
http://emberjs.jsbin.com/zohav/1/edit
You can read more about the transition promise, and afterRender here Ember transition & rendering complete event
This may be abusing Ember, but I want to create a computed property for the number of items in the store.
I'm trying to prototype a UI that exists entirely client-side. I'm using fixture data with the local storage adapter. That way, I can start off with canned data, but I can also add data and have it persist across reloads.
As I'm currently working on the data layer, I've built a settings route that gives me a UI to reset various models. I would like to add a Handlebars expression like {{modelCount}} so I can see how many records there are in the store. That's quicker than using the Ember Data Chrome extension, which resets to the routes tab on every page reload.
The following will show me the number of records once, but does not change when the number of records changes:
modelCount: function() {
var self = this;
this.store.find("my_model").then(function(records) {
self.set("modelCount", records.get("length"));
});
}.property()
I get that the store is supposed to proxy an API in the real world, and find returns a promise, but that's about the limit of my knowledge. I don't know how tell Ember to that I want to know how many records there are, or if this is even a valid question.
Load the result of store.find into an Ember.ArrayController's content and then bind the length of content to modelCount. An example:
App.SomeRoute = Ember.Route.extend({
model: function(){
return this.store.find('my_model');
}
});
App.SomeController = Ember.ArrayController.extend({
modelCount: Ember.computed.alias('content.length')
});
See a working example in http://jsbin.com/iCuzIJE/1/edit.
I found a workable solution by combining the answer from #panagiotis, and a similar question, How to load multiple models sequentially in Ember JS route.
In my router, I sequentially load my models:
model: function() {
var self = this;
return new Ember.RSVP.Promise(function(resolve, reject) {
self.store.find("model1").then(function(model1) {
self.store.find("model2").then(function(model2) {
self.store.find("model3").then(function(model3) {
resolve({
model1: model1,
model2: model2,
model3: model3
});
});
});
});
});
},
Then in my controller, I define simple computed properties:
model1Count: function() {
return this.get("model1.length");
}.property("model1.length"),
...
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