0

I have a function which returns an observable

  getData(){
    return this.http.get<{mat: any[], mat_auf: any[], mat_lok: any[], auf_an: any[]}>(this.authUrl + 'get/mat-data').pipe(
      map(({mat, mat_auf, mat_lok, auf_an}) => {
        console.log("AuthService", {mat, mat_auf, mat_lok, auf_an}); // correct data format
        mat.map(mat => {
          this.matAdapter.adapt(mat);
        });
        mat_auf.map(mat_auf => {
          this.mat_aufAdapter.adapt(mat_auf);
        });
        mat_lok.map(lok => {
          this.lokAdapter.adapt(lok)
        });
        auf_an.map(an => {
          this.aufAdapter.adapt(an)
        });
      })
    );
  }

The conversion by the adapters works fine if I look in the console but when I try to subscribe to the Observable in another component I get undefined:

ngOnInit(){
    this.authSrv.getData().subscribe(res => console.log(res)) // res is undefined but why?
}

An adapter looks like this:

@Injectable({
    providedIn: "root"
})
export class MatAdapter implements Adapter<Mat>{
    adapt(mat: any): Mat {
        return new Mat(mat.ID, mat.name, mat.location);
    }
}

As I said the adapters work fine as in the console the correct object is logged. But why is the data undefined, when I try to subscribe to the observable? I think I use the map function wrongly.

Any help is appreciated. If further information is needed, please leave a comment. Thank you

CNIS
  • 101
  • 1
  • 7
  • 2
    `console.log()` ***always returns `undefined`!!!*** so if you map to `console.log(something)` you *log* the value of `something* but *produce* the value `undefined`. – VLAZ Nov 30 '20 at 07:57
  • @VLAZ hey I just used console.log to see if the conversion was correct. But the data is still undefined if I leave out the console.log part. I change it to avoid misunderstandings. Thanks! – CNIS Nov 30 '20 at 07:59
  • 2
    Oh, also, you don't even have a `return` statement in the first block with the `.map`. So when `getData()` is called it calls ` this.http.get().pipe(map())` but there is no mapping result. The implicit return is always `undefined`. "*hey I just used console.log to see if the conversion was correct*" please do **not** use `map` for simple iteration. Use `forEach` or an actual loop. It's extremely misleading for a start. – VLAZ Nov 30 '20 at 08:02
  • @VLAZ Thank you for all the information. Could you leave an example as answer, I would mark your answer! – CNIS Nov 30 '20 at 08:03
  • 1
    I'd suggest reading [Is performing a mapping operation without using returned value an antipattern?](https://stackoverflow.com/q/56903693) as that's exactly the problem here is - performing a map over some data without returning anything. – VLAZ Nov 30 '20 at 08:05
  • @VLAZ thank you, you saved me few hours! It works now as expected – CNIS Nov 30 '20 at 08:20
  • 1
    As additional tip specific to `rxjs`: If you want to perform a side effect on an observable without changing the observable, `rxjs` provides the `tap` operator. That would replace the outer map in your example. – Gunnar B. Nov 30 '20 at 08:38
  • @GunnarB. Thank you for this information! – CNIS Nov 30 '20 at 09:06

1 Answers1

1

If you assign a value to a function with return-type void, you get undefined.

function addAndLog(x,y): void {
  console.log(`x + y = ${x + y}`);
}

const added = addAndLog(8,10);
consle.log(added);

// Output: 
// "8 + 10 = 18"
// undefined

I hope you can understand that even though I logged the two numbers added together, that doesn't mean added is given that value. addAndLog doesn't return a value and therefore added will always be undefined.

Your mapping operation maps every value it received to undefined. The function you give map is run and its return value is assigned as the mapped value. In this case you never return anything.


Rewritten to work:

function addAndLog(x,y): void {
  console.log(`x + y = ${x + y}`);
  return x + y;
}

const added = addAndLog(8,10);
consle.log(added);

// Output: 
// "8 + 10 = 18"
// 18
Mrk Sef
  • 7,557
  • 1
  • 9
  • 21