1

From an x.component.ts I call getSomething() in y.service.ts. I subscribe to getSomething() because is returns an observable. The thing is that before the .subscribe() I have an object that has 4 elements inside, one of which is an array of number arrays (number[][]), and after the .subscribe that same array is now empty.

x.component.ts

this.y
      .getSomething()
      .pipe(
        //make an observable to pass to child component 
        tap(data => (this.pautaFinal$ = of(data))),
        // This object is just for development purpose
        tap(data => (this.pautaFinal = data)),
        // This is to change the number[][] object into a number[][][]
        // I need it that way
        tap(
          data =>
            (this.escalaPorcionada = this.transformEscala(
              data.escala.escalaArr,
              10
            ))
        )
      )
      .subscribe();

// Makes an array of number arrays, into an array of arrays of chunkSize number arrays.
transformEscala(escala: number[][] = [], chunkSize: number): number[][][] {
    let results: number[][][] = [];
    while (escala.length) {
      results.push(escala.splice(0, chunkSize));
    }
    return results;
  }

In this x.component I've also tried, instead of the third tap, a map(data => data.escala.escala) and then `.subscribe(data => this.escalaPorcionada = this.transformEscala(data, 10).

y.service.ts
  getSomething(): Observable<Pauta> {
    let admin: DatosAdmin;
    let escala: Escala;
    let rubrica: Rubrica;
    let criterios: Criterios;
    this.datosAdminAcumul$.subscribe(datos => (admin = datos));
    this.escalaAcumul$.subscribe(datos => (escala = datos));
    this.rubricaAcumul$.subscribe(datos => (rubrica = datos));
    this.criteriosAcumul$.subscribe(datos => (criterios = datos));

    let user = JSON.parse(localStorage.getItem("user"));

    // Ultimos datos que agregar a la pauta.
    let extras = {
      usuarioModificacion: user.id
    };

    const pautaFinal: Pauta = {
      datosAdmin: admin,
      criterios: criterios,
      rubrica: rubrica,
      escala: escala,
      extras: extras
    };
    return of(pautaFinal);
  }

This is the function im calling in y.service.ts. It has some observables that are in the same service, it subscribes to them, gets some values, assign them to something else and then all those, put them inside a pautaFinal object, and that's what I return as an observable.

What have i tried: I've checked the chrome debugger and before the subscribe the array exists, after, it is empty.

This is the value of escalaArr inside the observable this.escalaAcumul$ (from y.service) Before .subscribe() (escalaArr is the array that is lost)

This is after the subscribe. Just one function call after the previous image. After .subscribe()

This object has 4 more elements, and none of them change, just escalaArr.

I don't know what i'm doing wrong here. I've been stuck for a while and appreciate some help, thanks.

sebaLinares
  • 485
  • 5
  • 17

1 Answers1

1

Try to clone the object handed in by the Observable before processing it. This way you break the 'by reference'-chain of JavaScript.

this.y
  .getSomething()
  .pipe(
    //make an observable to pass to child component 
    tap(data => (this.pautaFinal$ = of(data))),
    // This object is just for development purpose
    tap(data => (this.pautaFinal = data)),
    // This is to change the number[][] object into a number[][][]
    // I need it that way
    tap(
      data =>
        (this.escalaPorcionada = this.transformEscala(
          Object.assign({}, data.escala.escalaArr), // <-- clone here to avoid changing the original
          10
        ))
    )
  )
  .subscribe();

If this does not solve the problem check whether any other component has access to the setter of your Observable.

EDIT:

Option 2:

transformEscala(escala: number[][] = [], chunkSize: number): number[][][] {
    let results: number[][][] = [];
    const clone = Object.assign({}, escala);

while (clone.length) {
  results.push(clone.splice(0, chunkSize));
}
return results;

}

  • I tried `map(data => deepCopy(data) as Pauta`, before the first `tap()`, but same result. – sebaLinares Feb 01 '19 at 09:34
  • 1
    Okay, I made an edit and added an option 2. Try to clone the object inside `transformEscala()`instead of inside the subscription. –  Feb 01 '19 at 09:41
  • Your vudu WORKED! Thanks man :clapping:!!!! Last thing, when you say "..instead of inside the subscription", you mean inside the `.subscribe()`? Because when I tried the `deepCopy` I did it before the `.subscribe()`?? – sebaLinares Feb 01 '19 at 09:48
  • 1
    You're welcome. Yes, I meant my example above where I put the Object.assign() inside the subscription()-block. –  Feb 01 '19 at 09:54
  • I tried to look for an explanation about the "clone" approach you proposed and couldn't find much. Could you recommend me somewhere to read about it. I need a better understanding. – sebaLinares Feb 01 '19 at 11:50
  • 1
    Unfortunately, I have no #1 page I could recommend on this topic. Sorry. -- The important point is the fact, that the `splice()`-operator changes the original array. In your case the array `escala`. So while you're splicing and pushing you actually manipulate the array inside your service. That's why the array is empty afterwards. Here is how `splice()` works in general. https://www.w3schools.com/jsref/jsref_splice.asp . Using `Object.assign()` decouples the original object and your `splice()`-operation only affects your local copy. https://googlechrome.github.io/samples/object-assign-es6/ –  Feb 01 '19 at 12:24
  • 1
    PS: Here is an interesting post concerning 'pass-by-reference', which is what happened in your example: https://stackoverflow.com/questions/13104494/does-javascript-pass-by-reference –  Feb 01 '19 at 12:30