Kevin Boswell Kevin Boswell -3 years ago 152
Javascript Question

Assigning Angular2 method to JS MediaRecorder event ondataavailable loses scope of Angular2 component variables

I am trying convert this webRTC example https://webrtc.github.io/samples/src/content/getusermedia/record/
to run in my Angular2 app. The source is also on github here https://github.com/webrtc/samples/tree/gh-pages/src/content/getusermedia/record.

It consist of a 1 simple html page with 1 vanilla

main.JS
file that contains all the code. I tried to create a plnkr demo but it wouldn't support the
navigator.GetUserMedia
call so I'll do my best to explain below:

So in my attempt to convert this into an Angular2 component. I have the following:

import {Component, OnInit} from "@angular/core"

@Component({
selector: "my-web-rtc-recording",
templateUrl: 'app/test.web.rtc.recording.component.html'
})
export class TestWebRtcRecordingComponent implements OnInit {

// -- private variables --
startStop: string = "Start";
mediaSource: MediaSource = new MediaSource();
mediaRecorder: MediaRecorder;
recordedBlobs: Blob[]; **// <<== This is the variable in question**
sourceBuffer: any;

gumVideo: HTMLVideoElement;
recordedVideo: HTMLVideoElement;
recordButton: HTMLButtonElement;
playButton: HTMLButtonElement;
downloadButton: HTMLButtonElement;


Notice the private variable
recordedBlobs
. This will get set in the
MediaRecorder
event
ondataavailable
event that I must subscribe to. In lies the problem, I am able to subscribe to the event but my component private variables are out of scope?

Once I grab the video stream I do the following:

this.recordedBlobs = [];
this.mediaRecorder = new MediaRecorder(window.stream);


Then I assign an Angular method to the event.

this.mediaRecorder.ondataavailable = this.handleDataAvailable;


I'm not sure this is the right syntax but the event does fire and the data is sent correctly.

handleDataAvailable(event) {
// debugger;

if (event.data && event.data.size > 0) {
// Note: It is undefined here because it is a different scope?
// I'm trying to figure this out why these are 2 sep variables
// My hunch is they are being updated in different processes
if (this.recordedBlobs == undefined)
{
console.log('this.recordedBlobs is undefined');
this.recordedBlobs = [];
}
// Add each recorded data event to our array of blobs
if (event.data != undefined)
this.recordedBlobs.push(event.data);
console.log('Recorded Blob Count = ', this.recordedBlobs.length);
}
}


So the problem is that my reference to
this.recordedBlobs
is no longer a reference to my component private variable.

In fact this references the
mediaRecorder
object and not my component which is why I had to check for undefined and initialize it. So what am I missing here.

Is this firing in a different Zone or thread and I am unable to access my Angular2 component scope variables? I must be able to set a variable so when the recording is done I can refer to the
recordedBlobs
. Since this is not a pre-defined DOM event like
onclick()
do I have to do something extra for Angular to handle it?

Any ideas on what else to try would be appreciated....

Kevin

Answer Source

I finally solved it...after several days of digging through docs and looking at multiple sample projects...I was proceeding to write a JS wrapper around the MediaRecorder object to try and expose a property when I saw an example of a JS property being added on-the-fly and it dawned on me, that might do it!

So it is a "scope" issue, when the mediaRecord event is fired and my local assigned Angular "handleDataAvailable" method is called the scope of the "this" variable has changed to the MediaRecorder object and is no longer referencing the Angular component. Therefore the component variables are out of scope. I still find it odd that Angular doesn't provide a way to provide access to you're private component variables in the event handler code since it's defined in the component.

But it turns out the fix is simple and I can just add a property on-the-fly to the MediaRecord object and then reference it in both places.

this.mediaRecorder = new MediaRecorder(window.stream);
this.mediaRecord.blobs = []; <== This adds a new .blobs property on-the-fly

I then referenced this new mediaRecorder.blobs property in the event handler code.

// Add each recorded data event to our array of blobs    
if (event.data != undefined)
    this.blobs.push(event.data); <== Now referencing the new .blobs property I added.

And when I return from the handler, I can reference the this.mediaRecorder.blobs property and it has the data assigned in the event handler.

So now I have a property in scope in both the component and the event handler, which is what I needed. Issue solved, on to the next challenge!

Kevin

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download