Ember.js setting application property on load - ember.js

I'm trying to fetch the current logged in user via my REST API and then set it as a property of the ApplicationController. This is how I'm trying to do it:
App.ApplicationController = Ember.Controller.extend({
init: function() {
this._super();
var self = this;
App.User.findCurrent().then(function(user) {
self.set('currentUser', user);
});
}
});
App.User = Ember.Object.extend({});
App.User.reopenClass({
findCurrent: function() {
return $.getJSON('/api/v1/users/current').then(
function(response) {
return response.user;
}
);
}
});
When I check the Chrome network tab, I see there's a call to the API and the JSON is returned, but when I try to access e.g. {{currentUser.name}} in my application template (or a partial of it), it doesn't return the name. No errors are given as well.
But in the application template it doesn't return it.
What am I missing?
Thanks!
Edit
When I create another controller, e.g. HelpController and visit /help, then {{currentUser.name}} does return the username:
App.HelpController = Ember.Controller.extend({
needs: ['application'],
currentUser: Ember.computed.alias('controllers.application.currentUser')
});
Edit 2
Sorry, I forgot to mention that I'm actually trying to use {{currentUser.name}} from a partial ({{partial 'sidebar'}}), but that shouldn't change anything, because that's the same scope, right?
Edit 3
I noticed something very strange. When I call {{currentUser.name}} in my application template (which is not what I want btw), then it also works in the {{partial 'sidebar'}}.
Edit 4
As per request:
DEBUG: Ember.VERSION : 1.0.0-rc.6 ember.js?body=1:361
DEBUG: Handlebars.VERSION : 1.0.0-rc.4 ember.js?body=1:361
DEBUG: jQuery.VERSION : 1.10.0

This isn't the correct place to put this logic. You can use the route hooks model and afterModel on the ApplicationRoute, to do this easily. In general in ember loading of data is done in the routes hooks. This allows the router pause while loading so by the time your controller and templates come into play, they are working with loaded data.
App.ApplicationRoute = function() {
model: function() {
return App.User.findCurrent();
},
afterModel: function(model) {
App.set('currentUser', model)
}
}

Related

"undefined" instead of ID in URL on transition in Ember and Ember Data

