angularobservablewebusb

Angular: Convert a service function into an observable


Having spent some time trying to get my head around using observable's on service functions, I have decided to ask the question on here.

I have a service which is designed around WebUSB in order to make a connection to a device attached to the computer within the browser. The code works but it needs to be better, in that when it calls the function to connect, since it is a pure function, the code continues in the background within the controller regardless of whether or not a device is actually selected.

webSerialService.ts

// Grab and assign a port to the variables this.device.
requestPorts() {

    navigator.serial.requestPort(this.filters)
        .then(
            (result) => {

                this.device = result;

                // Open up the port.
                this.device.open({ baudRate: 9600 });
            },
            (error) => {
                console.log("The user selected the cancel, no port was selected.");
            }
        );

    return this.device;
}

And the receiving controller code:

public connect() {

    // Let the user know they are about to do something.
    this.displayText = "-----";

    try {

        // Return the device selected.
        this.device = this.webSerialService.requestPorts();

    } catch (e) {

        console.log(e);
    }

    // Have we selected a port or not?
    if (this.webSerialService.device == false) {

        // Let the user know that no port was selected.
        this.displayText = "NO PORT";
    }
    else {
        // Let the user know they are connected.
        this.displayText = "Awaiting Data";
    }
}

The second part of the controller code, obviously executes without waiting for a device to be selected, which is not what I want, but expected. Having tried to turn the service function into an observable and then subscribing to it within the controller, I would get the error message when the "connect" function is fired:

TypeError: Cannot read properties of undefined (reading 'subscribe')

I amended the function definition within the server to:

requestPorts(): Observable<any> {

And then the controller code would be:

this.webSerialService.requestPorts().subscribe(data => {console.log(data)});

Which is where the aforementioned error would occur.

I've tried several various examples found on the internet and on StackOverflow, but I seem to be missing a crucial bit of understanding how Observables work and how to define them for functions.

Thank-you for any input and insight you can provide.


Solution

  • The problem is not in the Observable itself. The problem is that the moment you return this.device, it hasn't been assigned a value yet. It's undefined. Hence the error.

    Try converting the Promise into an Observable like this instead:

    import { from, map } from 'rxjs';
    
    // ...
    
    requestPorts(): Observable<any> {
       return from(navigator.serial.requestPort(this.filters)).pipe(
          map((result) => {
             this.device = result;
             this.device.open({ baudRate: 9600 });
             return this.device;
          })
       );  
    }