Search code examples
angularrxjsspring-websocketbehaviorsubject

Angular 6 BehaviorSubject 'undefined': Error: TypeError: Cannot read property of my declared BehaviorSubject


I'm pretty new to Angular (as well as StackOverflow) and I've been trying to make a small project using Spring Boot as my back-end and WebSockets in attempt to make a small real-time web application (all these technologies are fairly new to me and I'm eager to learn them!). The problem I'm having is the use of BehaviorSubjects. I intend to use my BehaviorSubject ('messageSubject$') each time I receive a message from Spring Boot through a WebSocket. Basically once I receive a message I would just have my BehaviorSubject pick up on it by using the 'next()' method and then have one of my components subscribe to it (The subscription to my web socket and definition of my BehaviorSubject are all within a service). However when I do this I get an error displayed on Google Chrome's console: 'Error: TypeError: Cannot read property 'messageSubject$' of undefined'. I'm very confused as to why such an error would pop-up since I did make sure to define it correctly (I've watched multiple tutorials and they each seem to do it the same way).

Here is how I imported BehaviorSubject from rxjs:

import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';

Here is the BehaviorSubject definition (on top of the constructor):

private messageSubject$ = new BehaviorSubject<string>('default message');

Here is the subscription to my WebSocket and where I try to use my Behavior Subject:

    // Connecting to WebSocket found in Spring Boot MS. Also checkout polyfill.ts
  connect(user: string, password: string) {
    console.log('Inside Connect. Guest and Password: ' + user + ' ' + password);
    const socket = new SockJs('http://localhost:8080/football-ws');
    this.stompClient = Stomp.over(socket);
    this.stompClient.connect(user, password, this.onConnected);
    console.log('Attempting to connect!');
  }

  onConnected = (frame: any) => {
    console.log('Frame: ' + frame);
    this.stompClient.subscribe('/topic/public', this.onMessageReceived);
  }


  onMessageReceived(message: any) {
    console.log('Message Received from MS: '  +  message);
    try {
      this.messageSubject$.next(message.body);
    } catch (e) {
      console.log('Error: ' + e);
    }
  }

  getMessageSubject() {
    return this.messageSubject$.asObservable();
  }

I apologize if a question like this has been asked before, I did research a lot about my problem here however none of the changes the solutions suggested seem to work. Thank you all so much for your help, if any more code snippets or information is needed I'd be happy to provide them to you!


Solution

  • The problem will be caused by the scope of "this" being different in your callbacks. JavaScript has a concept of "this" which is unlike most other languages, however typescript (and modern Javascript) has another syntax to work around this, giving you a more intuitive behavior of "this".

    this.stompClient.connect(user, password, (frame: any) => this.onConnected(frame));
    

    .. and ...

    this.stompClient.subscribe('/topic/public', (message: any) => this.onMessageReceived(message));
    

    This syntax ensures that the value of "this" is as you would expect when you execute the callbacks.

    You can also put the callback inline using this syntax, for example:

    this.stompClient.connect(user, password, (frame: any) => {
        console.log('Frame: ' + frame);
        this.stompClient.subscribe('/topic/public', (message: any) => {
            console.log('Message Received from MS: '  +  message);
            try {
                this.messageSubject$.next(message.body);
            } catch (e) {
                console.log('Error: ' + e);
            }
        });
    });