Incrementally adding models to controller - ember.js

I have a standard issue route like:
App.MessagesRoute = Ember.Route.extend({
model: function() {
return App.Message.find();
}
});
Which works great for the existing set of messages that i get from a REST endpoint. But I also get new messages via a websocket. How in the RC2 routing architecture (and non-Ember Data store) how do I plumb new message like these into Ember cleanly?

I solved this using the Ember.Instrumentation framework element that's talked about in How to fire an event to Ember from another framework.
Here's my complete code:
...
// this is fired from my websocket message reception code.
Ember.Instrumentation.instrument('onMessage', message);
...
App.MessagesRoute = Ember.Route.extend({
setupController: function(controller, model) {
Ember.Instrumentation.subscribe('onMessage', {
before: function(name, timestamp, message) {
controller.send('onMessage', message);
},
after: function() {}
});
},
model: function() {
return App.Message.find();
}
});
App.MessagesController = Ember.ArrayController.extend({
onMessage: function(message) {
this.unshiftObject(message);
}
});

Related

Ember-simple-auth ApplicationRoute model function

I have classic setup of Ember-simple-auth, in ApplicationRoute I use
model: function () {
return Ember.RSVP.hash({
user: this.store.find('gsUser').then(function(data) {
return data.get('content')[0]
})
});
},
setupController: function(controller, model) {
this.controllerFor('user').set('content', model.user);
}
When user losts authorization, and you open the page. ApplicationRoute::model is fired first, server returns 401 and other execution is stopped.
GET http://localhost:8000/app_dev.php/api/1/users.json 401 (Unauthorized)
Error while loading route: undefined
model should be fired only when authentication is successfull.
I saw that there is sessionAuthenticationSucceeded but I've tried all the ways to listen to it, noone worked. How to listen to this event and get data from server when user is successfully authenticated?
11/06 22:57 UPDATE:enter code here
One solution for this problem that I've managed to achieve, but it seems totally not ember way:
App.ApplicationRoute = Ember.Route.extend(Ember.SimpleAuth.ApplicationRouteMixin, {
skipModelLoading: false,
beforeModel: function() {
this.set('skipModelLoading', !this.get('session').get('isAuthenticated'));
},
model: function () {
if (this.get('skipModelLoading')) {
return;
}
return Ember.RSVP.hash({
user: this.store.find('gsUser').then(function(data) {
return data.get('content')[0]
})
});
},
setupController: function(controller, model) {
if (this.get('skipModelLoading')) {
return;
}
this.controllerFor('user').set('content', model.user);
}
});
I assume you're loading the authenticated user in that model method. I'd do it differently and attach that property to the session as shown in this example: https://github.com/simplabs/ember-simple-auth/blob/master/examples/4-authenticated-account.html#L101
I think I found a more ember-way solution for my problem:
App.ApplicationRoute = Ember.Route.extend(Ember.SimpleAuth.ApplicationRouteMixin, {
onSessionIsAuthenticated: function () {
var isAuthenticated = this.get('session').get('isAuthenticated');
if (!isAuthenticated) {
return false;
}
var userController = this.controllerFor('user');
return Ember.RSVP.hash({
user: this.store.find('gsUser').then(function (data) {
userController.set('content', data.get('content')[0]);
})
});
}.observes('session.isAuthenticated').on('init')
});

ember data reload() undefined

I am trying to reload a model that has changed on the server. My code is as follows:
App.CustomersController = Ember.ArrayController.extend({
intervalId: undefined,
startRefreshing: function() {
var self = this;
if ( self.get( 'intervalId' ) ) {
return;
}
self.set( 'intervalId', setInterval( function() {
//self.get('model').update();
self.get('model').reload();
}, 30000 ) );
}
});
App.CustomersRoute = Ember.Route.extend({
model: function() {
return this.store.find('customer');
},
setupController: function( controller, model ){
this._super( controller, model );
controller.startRefreshing();
},
actions: {
reload: function() {
this.get('model' ).reload();
}
}
});
You can see that I have two mechanisms for reloading the data - one a timer, and also an action triggered by a button in the UI. The latter is exactly what is shown in the ember-data documentation here: http://emberjs.com/api/data/classes/DS.Model.html#method_reload
Neither works. I get undefined in both cases i.e. the model returned does not have a reload() method. update() sort of works, except it does not remove deleted records and it is not what is recommended in the documentation. What am I doing wrong here in trying to use reload?
My stack:
DEBUG: -------------------------------
DEBUG: Ember : 1.5.1+pre.07fafb84
DEBUG: Ember Data : 1.0.0-beta.7.f87cba88
DEBUG: Handlebars : 1.3.0
DEBUG: jQuery : 1.11.0
DEBUG: -------------------------------
and I am using the following adapter in case that makes any difference:
App.Store = DS.Store.extend({
// Override the default adapter with the `DS.ActiveModelAdapter` which
// is built to work nicely with the ActiveModel::Serializers gem.
adapter: '-active-model'
});
reload exists on a record, not a collection.
You would need to iterate the collection and call reload on each record.
self.get('model').forEach(function(record){
record.reload();
});
But I'm guessing you don't want to waste the callbacks to the server. In this case I'd recommend returning a filter as your model, then make another call to the server for all records.
App.CustomersRoute = Ember.Route.extend({
model: function() {
this.store.find('customer');
return this.store.all('customer');
},
setupController: function( controller, model ){
this._super( controller, model );
controller.startRefreshing();
},
actions: {
reload: function() {
this.get('model' ).reload();
}
}
});
App.CustomersController = Ember.ArrayController.extend({
intervalId: undefined,
startRefreshing: function() {
var self = this;
if ( self.get( 'intervalId' ) ) {
return;
}
self.set( 'intervalId', setInterval( function() {
self.store.find('customer'); // get all customers again, updating the ones we have
}, 30000 ) );
}
});

