0
votes

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)
);
1
Inside the then callback, this probably does not refer to the controller. See 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: stackoverflow.com/help/mcve - Felix Kling
What Felix said. You could always .bind() the function in the then() to this so that it would be the correct keyword. - Jhecht

1 Answers

3
votes

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