0

I have a parent controller, UserEditCtrl and a child controller, EditUserCtrl. Inside of my parent controller I am pulling in a user object via a service:

userMgmtSvc.user(scope.editUserId).then(function(data) {
  this.user = data;
});

Then I want to set a property of my user object to another variable.

this.selectedRoles = this.user.roles;

But this throws an error:

Cannot read property 'user' of undefined.

I'm confused as to how I reference objects that are set with this. For example, how do I just console.log the object? Because console.log('user', this.user); returns undefined:

user undefined

Here's the parent controller:

(
  function (app) {
    /* @fmt: off */
    'use strict';

    // @fmt:on
    app.controller('UserEditCtrl', ['$scope', '$http', 'userMgmtSvc', 'createUserSvc', 'authSvc', '$state', '$timeout', '$location', '_',
      function (scope, http, userMgmtSvc, createUserSvc, authSvc, state, timeout, location, _) {

      userMgmtSvc.user(scope.editUserId.id || sessionStorage.getItem('editUser')).then(function(data) {
        this.user = data;
        console.log('user', this.user);

      // GET states
      createUserSvc.states().then(function(data) {
          this.states = data;
          console.log(this.states);
      });

      // GET countries
        createUserSvc.countries().then(function(data) {
        this.countries = data;
      });

      // GET roles
      createUserSvc.roles().then(function(data) {
        this.roles = data;
      });

      // GET insurance groups
      createUserSvc.insuranceGroups().then(function(data) {
        this.insuranceGroups = data;
      });

      this.selectedRoles = this.user.roles;
    });

 }]);

}(window.app)
);
reknirt
  • 2,237
  • 5
  • 29
  • 48
  • Inside the `then` callback, `this` probably does not refer to the controller. See [How to access the correct `this` / context inside a callback?](https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-context-inside-a-callback). In addition, at the moment you are trying to *read* `this.user.roles`, the `then` callback most likely hasn't been executed yet, so the value couldn't have been set, even if `this` referred to the correct objects. To help you better, you'd have to provide a **complete*** example, as described here: https://stackoverflow.com/help/mcve – Felix Kling Sep 08 '14 at 00:41
  • What Felix said. You could always `.bind()` the function in the `then()` to `this` so that it would be the correct keyword. – Jhecht Sep 08 '14 at 00:46

1 Answers1

3

This is a very basic mistake that happens when you refer to the current context with this inside a callback about which you don't know about the execution context and you end up setting values elsewhere.

In order to avoid getting into this issue, when your controller starts just set this (context of controller instance) to a variable and set everything on that. Don't assume what this is going to be.

 .controller('crtl',[deps..., function(...) {
       //Set this
       var vm = this; //Always use this cached variable have seen a commonly used name of vm

       //............... 
       //...............
       userMgmtSvc.user(scope.editUserId).then(function(data) {
         vm.user = data;
       });

       //...............
       vm.selectedRoles = vm.user.roles

   }

There are numerous other ways to do this using angular.bind, or es5 function.bind to create a bound functions (function reference pre bound with a specified context), but easiest way would be to use a cached context.

When you are using typescript you could use a => (fat arrow) syntax since typescript in ES5 mode will actually convert this.

      userMgmtSvc.user(scope.editUserId).then((data) => {
         this.user = data;
      });

to:-

    var _that = this;
    userMgmtSvc.user(scope.editUserId).then((data) => {
         _that.user = data;
     });

Arrow functions are going to be part of the language itself (with ES6 specifications) when the engines starts supporting the arrow function syntax. So with ES6 you could safely write:-

      userMgmtSvc.user(scope.editUserId).then((data) => {
         this.user = data;
      });

Here is an excellent answer that specifically targets this

Community
  • 1
  • 1
PSL
  • 123,204
  • 21
  • 253
  • 243
  • Can you explain what `_that` is doing? How can you set `_that.selectedRoles` to `_that.user.roles` when `_that` hasn't been defined yet? – reknirt Sep 08 '14 at 00:50
  • _that is just a variable that holds the reference (copy of reference) of the controller instance. Well you need to make sure it is defined in your controller before anything that uses this variable runs.. :D I h – PSL Sep 08 '14 at 00:52
  • I have now changed it to variable name as vm which just is an abbr for viewmodel. – PSL Sep 08 '14 at 00:57