Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to correct function scope?

In my template, I'm using some third party component which renders a stepper, where you can navigate between the steps:

<my-stepper step="{{ currentStep }}" steps-label="steps"
   [stepChangeCallback]="stepChangeCallback">
   <my-stepper-step>First</my-stepper-step>
   <my-stepper-step>First</my-stepper-step>
   <my-stepper-step>First</my-stepper-step>
</my-stepper>

and

export class HeaderComponent implements OnInit {
   currentStep = 0;

   stepChangeCallback(selectedStep) { 
     this.currentStep = selectedStep; // PROBLEM: this does not point to HeaderComponent
   }
}

So I thought I could just generate a new function with a fixed this pointer:

getStepChangeCallbackFunction() {
   return this.stepChangeCallback.bind(this);
}

and [stepChangeCallback]="getStepChangeCallbackFunction()"

This does work, however, I see that the memory consumption is rising constantly and that the website will eventually crash the browser, since it constantly generates new function copies.

Is there any other solution?

like image 760
user66875 Avatar asked Feb 16 '26 04:02

user66875


2 Answers

As for me, the best option here is to use instance arrow function:

stepChangeCallback = (selectedStep) => { 
  ...
}

You could use [stepChangeCallback]="stepChangeCallback.bind(this)" but it will also create a new function every change detection run.

Another option is to bind this in constructor.

export class HeaderComponent implements OnInit {   
  constructor() {
    this.stepChangeCallback = this.stepChangeCallback.bind(this);
  }

   stepChangeCallback(selectedStep) { 
     ...
   }
}
like image 73
yurzui Avatar answered Feb 18 '26 18:02

yurzui


In addition to @yurzui answer, I think there is an anti-pattern with my-stepper component. Instead of taking an function as an input and calling it within, you can dispatch an event from my-stepper on stepChange.

Here is how you can do it

@Component({
  selector: 'my-stepper',
  ...
})
export class MyStepperComponent {

  @Input()
  stepChangeCallback; // <- currently you have this.

  @Output() // instead of an input, just define this output.
  stepChange: EventEmitter<any> = new EventEmitter(); 


  onStepChange(selectedStep) {
    // currently, you call this.stepChangeCallback and you need to check if it is defined
    if (this.stepChangeCallback) {
      this.stepChangeCallback(selectedStep);
    }

    // instead you can simply emit an event and do not care about who is listening
    this.stepChange.emit(selectedStep);
  } 
}

Then your template would be like following and you would not need to use arrow function as well

<my-stepper step="{{ currentStep }}" steps-label="steps"
   (stepChange)="stepChangeCallback($event)">
   <my-stepper-step>First</my-stepper-step>
   <my-stepper-step>First</my-stepper-step>
   <my-stepper-step>First</my-stepper-step>
</my-stepper>
like image 43
Bunyamin Coskuner Avatar answered Feb 18 '26 18:02

Bunyamin Coskuner



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!