pattmorter pattmorter - 2 months ago 15
Javascript Question

Ember.js with Socket.io

I am trying to impliment a simple Ember application within my node application view. I know Ember is set up and my sockets are working correctly, now the only problem is that the data isn't seemly being returned even though it is being retrieved.

Here is what I have:

App = Ember.Application.create({
rootElement: '#ember'
});

App.Router.map(function() {
// put your routes here
});

App.IndexRoute = Ember.Route.extend({
model: function() {
async.waterfall([
function (callback) {
socket.emit('getUserList', { data: null });
callback(null, '');
}, function (res, callback) {
var userList = new Array();
socket.on('recieveUserList', function (data) {
for(var i=0; i<data.userList.length; i++) {
userList.push(data.userList[i].name);
}
});
callback(null, userList);
}
], function (err, result) {
return result;
});
}
});


Now if I
console.log(results)
I get back ['John Smith', 'Jane Doe'] but it doesn't print out on my page with:

<script type="text/x-handlebars">
<ul>
{{#each item in model}}
<li>{{item}}</li>
{{/each}}
</ul>
</script>


From looking at examples it should work, right?

EDIT
Here is the fiddle http://jsfiddle.net/UJ4Su/

Answer

Fixing Your Fiddle

In your view try this:

<script type="text/x-handlebars" data-template-name="index">
  <ul>
  {{#each item in model}}
    <li>{{item}}</li>
  {{/each}}
  </ul>
</script>

(Note the explicit reference to the index route for this template)

Here's an updated version of your fiddle that's working:

EXAMPLE: http://jsfiddle.net/UJ4Su/13/

Note that this example will not work with your sockets because you are not returning a promise. Since you are returning a normal array (technically you are returning nothing since you don't have a return call outside of your async socket call), Ember expects the data to be there immediately. See below for how to fix this.

Handling Async Calls in your Route Part 1

Since you are making an async call to populate userlist you will need to return an empty userlist first and then populate it when your socket event resolves. Here's a fiddle that demonstrates this using setTimeout to simulate the async nature of the socket calls:

App.IndexRoute = Ember.Route.extend({
    model: function(){
      var data = Ember.A();  
      window.setTimeout(function(){
         data.pushObject('a');
         data.pushObject('b');
         data.pushObject('c');
      }, 1000);    
      return data;
    }
});

You need to use an Ember Array as well (versus a native Javascript array) so that Ember can observe changes on the array and update your templates for you.

EXAMPLE: http://jsfiddle.net/UJ4Su/7/

(See this page for more information on taking advantage of asynchronous routing in Ember: http://emberjs.com/guides/routing/asynchronous-routing/)

Taking Advantage of Ember.Deferred or Ember.RSVP

The above is very simple but if you want to take even more advantage of Ember then I would suggest using Ember.Deferred or RSVP Promises directly. Ember.Deferred is a simple mixin that acts as a wrapper around some of the RSVP functionality (specifically then(), resolve(), and reject()). Here is an example using Deferred:

App.IndexRoute = Ember.Route.extend({
    model: function(){
      var deferredData = Ember.Deferred.create();          
      var data = [];
      window.setTimeout(function(){
         data.push('a');
         data.push('b');
         data.push('c');
         deferredData.resolve(data);
      }, 3000);    
      return deferredData;
    },
    actions: {
        loading: function(transition, originalRoute){      
           return true;   
        }
    }
});

Basically we return an Ember.Deferred instead of the Ember Array. Until the Deferred is resolved Ember will transition into a loading state. You can then display a loading data message to the user while the data is retrieved (either via sockets or AJAX). Here's a working fiddle example:

EXAMPLE: http://jsfiddle.net/UJ4Su/10/

Here is an example using Ember Promises:

EXAMPLE: http://jsfiddle.net/UJ4Su/12/

More info on Loading/Error Substates can be found here: http://emberjs.com/guides/routing/loading-and-error-substates/

Hope that helps!