How to avoid too many empty records?

Ember : 1.5.0-beta.2
Ember Data : 1.0.0-beta.7
I have the following router:
App.Router.map(function() {
this.resource('posts', function() {
this.route('new');
});
});
My PostsNewRoute creates a new record in the model hook:
App.PostsNewRoute = Ember.Route.extend({
model: function() {
return this.store.createRecord('post');
}
});
Since I don't want transient record to be visible, I filter them out in my PostsRoute:
App.PostsRoute = Ember.Route.extend({
model: function() {
this.store.find('post');
return this.store.filter('post', function(post) {
return !post.get('isNew');
});
}
});
This works as expected, but every transition to posts.new add a new record to the store, which is something I would like to avoid. So, instead of calling createRecord every time the model hook is called, I filter the store for an empty record and return this if there is one found:
App.PostsNewRoute = Ember.Route.extend({
model: function() {
var route = this;
return this.store.filter('post', function(post) {
return post.get('isNew');
}).then(function(result) {
return result.get('firstObject') || route.store.createRecord('post');
);
});
This gives me at the most one empty record.
My question: is there a better way to avoid my store being populated with (many) empty records ?
UPDATE:
Instead of filtering on the isNew attribute, I can probably use currentModel:
model: function() {
this.get('currentModel') || this.store.createRecord('post');
};
You can use this addon https://github.com/dockyard/ember-data-route to clean up when you leave a /new route. It hooks into the willTransition action hook that gets called on the route whenever a transition occurs.
The source code is a short read: https://github.com/dockyard/ember-data-route/blob/master/addon/mixins/data-route.js.
The alternative would be to not create a new record in the model hook, but according to a comment of yours it doesn't seem to be an option.

Computed property in handlebar #if not updating

I am trying to use the following template:
<script type="text/x-handlebars" data-template-name="login">
{{#if logged_in}}
Logged in
{{else}}
Not logged in
{{/if}}
</script>
with the model:
App.Login = DS.Model.extend({
access_token: DS.attr('string'),
logged_in: function() {
return (this.get('access_token') != null);
}.property('access_token')
});
to display the user's logged-in state.
The access_token is being set via an async callback in the Route's setupController:
App.LoginRoute = Ember.Route.extend({
setupController: function(controller, model) {
controller.set('content', model);
// call async login method
window.setInterval(function test() {
model.set('access_token', 'MY_ACCESS_TOKEN');
console.log(model.get('access_token'));
}, 5000);
},
model: function() {
return App.Login.find();
}
});
The problem is logged_in never seems to change (even though the model.set line is executed and 'access_token' is updated). Am I doing something wrong or should I be filing a bug?
Full code: http://jsfiddle.net/Q8eHq/
You are setting the model to App.Login.find() which returns an enumerable, not a single object. One way to do it, is to set the model to a single object:
App.LoginRoute = Ember.Route.extend({
model: function() {
return App.Login.find(1);
}
});
Or if you are going to use a dynamic route (e.g. users/login/9):
App.LoginRoute = Ember.Route.extend({
model: function(params) {
return App.Login.find(params.id);
}
});

Same Ember.JS template for display/edit and creation

I am writing a CRUD application using Ember.JS:
A list of “actions” is displayed;
The user can click on one action to display it, or click on a button to create a new action.
I would like to use the same template for displaying/editing an existing model object and creating a new one.
Here is the router code I use.
App = Ember.Application.create();
App.Router.map(function() {
this.resource('actions', {path: "/actions"}, function() {
this.resource('action', {path: '/:action_id'});
this.route('new', {path: "/new"});
});
});
App.IndexRoute = Ember.Route.extend({
redirect: function() {
this.transitionTo('actions');
}
});
App.ActionsIndexRoute = Ember.Route.extend({
model: function () {
return App.Action.find();
}
});
App.ActionRoute = Ember.Route.extend({
events: {
submitSave: function () {
this.get("store").commit();
}
}
});
App.ActionsNewRoute = Ember.Route.extend({
renderTemplate: function () {
this.render('action');
},
model: function() {
var action = this.get('store').createRecord(App.Action);
return action;
},
events: {
submitSave: function () {
this.get("store").commit();
}
}
});
The problem is that when I first display an action, and then come back to create a new one, it looks like the template is not using the newly created record, but use instead the one displayed previously.
My interpretation is that the controller and the template are not in sync.
How would you do that?
Maybe there is a simpler way to achieve this?
Here is a JSBin with the code: http://jsbin.com/owiwak/10/edit
By saying this.render('action'), you are not just telling it to use the action template, but also the ActionController, when in fact you want the action template, but with the ActionNewController.
You need to override that:
this.render('action', {
controller: 'actions.new'
});
Updated JS Bin.