angulartypescriptobservablepass-by-referencepass-by-value

Workaround for primitive-type pass by reference


I'm running into a problem, and not an expert of async/rxjs development.

Let me explain my problem:

I have an angular service TimeoutService which only contains a processTimeout() method:


  processTimeout(timeout: NodeJS.Timeout, stillLoading: boolean): void {
    if (timeout !== undefined) {
      clearTimeout(timeout);
    }

    timeout = setTimeout(() => {
        if (stillLoading) this.dialog.open(LoadingDialogComponent);
    }, this.TIMEOUT_DURATION_MS);
  }

processTimeout() does the following:

Problem is, the stillLoading boolean passed to method is an attribute of my standard angular components (for example lets call it ComponentA that has a boolean loadingA attribute ), and I already know that js/ts has pass-by-value for primitive types:

It is set to true when I call my api service and set to false when loading is completed (when subscribe to my api request has been done).

But setting the value of loadingA to false occurs after I call the processTimeout() method from my service.

  1. So my question is, what is the best way/ best practice so the boolean used in processTimeout() stays a reference or pointer (the same for me but I do not really know how Js/Ts works behind the scenes, but a variable should have an internal memory / DOM Hex address) or even observable or something so the value of stillLoading in processTimeout() stays the same as my attribute loadingA of my componentA ? (Attribute loadingA is passed as stillLoading parameter and is also a primitive boolean type in componentA).

Because of the nature of the timeout that makes the code inside it execute after the timeout, i need the boolean value at that time (from the componentA), and not the value it was when passed-by-value in the method.
I'm down to listen to every possible solution, and if possible respecting ts and angular best practices.

  1. While writing the question, I also think I'll need the same solution for the timeout object too (But as it is an object it shall already be passed-by-reference right ?)

Thanks.

Tried: not a lot to be honest because I prefer some advice before losing hours on this problem, had an idea to pass a newly instanciated observable pointing on my componentA loadingA attribute, but I'm not sure at all of my comprehension of the way observables works for this kind of cases.

Expecting: the stillLoading method parameter in processTimeout being the same variable (same memory address) than my componentA loadingA boolean attribute, or any workaround solution/clues on tools i can use to fullfill my need.


Solution

  • METHOD 1:

    The code does not necessarily need to be on the service, you can create a common class that two children inherit, that contain this code. This will ensure that the function has access to the component variables, because each child will have its own scope!

    export class Common {

      processTimeout(timeout: NodeJS.Timeout, stillLoading: boolean): void {
        if (timeout !== undefined) {
          clearTimeout(timeout);
        }
    
        timeout = setTimeout(() => {
            if (stillLoading) this.dialog.open(LoadingDialogComponent);
        }, this.TIMEOUT_DURATION_MS);
      }
    

    }

    child

    export class ParentA extends Common {
        loading: false;
    
        constructor(private timeoutService: TimeoutService) {
            super()
        }
    
        ngOnInit() {
            this.timeoutService(5000, this.loading);
            this.loading = true;
        }
    }
    

    This can be done with any number of children!


    METHOD 2:

    As you said you can pass by reference and ensure the service has the latest value!

    In your component, you can call it like

    export class ParentA {
        loadingWrapper = {
            loading: false,
        }
    
        constructor(private timeoutService: TimeoutService) {}
    
        ngOnInit() {
            this.timeoutService(5000, this.loadingWrapper);
            this.loadingWrapper.loading = true;
        }
    
    }
    

    in your service you can change it to

      processTimeout(timeout: NodeJS.Timeout, stillLoadingWrapper: {loading: boolean}): void {
        if (timeout !== undefined) {
          clearTimeout(timeout);
        }
    
        timeout = setTimeout(() => {
            if (stillLoadingWrapper.loading) this.dialog.open(LoadingDialogComponent);
        }, this.TIMEOUT_DURATION_MS);
      }