10

I have an application that is listening for incoming data from an IPC Renderer Channel. Here is my setup:

container that sends data to angular app (mainWindow):

mainWindow.loadURL('http://www.myangularapp.com') //where the angular app lives (example url).
mainWindow.webContents.on('did-finish-load', () => {
      const data = { name: "John Doe", address: "123 Main St", city: "NY"}
      mainWindow.webContents.send('data-from-container', data)
        }
    })

angular app:

constructor(
    private store: Store<fromStore.AppState>,
    private cd: ChangeDetectorRef,
    private zone: NgZone,
  ) {}

  ngOnInit() {
this.isLoading = true;
    if (this.electronService.ipcRenderer) {
      this.electronService.ipcRenderer.on('data-from-container', (event, data) => {
        this.zone.run(() => {
          if(data){
            setTimeout(() => {
              console.log(data) // verified data is always received
              this.formData = data; // property that form uses to populate data in the dom
              this.prepopulateForm(data) // method that places incoming data in ngrx store
            }, 1000)
          }
        })
      })
    }

    this.store.select('containerData').subscribe(data => {
        setTimeout(()=> {
          console.log(data) // data is always being set in ngrx
          this.isLoading = false
        }, 5000);
    })

  }

Everytime the IPC Channel emits the 'data-from-container' event, the data is always getting received from my OnInit call, but the data doesn't always get set in my form! The pattern that i've noticed is typically that the data does not prepopulate the form when the angular app first launches inside the container, after the initial launch, every subsequent time the app is launched, the data appears.

I've tried using ngZone, setTimeout, and detectChange methods to trigger Change Detection so the Angular App can detect the newly set formData, but its not consistently prepopulating the form. Any suggestions on how to fix this?

Kode_12
  • 4,506
  • 11
  • 47
  • 97
  • 1
    Try: `if (data) { setTimeout(() => { this.zone.run(() => { this.formData = data; ... }); }, 1000); }`. – ConnorsFan Jul 16 '19 at 00:05
  • still same result :/ – Kode_12 Jul 16 '19 at 18:37
  • 2
    why do you use `setTimeout` inside `zone.run` ? – Nidhin Joseph Jul 18 '19 at 21:59
  • trial and error, ive tried without the setTimeout as well – Kode_12 Jul 18 '19 at 22:01
  • can you confirm the first event 'data-from-container' is triggered after the component is initialized? and what do you mean with 'the angular app first launches'? the 1st app load? – JavierFromMadrid Jul 18 '19 at 22:16
  • Correct when the app first loads. Ive added additional code to show when data is sent from the container – Kode_12 Jul 18 '19 at 22:49
  • could you try the web-contents event 'dom-ready' instead of 'did-finish-load' to launch the 1st data through 'data-from-container' event? – JavierFromMadrid Jul 18 '19 at 23:39
  • 1
    Could you show all the code, which is relevant to your form and data binding? – Valeriy Katkov Jul 19 '19 at 14:33
  • 2
    If you can create a git repo with minimal reproducible code, i am sure it would be easy to provide a solution – Tarun Lalwani Jul 19 '19 at 20:35
  • Please provide more details in your question. For instance, you said you tried ````detectChanges```` but there seems to be no mention here that the ````ChangeDetectionStrategy```` you have used is ````OnPush````. If you are using ````OnPush````, you will need to trigger changeDetection manually or assign ````formData```` immutably. I'm not sure why you are using ngZone when you don't seem to be running anything outside Angular. It would be easier to help if you provided the entire component and template or even better, a StackBlitz example. – nash11 Jul 23 '19 at 09:56
  • @Kode_12 i think your view is not completely created so that the first element is logged but not shown ! For that you have to wait until the view is initialized without loosing your first emission. Somthing like `combineLatest('data-from-container', viewInitialized)` – bubbles Jul 23 '19 at 12:36
  • @bubbles can you provide that as an answer so I can test this out? – Kode_12 Jul 23 '19 at 20:23

1 Answers1

3

I have a very basic knowledge of electron, so i'll try to show you the idea. For me, your problem comes from the initialization of the view. You're not loosing events because you can see them in the console but not in the view, which enforces my guesses.

As shown in your code, your are sending only one event (I suppose it's only for testing raison) and we want to show it when the view is rendered.

In your component add a subject which informes us that the view is initialized, like:

import { Subject, combineLatest, fromEvent } from 'rxjs';

viewInitialized$ = new Subject();

...

ngAfterViewInit() {
 this.viewInitialized$.next();
}

...

Now we can wait the two emissions to come, one from the ipcRenderer and the other from viewInitialized$by using combineLatest operator.

Before that, we have to convert the ipcRenderer to an Observable. From this SO response we can do fromEvent(ipcRenderer,'data-from-container'). If it does not work we can use another subject that emits events each time we receive something in ipcRenderer.on(), the second solution requires ngZone.


ngOnInit() {
...

const containerData$ = fromEvent(this.electronService.ipcRenderer, 'data-from-container');
this.subscription = combineLatest(containerData$, this.viewInitialized$).subscribe(combined => {
  const data = combined[0];
  this.formData = data;
  this.prepopulateForm(data) 
})

...
}

ngOnDestroy() {
  this.subscription.unsubscribe();
}

Hope this helps.

bubbles
  • 2,597
  • 1
  • 15
  • 40