Angular + Web Components

My article on Angular Elements - building Web Components with Angular. At my job, we iteratively turn an existing ASP.NET <VC app into a single page app by converting each page to a Web Component, later to be merged into an SPA. This article is about the challenges our team faced when implementing this solution.

1 Like

Hi Armen,

Did you see whether Ivy decreases the bundle size yet?

I’m actually still stuck on Angular 7. But after my task at hand (hopefully next week) I will get to upgrade to 8 and then 9 and try out Ivy. The bundle sizes right now… Well, they are not bad, but certainly suboptimal. For example, the web component that I make now is a huge page with forms and lots of interconnected components. It actually uses NGRX under the hood to handle permissions/configurations that are multiple, lots of ngif-s and so on. Bundle size after a production build stands firmly at 1mb. This is the best I could get after several days of optimizing. It’s not bad, but definitely would benefit from becoming smaller. So I hope Ivy fixes that

1 Like

I will run some experiments to create the smallest Elements bundle possible.

1 Like

That would be great, keep us posted on the results and how you managed!

1 Like

Try building web components with preact and preact-custom-elements, that would be quite interesting.

I’ve finished my first experiment.

33.5 KB gzipped (107 KB uncompressed).

Hello World Angular element without NgZone.

The browser platform, 1 element, 1 Angular module, 1 component.

Next up, renderComponent based minimal Angular element bundle.

Second experiment.

8.82 KB gzipped (24.5 KB uncompressed).

Hello World Angular element using renderComponent.

No NgZone, no platform, no Angular module, 1 element, 1 component.

Third experiment.

13.5 KB gzipped (40 KB uncompressed).

Zippy Angular element using renderComponent and a feature render module.

No NgZone, no platform, 1 Angular module, 1 element, 1 component, 1 directive, 1 pipe.

Note: Still need to implement Shadow DOM and content projection.

So I upgraded to ng9 today. I didn’t yet put out ngzone and the other stuff yet, so I actually have an increase in bundle size, almost 30%. But obviously that will change, so tomorrow I’m going to get rid of ngzone. @LayZee, thanks for your examples, I will definitely take a look at them

So here I try to remove ngmodules and zone.js, and hit a wall now:

forms.js:1067 Uncaught Error: Cannot inject Renderer2 when the application uses Renderer3!
at getOrCreateRenderer2 (forms.js:1067)
at injectRenderer2 (forms.js:1077)
at push…/…/node_modules/@angular/core/ivy_ngcc/fesm5/core.js.Renderer2.NG_ELEMENT_ID (primeng-button.js:61)
at getOrCreateInjectable (core.js:26019)
at Module.ɵɵdirectiveInject (forms.js:4714)
at NodeInjectorFactory.NgClass_Factory [as factory] (core.js:17044)
at getNodeInjectable (core.js:26211)
at instantiateAllDirectives (core.js:30663)
at createDirectivesInstances (core.js:30054)
at Module.ɵɵelementStart (forms.js:4860)

As you can see, this gives me an error related to a primeng component, but I am using primeng@9.0.0-rc4, which is (as they claim) Ivy compatible. And honestly I don;t understand how this injectRenderer2 got there at all. I don’t have that many dependencies (only ngrx and primeng), and I already took care of almost all of them, and this error just ruins it all.

It appears that the problems lies in the implementation of the NgClass directice (what a surprise!). Here is the line of code that causes trouble:
private _renderer: Renderer2
Well, NgClass uses Renderer2 (obviously), but somehow Ivy does not permit to use Renderer2 because it already uses Renderer3! Any ideas on How to bypass this problems?

There is no Renderer3 in the public API, only a new implementation. The interface is the same. Why do you think NgClass is broken? A lot changed with styles in v9. PrimeNG might have relied on old, broken behavior.

Fourth experiment.

21.1 KB gzipped (65.9 KB uncompressed).

Hello World Angular element using hacked createCustomElement without platform and NgZone.

1 element, 1 component.

Hey, actually it’s NgClass that is broken - here is a working reproduction. I know there is no Renderer3 in public API, but the injector somehow tries to pull Renderer2 into the app built with Ivy and throws an error (that is how I perceive it, might be completely wrong).

When we use renderComponent, we don’t have a platform such as the one usually created by platformBrowser in main.ts.

There are a lot of platform level dependencies we need to provide ourselves to be able to use the declarables exported by CommonModule and other platform level dependencies needed to run dependently and securely in the browser such as a Sanitizer.

We should pass a DomSanitizer to the sanitizer option of renderComponent. We need to provide platform level dependencies normally provided by platformBrowser, BrowserModule, and ApplicationModule.

Hi Armen,
Im trying to pass input to my webcomponent using @Input directive and using a typescript setter inside the component to detect the change and call the function. But Im having issues with IE. It is not detecting the input hence not calling the function inside the setter. I’ve added ElementZoneStrategy as well but still facing this issue(only in IE). Can you suggest some workaround for this to work in IE.

Hi, I would love to help, but I need more info. Does change detection not work at all? Or is the problem only with that input? What is the name of the input property? Is the input getter/setter?

Change detections are working fine. Only having problems with input directive. Working fine in all browsers except IE.
input name : ab2input

in component.ts
@Input(‘ab2input’) set ab2input(val:any){
//function call