Can I use a pre-computed value as url parameter in emberjs - ember.js

I am just playing with angularjs and the ui-router module and I am not satisfied with it.
I am missing a function/a process that allows me to execute logic before a state is activated.
The logic is to calculate the first day of the week of the current logged in user. Thus before the weekly view of a calendar is activated I have to execute this date logic so the user gets an url like:
/dateplanner/week/'firstdayOfWeek'
Its not enough for me to show a /dateplanner/week url.

Yes. In overriding a route's beforeModel function you can call this.transitionTo() and provide a different route and model as parameters. This will automatically abort the current route transition.
For example:
App.Router.map(function() {
this.resource('dateplanner', function() {
this.resource('week', { path: "/week/:firstDay" });
});
});
App.WeekRoute = Ember.Route.extend({
beforeModel: function(transition, queryParams) {
return someFunctionToGetFirstDayReturningPromise().then(function(firstDay) {
return this.transitionTo('dateplanner.week', firstDay);
});
}
});
You can find another example in the guide here (one that doesn't use promises or asynchronous code):
http://emberjs.com/guides/routing/redirection/#toc_based-on-other-application-state
API References:
http://emberjs.com/api/classes/Ember.Route.html#method_beforeModel
http://emberjs.com/api/classes/Ember.Route.html#method_transitionTo

Related

Loading Routes in nested route Hierachy

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

Ember Data is always fetching records for route

I just switched my application over to Ember CLI and Ember-Data (previously using Ember Model). When I transition to my employees route ember data does a GET request on the api's user route with a query as intended. However, whenever I leave this route and return it continues to perform a GET request on the api. Shouldn't these results be cached? I had a filter running on the model, but I removed it and still ran into the same issue.
Route w/ Filter:
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
// This queries the server every time I visit the route
return this.store.filter('user', {type: 'employee'}, function(user) {
if(! Ember.isEmpty(user.get('roles'))) {
return user.get('roles').contains('employee');
}
});
}
});
Route w/out Filter:
import Ember from 'ember';
// This still queries the server every time I visit the route
export default Ember.Route.extend({
model: function() {
return this.store.find('user');
}
});
Passing a second parameter into the filter function, {type: 'employee'}, turns it into a findQuery + filter, and find will always execute a query request. If you want to only call a particular resource once per SPA lifetime in a particular route you can add logic to keep track of it. The basic concept goes like this:
Check if you've fetched before
If you haven't fetch the records
Save the fetched records
return the saved fetched records
Example
export default Ember.Route.extend({
model: function() {
//resultPromise will return undefined the first time... cause it isn't defined
var resultPromise = this.get('resultPromise') || this.store.find('user');
this.set('resultPromise', resultPromise);
return resultPromise;
}
});
Additionally if you've already called find you can also just use store.all('type') to get all of the records for that type in the store client side without making a call to the server.

Computed property for the number of records in the store?

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"),
...

Find query concept for route and controller

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

How do I use an object controller in Ember.js with an API that only returns all objects?

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.