cat-t
cat-t

Reputation: 1376

Using returned promise data

I have something like:

App.IndexController = Ember.ObjectController.extend({
   results : function(){

      return new Ember.RSVP.Promise(function(resolve, reject){

        Ember.$.getJSON('/search').then(function(res){
          console.log('response data is: ', res);
          resolve(res)
        })

      })

   }.property(),
  ...
})

// data
$.mockjax({
  url: '/search',
  responseText : {
    type: 'different people',
    res: [
      {name: 'charlie', age: '55'},
      {name: 'bobby', age: '19'},
      {name: 'raymond', age: '39'}
    ]
  }
})

my jsbin

How do I actually use the returned data? Currently, results returns a promise ( this.get('results') ) so I can't use it in my hbs template. Do I need to convert it to an object and then return that object?

Upvotes: 0

Views: 583

Answers (2)

Mitch
Mitch

Reputation: 1555

Answering the Question

Internally Ember uses a PromiseProxyMixin to do the magic rendering of promises that we know and love. Here is your updated JSBin working with a PromiseProxy:

http://emberjs.jsbin.com/danazu/edit?html,js,output

Your results property becomes this:

results: Ember.computed.promise(function(resolve, reject) {
  Ember.$.getJSON('/search').then(function(res) {
    console.log('response data is: ', res);
    return resolve(res);
  });
})

However I don't recommend this. Take a look at this discourse thread to get some information on why you might not want to do this. In short, it will be clunky to handle all the different states of promises.

Looking For a Better Way

The Router is the perfect place to deal with promises with AJAX requests. There must be some reason that you're not just loading this data in your Router in the model or afterModel hooks. Could you create another nested resource to represent these search results and then just link-to that resource?

In your Router:

Router.map(function() {
  this.resource('search', { path: '/search/:term' });
});

App.PeopleRoute = Ember.Route.extend({
  model: function(params) {
    return Ember.$.getJSON('/search?term=' + params.term);
  }
});

If that won't work at the very least you could:

  1. Send an action when you want the search results
  2. Handle the AJAX request in the controller
  3. Set the results on the controller when they are resolved.

Upvotes: 1

mistahenry
mistahenry

Reputation: 8724

I use ic ajax for easy use with promises. I don't currently use Ember data so I wrote myself a nice rest client on top of ic ajax to easily get/post/put to urls:

rsvpAjax: function(url,method, data){
      var headers = {};

      // build request
      var opts = {};
      var defaultOpts = {
        type: method,
        headers: headers,
        data: JSON.stringify(data),
        dataType: 'json',
        xhrFields: {
          withCredentials: true
        }
      };
      return ajax.request(url, $.extend({}, defaultOpts, opts));
}

And then I have get method:

getAsync: function(url){
      return this.rsvpAjax(url, "GET");
}

Then using it elsewhere in a facade/helper class:

import objFactory from 'appname/models/obj';
...
...
foo: function(){
        //get your rest client somehow
        var apiFacade = this.getRestClient();
        return restClient.getAsync('url_string').then(function(response){
            //targets some part of the response - here its an array of 
            var data = response.data;
            return data.map(function(obj){
                return objFactory.create(obj);
            });
        });
}

foo returns an array of Ember objects. the objFactory I import is just a Ember.Object class. The obj passed into the create generally is a one to one match with the properties in said Ember.Object. With an initializer, I inject this object into every route:

import someFacade from "app/facades/facade";

export default {
    name: 'someFacade',

        initialize: function(container, app) {
        //a singleton by default
        container.register('facades:someFacade',someFacade);
        app.inject('route', 'someFacade', 'facades:someFacade');
    }
};

Any of my routes can use this facade without importing it thanks to DI.

//Ember blocks
model: function(){
    return this.someFacade.foo();
}

or:

setupController(controller, model){
    //set controller prop on return of resolved val
    this.someFacade.foo().then(function(foosReslovedVal){
        controller.set('someProp', foosResolvedVal);
    });   
}

Upvotes: 0

Related Questions