-1

Seeking some guidance on a problem modifying an object within a closure function. I'm rewriting a program in Angular 5. My program uses a store to manage a form wizard, where some questions are answered using svg elements/buttons. Once clicked an object property is set. The code below is a skeleton version of what I'm having a problem with.

I've read other closure posts but I did not come across one that fits my scenario.

Thanks for answers and/or suggestions to refactor.

class Builder {
  object:any;

  constructor(){
    this.object = {
       car:'blue'
    }
  }

  init(){
    //Short cutting to click commands.. Using lodash
    _.forEach(database, (o)=>{
      console.log(this.object.car); // 'blue' to console
      ((function(){
         button.click(function(){
            this.object.car = 'red';
            console.log(this.object.car); //Cannot read property 
                                              'object' of undefined
         });

       })(),false)
    }
  }

}

Update to original post

I think I need to add a bit more info to my questions for others that may run into this problem. The code I provided was a skeleton of my overall code.

Before testing I did refactor the function() calls using ()={} arrow notation (in different combinations) and this did not solve the problem, because it further broke methods requiring (this).element as a result of the button.click().

user3200548
  • 111
  • 1
  • 10
  • Your error looks weird to me. If the assignment before doesn't throw, how can `this` be undefined? – ASDFGerte Jun 11 '18 at 18:52
  • @ASDFGerte, maybe the OP assumes that `Cannot read property 'object' of undefined` comes from line `console.log(this.object.car);` when it actually comes from `this.object.car = 'red';`, since it's very probably this line will throw that same error. – lealceldeiro Jun 11 '18 at 18:57
  • 2
    To preserve the reference to `this`, you could replace each occurrence of `function() {...}` with an arrow function `() => {...}`. – ConnorsFan Jun 11 '18 at 19:12
  • 1
    Possible duplicate of [How to access the correct \`this\` inside a callback?](https://stackoverflow.com/questions/20279484/how-to-access-the-correct-this-inside-a-callback) – ConnorsFan Jun 11 '18 at 19:12
  • @lealceldeiro while both would throw a TypeError and the spec doesn't seem to say what the message of said error should be, that is possible. However, chrome, FF, and node all show proper messages that are distinguishable for these cases, making it highly unlikely the error he shows comes from that code (together with the statement that the only other place that could throw this error supposedly logs "blue"). – ASDFGerte Jun 11 '18 at 19:49

2 Answers2

1

One way here is if you pass this as a parameter, e.g.

   ((function(that){
     button.click(function(){
        that.object.car = 'red';
        console.log(that.object.car);
     });

   })(this),false)     // <--  pass 'this' here

And with an arrow function, which preserves this, you could do something like this

    console.log(this.object.car); // 'blue' to console
    button.click(() => {
        this.object.car = 'red';
        console.log(this.object.car);
    });
Asons
  • 84,923
  • 12
  • 110
  • 165
  • Needs to get `this` as argument like this: `((function(that){ ... })(this),false)` – lealceldeiro Jun 11 '18 at 18:50
  • Or OP could use arrow functions instead, which preserves `this` – user184994 Jun 11 '18 at 19:22
  • @user184994 Maybe so, and as me not being an expert when it comes to arrow functions, how does those work/look when replacing a _closure_? – Asons Jun 11 '18 at 19:29
  • Just replace `function ()` with `() => ` – user184994 Jun 11 '18 at 19:34
  • ... and remove the IIFE. – ConnorsFan Jun 11 '18 at 19:35
  • Since the OP passes the function as an argument to `button.click`, the equivalent would be something like this: `button.click(() => {...})`. – ConnorsFan Jun 11 '18 at 19:49
  • @ConnorsFan Thanks, updated, and learned something new :) ... and since I need to support IE I just haven't taking the time to read up on these new stuff – Asons Jun 11 '18 at 19:50
  • Thanks @LGSon and others.. This solution does solve this issue in the scenario I described.. Like I stated, the example was a skeleton to my problem, which also included using other libraries, relying on 'this' in creating the buttons. – user3200548 Jun 12 '18 at 17:48
0

Thanks to everyones' comment and submitted answer. I want to state that the answer provided by @LGson is a solid answer and works. But I felt I needed to clean up this post a bit and share what I did to solve this issue for my situation which uses a canvas framework to generate the UI elements that rely on (this) on the element within the button.click() event and where I could not access the outside object.

Here is how I was able to get this to work.

  1. I changed the outer function to the arrow notation format and left the button.click() call as stated in the problem.
  2. I added a variable for self that points to the object I need to access.
  3. I added a getter method in the object to return the value of the internal reference needed and used self.car and self.getter to set and retrieve the value that I needed. And the library method continues to work as expected.

    class Builder {
     object:any;
    
     constructor(){
      this.object = {
        car:'blue',
        get value():{ return this.car;}
       }
      }
    
     init(){
      var self = this.object;
    
      _.forEach(database, (o)=>{
      console.log(this.object.car); // 'blue' to console
      ((()=>{
        button.click(function(){
             self.car = 'red';
             console.log(self.value); // red
    
        });
    
       })(),false)
    

    } } }

user3200548
  • 111
  • 1
  • 10