question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

@angular/elements + zone.js change detection related to RxJs stream runs in wrong zone

See original GitHub issue

Reproduction

You can get running example of the last solution using this one liner git clone https://github.com/tomastrajan/angular-elements-cd-example.git && cd angular-elements-cd-example && npm i && npm start This will be working, it can be broken by removing { ngZone: (window as any).ngZone } from elements main.ts file (as per description)

Description

Basically it’s about using @angular/elements inside of the Angular application. It all started with weird behavior in change detection but ONLY in rxjs observable streams…

The stream in element did not trigger change detection…

Example, we have an @Input() username and we display it inside of element but also use it to load repositories from github API, the change to username will:

  1. trigger component re-render ( and display actual username in template )
  2. trigger ngOnChanges
  3. trigger backend request as a part of observable stream ( also receive data)

but the change will NOT:

  1. trigger re-render as a result of received data from backend

Causes

It seems to be the case when current zone is logged out inside of the rxjs steam it is the parent Angular app zone instead of the element zone…

Explored solution 1: zone.js/dist/zone-patch-rxjs

I tried to solve it in various ways, the most promising was to add import 'zone.js/dist/zone-patch-rxjs'; to the element BUT…

This leads to second more serious problem …

If the consumer SPA or ( any of it libs ) already uses import 'zone.js/dist/zone-patch-rxjs'; then any subsequent call of import 'zone.js/dist/zone-patch-rxjs'; which calls Zone.__load_patch('rxjs', handler ...) will be ignored because in zone.js there is

static __load_patch(name, fn) {
            if (patches.hasOwnProperty(name)) {                    // only first rxjs patch will be applied
              if (checkDuplicate) {
                    throw Error('Already loaded patch: ' + name);   // this will not happen because checkDuplicate = false by default
                }
            }
            else if (!global['__Zone_disable_' + name]) {
                const perfName = 'Zone:' + name;
                mark(perfName);
                patches[name] = fn(global, Zone, _api);             // patch rxjs
                performanceMeasure(perfName, perfName);
            }
        }

so then if element comes with it’s own rxjs in its bundle and tries to import 'zone.js/dist/zone-patch-rxjs'; patch it, it will be ignored because zone already has rxjs patch…

Explored solution 2: pass parent Angualr app NgZone into the element

In terms of solutions what we do now is

In element:

  1. do not import any zone or any patch
  2. use .bootstrapModule(AppModule, { ngZone: (window as any).ngZone })

In consumer SPA: 1.

export class AppModule {
  constructor(private ngZone: NgZone) {
    (window as any).ngZone = this.ngZone // store reference on window to be used by element during its bootstrap
  }
}

The solution seems pretty dirty, would be curious about why we can’t patch more than one rxjs and in what direction will the element go and if anybody ever struggled with this ?

Reproduction (copy)

You can get running example of the last solution using this one liner git clone https://github.com/tomastrajan/angular-elements-cd-example.git && cd angular-elements-cd-example && npm i && npm start

The error behavior (when rxjs does NOT trigger change detection after receiving of data) we can simply remove { ngZone: (window as any).ngZone } from the element main.ts file

Questions

  1. why is rxjs broken at all ? (everything else seems to be working)
  2. why we’re not allowed to zone-patch-rxjs for multiple rxjs bundles in the single page ?
  3. which solution makes most sense based on the roadmap of Angular and elements?
  4. what can go wrong when sharing parent ANgular NgZone instance with the child element?
  5. are the other solutions ( which does NOT involve manual CD because we need to convert existing Angular apps into elements without major rewrite)?

cc: @JiaLiPassion @robwormald @manfredsteyer

Versions

Angular / CLI / elements 8

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Reactions:9
  • Comments:13 (12 by maintainers)

github_iconTop GitHub Comments

1reaction
tomastrajancommented, Aug 7, 2019

Hey @JiaLiPassion , I debugged it further and it turned out to be something else, basically a stream was subscribed in runOutsideAngular and this made then any subsequent subscription to a other stream which was part of the first one to also run in <root> zone.

The solution was to NOT subscribe in runOutsideAngular so that the sub-stream is not pulled out and then everything works as expected.

1reaction
JiaLiPassioncommented, Jul 29, 2019

@tomastrajan, thank you very much for creating the documentation, and I will try to find out an elegant solution for this issue. Have a nice day 😄

Read more comments on GitHub >

github_iconTop Results From Across the Web

NgZone - Angular
A zone is an execution context that persists across async tasks. ... it is important to have a clear grasp of what change...
Read more >
Angular Change Detection & Zone.js Deep Dive - Beyond Java
The most popular error is that change detection runs too often, slowing down your application. But that's not that bad.
Read more >
Reactive Angular with ngrx/component - Chris Kohler
What's wrong with Zone.js? When should we run change detection? Zone less approach in Angular; Change detection in a reactive ...
Read more >
Binding RxJS Observable Sources Outside Of The NgZone In ...
As it turns out, RxJS is not entirely compatible with Zone.js, which is what Angular uses to manage its change-detection algorithm. This means ......
Read more >
The Best Way To Lazy Load Angular Elements - Tomas Trajan
Web components API is closely related to the native HTML elements and the DOM ... update element view… rxjs might run in a...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found