Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using ipcRenderer in angular 2; trigger change detection

I am building an electron app with angular 2 in the renderer process. My main process talks to a socket server. Whenever the user is connected to this server or disconnected, I wish to show the user's status in the view.

For this, I use electron's ipc to send a message from the main to the renderer process, like this

socket.on('connect', function() {
  mainWindow.webContents.send('socket-connection-status', true);
});
socket.on('disconnect', function() {
  mainWindow.webContents.send('socket-connection-status', false);
});

In my view, I then have a (simplified) angular component, like this

const ipc = require('electron').ipcRenderer;
@Component({
  selector: 'status-bar',
  template: '{{status}}'
})
export class StatusBarComponent {
  private status: string = "offline";
  constructor() {
    ipc.on('socket-connection-status', function(event, status) {
      if (status===true) {
        this.status = 'online';
      } else {
        this.status = 'offline';
      }
      console.log(status);  // Successfully logs true|false
      console.log(this.status);  // Successfully logs online|offline
    })
  }
}

I successfully log the messages from main process.

The problem is that angular 2 does not 'know' electron's ipc, so change detection is not triggered for status. I have seen several people struggling with this issue, but haven't come across a 'true' solution.

I have tried to solve it with injecting ApplicationRef, ChangeDetectorRef and ngZone (ref: Triggering Angular2 change detection manually), but none of the methods provided (tick(), detectChanges(), run() respectively) happened to provide a solution.

Apparently, 'within' ipc.on I cannot reference my class' properties/methods/injectables as I run into errors: For instance, this (https://github.com/JGantner/angular2_change_detection_issue/blob/master/browser/security-level-indicator-component.ts) solution (which I find not very elegant) results in Uncaught TypeError: Cannot read property 'markForCheck' of undefined.

Could somebody please help me out with how to make change detection work in my case?


Edit (hack):

One way I have found in which I at least get the functionality that I need/want:

status-bar.component.ts:

const ipc = require('electron').ipcRenderer;
import { SocketStatusService } from '../services/socket-status.service';
@Component({
  selector: 'status-bar',
  template: '{{status}}'
})
export class StatusBarComponent {
  private status: string = "offline";
  status$: Subscription;
  constructor(private socketStatusService: SocketStatusService, private ref: ApplicationRef) {
  ipc.on('socket-connection-status', function(evt, status) {
    if (status===true) {
      this.service.updateSocketStatus('online');
    } else {
      this.service.updateSocketStatus('offline');
    }
  }.bind({service: socketStatusService}))

  this.status$ = this.socketStatusService.socket_status$.subscribe(
    status => {
      this.status = status;
      this.ref.tick();
    }
  )
}

socket-status.service.ts:

@Injectable()
export class SocketStatusService {
  private socket_status = new BehaviorSubject<string>("offline");
  socket_status$ = this.socket_status.asObservable();

  updateSocketStatus(status: string) { 
    this.socket_status.next(status);
  }
}

Although this works, I have the feeling there must be a more elegant way to achieve this behavior.

Best case scenario though would be a means to set the component's class properties directly in the ipc callback and trigger change detection... So far I have not been able to get that to work, so any help would be appreciated.

(p.s. Also, I am not sure why I have to manually trigger this.ref.tick(), which is not something I remember having had to do to trigger change detection from streams in earlier beta versions of angular 2...)

like image 432
Willem van Gerven Avatar asked Oct 30 '16 16:10

Willem van Gerven


1 Answers

this.status is set on wrong object, and markForCheck doesn't work because of this too. Event handler should be an arrow in order for proper context to be provided.

With proper this context any known way to trigger change detection should work.

For example

ipc.on('...', (evt, status) => {
  ...
  this.changeDetectorRef.detectChanges();
});

Or

ipc.on('...', (evt, status) => {
  setTimeout(() => {
    ...
  });
});
like image 82
Estus Flask Avatar answered Nov 15 '22 05:11

Estus Flask