1

In an Ember controller, I want to call my API to create a user and on success, transitionToRoute. This is what I currently want to work:

import ajax from "ic-ajax";
import Ember from "ember";

export default Ember.Controller.extend({
  actions: {  
    createAndLoginUser: function() {
      var user = { "user": this.getProperties("email", "name") };
      ajax({ url: "api/users", type: "POST", data: user })
        .then(transitionToHome);
    }   
  }
});

var transitionToHome = function() {
  this.transitionToRoute("home")
}

But when I place a debugger in the method, this is no longer the controller object and is out of scope to call transitionToRoute.

I've only ever written hacky javascript, but I'm trying to learn core concepts and some frameworks. Is this the right way to use a promise? And is this the right place in Ember to put a transition?

nevets138
  • 80
  • 8

2 Answers2

3

Your problem has nothing to do with Ember or transitions; it's all about this handling. The simplest solution is just

.then(transitionToHome.bind(this));

Putting transition method inside the controller

You could also consider putting transitionToHome inside the controller as a method. Then, you could call it as in the following:

export default Ember.Controller.extend({

  transitionToHome: function() { this.transitionToRoute("home"); },

  actions: {  
    createAndLoginUser: function() {
      var self = this;
      var user = { "user": this.getProperties("email", "name") };
      ajax({ url: "api/users", type: "POST", data: user })
        .then(function() { self.transitionToHome(); });
    }   
  }

});

This might be a simpler approach which is more readable and doesn't require reasoning about this and using bind (but does require using self).

Moving the transition to the route?

Outside the scope of your question, but some would say that route-related logic (including transitioning) logically belongs in the route, not the controller. If you agree, you could refactor the above to do a send

createAndLoginUser: function() {
  var user = { "user": this.getProperties("email", "name") };
  var self = this;
  ajax({ url: "api/users", type: "POST", data: user })
    .then(function() { self.send("goHome"); });
  }
}

and then implement the goHome action in your route. Or, you could implement goHome in a higher-level route, even the application route, thus making it available from any controller or lower-level route.

Moving the AJAX logic to a service?

Others might say that the ajax logic does not really belong here in the controller, and should rightfully be in a services layer, so

// services/user.js
export function createUser(data) {
  return ajax({ url: "api/users", type: "POST", data: { user: data } });
}

// controller
import { createUser } from 'app/services/user';

export default Ember.Controller.extend({
  createAndLoginUser: function() {
    var data = this.getProperties("email", "name"));
    var self = this;
    createUser(data) . then(function() { self.send("goHome"); });
  }
};

Using ES6 syntax, we can avoid the self, simplifying a bit in the process:

  createAndLoginUser: function() {
    var data   = this.getProperties("email", "name"));
    var goHome = () => this.send("goHome");

    createUser(data) . then(goHome);
  }

Now this code is starting to look more semantic and readable.

  • Alternatively, when I try to add it as a controller method, I receive the same `Cannot read property 'transitionToHome' of undefined` `transitionToHome: function() { this.transitionToRoute("home") },` EDIT: Just added my controller method code. – nevets138 May 12 '15 at 03:16
  • Sorry, there was a this/self problem now fixed. –  May 12 '15 at 03:28
  • Thanks for all the feedback too! I'm definitely going to try those refactors, I've been struggling a bit with the Ember documentation. – nevets138 May 12 '15 at 13:57
  • Got these refactors working, the code looks great! A couple of things I encountered making them: 1) `createUser` import had to be `../services/user`? 2) the `createUser` method was apart of the class `user` from the import? Other than that, thanks again! – nevets138 May 13 '15 at 03:11
  • Thanks for the follow-up. You can import using either a relative or absolute path. The former would be `from '../services/user'`, the latter would be `from '*appname*/services/user'`, replacing `appname` with the actual name of your application, and assuming `services` is at the top of your tree. I prefer the latter in this case. Wrt your second point, my mistake, The import should have been `import {createUser} from `...'`. –  May 13 '15 at 03:39
  • We're now completely working like a charm, thanks again! – nevets138 May 14 '15 at 00:07
1

An alternative solution is to simply use ES6 arrow syntax to maintain the context of this:

import ajax from "ic-ajax";
import Ember from "ember";

export default Ember.Controller.extend({
  actions: {  
    createAndLoginUser() {
      let user = { "user": this.getProperties("email", "name") };
      ajax({ url: "api/users", type: "POST", data: user })
        .then(() => {
          this.transitionToRoute("home");
        });
    }   
  }
});
Adam Reis
  • 4,165
  • 1
  • 44
  • 35