I have a pretty standard post model with a title and a text field. I have two routes for the model -- new route and show route. I want to create a post from new route and then transition to show route.
This is my router file
this.route('post-new', { path: '/posts/new' });
this.route('post-show', { path: '/posts/:postId' });
and submit action in post-new controller is something like this.
actions: {
submit() {
const { title, text } = this.getProperties('title', 'text');
let post = this.store.createRecord('post', {
title: title,
text: text
});
post.save().then(() => {
//success
this.transitionToRoute('post-show', post);
}, () => {
//error
});
}
}
So I am expecting this to redirect from http://localhost:4200/posts/new to something like http://localhost:4200/posts/23 (assuming 23 is id).
The save() is successful and record is created on the backend (which is rails) and I also see the post record updated in browser (it now has an ID) using Ember Inspector. But the redirection is happening to http://localhost:4200/posts/undefined.
How can I make this to redirect to something like http://localhost:4200/posts/23 after save ?
Btw, The versions are:
ember cli : 2.3.0-beta.1
ember : 2.3.0
ember data : 2.3.3
UPDATE
I was able to make it work by replacing this
this.transitionToRoute('post-show', post);
with this
this.transitionToRoute('/posts/' + post.id);
But I am hoping for a solution using the route name and not actual route path.
Try:
post.save().then(savedPost => {
//success
this.transitionToRoute('post-show', savedPost);
},
You can implement the serialize hook on your route.
serialize(model) {
return { postId: model.get('id') };
}
This will allow you to avoid calling the model hook if you already have the model. So, both of these will work as expected:
this.transitionToRoute('post-show', post); // this will NOT call model() hook
this.transitionToRoute('post-show', post.id); // this will call the model() hook
More information available in the API docs for Route.

Routing error with ember-data 2.0 and emberjs 2.0.1

Cross-posting from discuss.ember. I am using Ember 2.0.1 with Ember-data 2.0 and default the default RESTSerializer generated by ember-cli. I know this question has been asked to many places before (which none have real answers) but no solutions have been working for me yet.
I have this model hook for a user model :
export default Ember.Route.extend({
model() {
return this.store.findAll('user');
}
});
Router is the following :
Router.map(function() {
this.route('users', { path: '/' }, function() {
this.route('user', { path: '/:user_id' }, function(){
this.route('conversations', { path: '/'}, function(){
this.route('conversation', { path: '/:conversation_id' });
});
});
});
});
For example, going to /conversations/4 transitions to users.user.conversations. My relations are defined in my models. In the user model I have a DS.hasMany('conversation') conversations attribute set with { embedded: 'always' }. Returned JSON looks like this :
{"conversations":[
{
"id":183,
"status":"opened",
"readStatus":"read",
"timeAgoElement":"2015-08-20T16:58:20.000-04:00",
"createdAt":"June 16th, 2015 20:00",
"user":
{
"id":4
}
}
]}
The problem I get is that Ember-data is able to add my data to the store but I get this error :
Passing classes to store methods has been removed. Please pass a dasherized string instead of undefined
I have read these posts : #272 and #261
Is it a problem with the JSON response?
Thank you. I have been using ember-data for quite a bit of time and never encountered this error before switching to ember 2.0.1 and ember-data 2.0.0
EDIT : I am now sure it is related to the embedded conversations because in ember inspector, if I try to see the conversations of a user (and the conversations are loaded into the store), it returns me a promiseArray which isn't resolved.
Try not to push objects to store directly. Possible use-case of .push() :
For example, imagine we want to preload some data into the store when
the application boots for the first time.
Otherwise createRecord and accessing attributes of parent model will load objects to the store automatically.
In your case UserController from backend should return JSON:
{"users" : [ {"id":1,"conversations":[183,184]} ]}
Ember route for conversation may look like:
export default Ember.Route.extend({
model: function(params) {
return this.store.find('conversation', params.conversation_id);
}
}
User model:
export default DS.Model.extend({
conversations: DS.hasMany('conversation', {async: true})
});
You don't have to always completely reload model or add child record to store. For example you can add new conversation to user model:
this.store.createRecord('conversation', {user: model})
.save()
.then(function(conversation) {
model.get('conversations').addObject(conversation);
});
P.S. Try to follow Ember conventions instead of fighting against framework. It will save you a lot of efforts and nervous.
Your conversation route has URL /:user_id/:conversation_id. If you want it to be /:user_id/conversations/:conversation_id, you should change this.route('conversations', { path: '/'}, function(){ to this.route('conversations', function(){ or this.route('conversations', { path: '/conversations'}, function(){

Ember {{render}} with the corresponding Ember.route logic and model?

I don't understand Ember {{render}} explanation when it comes to the model (http://emberjs.com/guides/templates/rendering-with-helpers/). What is "singleton instance of the corresponding controller"?
When no model is provided it gets the singleton instance of the corresponding controller
When a model is provided it gets a unique instance of the corresponding controller
I am trying to embed a view on a page. I have a Post page that I want to embed Comments/New in the Post page so that I can comment on the Post page.
I can go to comments/new and add a new comment. But when I try to embed in the Post page using {{render}}, it has errors because it does not include the CommentsNewRoute logic.
The problem is that I have logic in CommentsNewRoute, but it appears that when I use {{render}} it ignores the Route logic. How can I get {{render}} to just use the corresponding Route logic?
CommentsNewRoute:
App.CommentsNewRoute = Ember.Route.extend({
model: function(){
return this.store.createRecord('comment');
},
actions: {
willTransition: function(transition) {
if (this.currentModel.get('isNew')) {
this.get('currentModel').deleteRecord();
};
}
}
});
CommentsNewController:
App.CommentsNewController = Ember.ObjectController.extend({
actions: {
save: function() {
// do save new comment stuff
}
}
});
Post/Index .hbs template:
<h1 class="page-header">Post {{id}}</h1>
{{render "comments/new"}} <<<<<< I want to embed the whole Comments/New page here, including the logic from CommentsNewRoute
Router:
this.resource('post', { path: '/posts/:id' }, function() {
this.route('edit');
});
this.resource('comments', { path: '/comments' }, function() {
this.route('new');
});
Version:
DEBUG: ------------------------------- ember.js
DEBUG: Ember : 1.6.0-beta.5 ember.js
DEBUG: Ember Data : 1.0.0-beta.8.2a68c63a ember.js
DEBUG: Handlebars : 1.3.0 ember.js
DEBUG: jQuery : 1.11.1 ember.js
DEBUG: -------------------------------
Yes, {{render}} knows nothing about routes and is completely independent from them. For the controller used by render to have a model is going to require it to get it from somewhere else. If you don't pass the render helper the model, you could populate the model on the controller by doing something like
content: function() {
return ['blue', 'green', 'red'];
}.property()
or setting it in the controller's init.
In general,{{render}} is best used for independent components on pages. Think of a notification widget which is independent of the page content, yet needs a view/template/controller and perhaps model. For the reasons you mention, it is probably better not to try to use {{render}} for a controller which is also being used by a route, where the route logic such as model and setupController will not be available. Instead, factor out your logic so that the {{render}} is independent, then invoke it from the template for your route.

Emberjs call a method from an other object

This might be a silly question, but I can't find out anything about it anywhere...
I create a method in one of my controller to verify if the user session is still good, and I'm using this method in almost every page of my app in my beforeModel. But the thing is that I don't want to copy/paste the code every time in every route, this will be dirty and I really don't like it.
Lets say I have this controller :
App.LoginController = Ember.ObjectController.extend({
...
isSession: function() {
var session = this;
Ember.$
.get(host + '/session', function(data) {
console.log('DEBUG: Session OK');
})
.fail(function() {
console.log('DEBUG: Session FAIL');
session.transitionToRoute('login');
});
}
});
How can I call it in this router :
App.HomeRoute = Ember.Route.extend({
beforeModel: function(transition) {
//Here
},
model: function() {
return this.store.all('login');
}
});
I've tried this this.get('loginController').isSession(); but I receive this error Error while loading route: TypeError: Cannot call method 'isSession' of undefined
Thanks for the help !
[edit]
I don't have much to show but this :
My map
App.Router.map(function() {
this.route('login', { path: '/' });
this.route('home');
this.resource('enquiries', function() {
this.route('enquiry', { path: '/:enquiry_id' }, function() {
this.route('update');
});
});
});
Most likely I only Have a LoginController and my HomeRoute. (its the beginning of the app)
I don't need to create a Route for my Login because I have an action helper in my login template and I'm redirected to my Home template after that.
You need to use controllerFor() method in order to call method on controller from router. If method is an action you need to use send() method, like this.controllerFor('login').send('isSession')
App.HomeRoute = Ember.Route.extend({
actions: {
willTransition: function(transition) {
transition.abort();
this.controllerFor('login').isSession()
}
});
If you don't need a return value from isSession you might consider making it an action on a top-level route. The router.send method in the docs has a pretty good example of how you declare actions as well as how you call them. Note that send is also a method you can call on a controller. Actions bubble up from a controller, to the parent route, and then all the way up the route hierarchy, as shown here

Connecting a View in connectOutlet with Ember RC1

From this [EDIT] [ToDo's sample]1, [/EDIT] I can connect a View via the connectOutlet. Is there an updated example for this using RC1?
index: Ember.Route.extend({
route: '/',
connectOutlets: function( router ) {
var controller = router.get( 'applicationController' );
var context = controller.namespace.entriesController;
context.set( 'filterBy', '' );
// This require was left here exclusively for design purposes
// Loads decoupled controller/view based on current route
require([ 'app/controllers/todos', 'app/views/items' ],
function( TodosController, ItemsView ) {
controller.connectOutlet({
viewClass: ItemsView,
controller: TodosController.create(),
context: context
});
}
);
}
}),
Actually the example you are linking should work. As you might know the Router API has changed and the code based on pre4 should still work. I am not aware of the requirements for the Todos App, so i cannot 100% tell, if it still works:
Todos.Router.map(function() {
this.resource('todos', { path: '/' }, function() {
this.route('active');
this.route('completed');
});
});
Todos.TodosRoute = Ember.Route.extend({
model: function() {
return Todos.Todo.find();
}
});
Todos.TodosIndexRoute = Ember.Route.extend({
setupController: function() {
var todos = Todos.Todo.find();
this.controllerFor('todos').set('filteredTodos', todos);
}
});
Here a little summary of the changes to the old router API:
You don't extend the Ember.Router Class anymore.
The URL Mappings don't reside in the Routes anymore. This is done via Todos.Router.map.
There is no connectOutlets event anymore in your routes. Instead there are 3 events you can implement: model(), setupController() & renderTemplate().
A little explanation on the hooks:
model(): Is called once when your route is entered via URL. This should return your model, which should become the content of your controller.
setupController(): Here you can get your controller and set its content how you may like. The default implementation sets the controller, that is name matching your route to the result of model().
renderTemplate(): Inside this hook you should use the new render method of routes to do the rendering. The render method is somehow the method that matches the old connectOutlets the most. There is also default implementation. Therefore it is also not implemented in the pre4 version of todomvc.
As Milkyway stated, you realy have to read the guides, but i hope this gets you started a little bit better.