lit / lit-element

LEGACY REPO. This repository is for maintenance of the legacy LitElement library. The LitElement base class is now part of the Lit library, which is developed in the lit monorepo.
https://lit-element.polymer-project.org
BSD 3-Clause "New" or "Revised" License
4.49k stars 318 forks source link

LitElement render starts after finishing life cycle events of Angular #1130

Closed msbasanth closed 3 years ago

msbasanth commented 3 years ago

Description

We are creating web-component based UI library for our customers. We were using Polymer library to create our web-components and they were working fine. Seeing the advantages and the control that Lit-Element provides to the component developer, we switched to LitElement.

We observed this issue with the Lit Element based webcomponent when used in an Angular component.

Angular-Lit-Angular

Figure 1: Angular->Lit->Angular

The LitElement render is called only after the ngAfterViewInit of both the parent and child Angular elements’ is called.

The result with one Polymer element between 2 Angular elements is as follows:

Angular-Polymer-Angular

Figure 2: Angular->Polymer->Angular

The result with the DOM Structure as Angular -> Polymer -> Angular -> Polymer -> Angular is as follows:

Angular-Polymer-Angular-Polymer-Angular

Figure 3: Angular->Polymer->Angular->Polymer->Angular

The result with the DOM structure as Angular->Lit->Angular->Lit->Angular is as follows:

Angular-Lit-Angular-Lit-Angular

Figure 4: Angular->Lit->Angular->Lit->Angular

  DOM Structure Observation
1 Angular->Lit->Angular Lit element render is called after ngAfterViewInit of both parent and child angular-elements
2 Angular->Polymer->Angular Render is called first before ngOninit of both parent and child angular components
3 Angular->Lit->Angular->Lit->Angular Both Lit elements’ render are called last after ngAfterViewInit of all angular components.
4 Angular->Polymer->Angular->Polymer->Angular Both Polymer elements get template() is called once(probably because they are the same element) before the ngOninit of other angular components

The problem we faced was this:

We have a similar structure as Angular->Lit->Angular as in Figure 1. We have written initialization logic in ngOninit of angular component 1 where we are trying to do some operation on the Lit component. But since the render of the Lit element has not been completed by that time as evidenced by Figure 1, it failed. But when we were using Polymer version of the same component, we didn’t face this issue as component was already rendered before ngOnInit.

What we tried?

Inside ngOnInit & ngAfterViewInit, we fetched the lit element and waited for the lit element’s updateComplete to finish. This helped in preserving the order of execution of functions (even though it didn’t block the normal execution of angular’s lifecycle events)

Angular-Update-Wait-Code

Angular-Update-Wait

So…is this best way to approach this scenario ?

Acceptance criteria

With the approach we tried the component consumer should handle the lit component differently.

How can we treat LitElement based webcomponents just like an angular component without such special handling for rendering? Please let us know if there is any better way other than using updateComplete to handle situation described above.

sorvell commented 3 years ago

Thanks for the detailed information. We acknowledge that Lit's asynchronous rendering model can, hopefully only in rare cases, require special handling like this (via awaiting updateComplete). We use async rendering for efficiency, to ensure that property sets are batched and not every set causes an update/render. What you've done with updateComplete is our recommended approach for addressing this.

To do better, you could potentially handle this in your LitElement. The API you're calling from your Angular component could be implemented to call performUpdate(). In LitElement any pending update can be "flushed" by calling performUpdate(). So it would be something like:

whatIsTheThing() {
  this.performUpdate();
  // presumably this property is somehow dependent on having updated
  return this.theThing;
}

and from Angular:

const value = element.whatIsTheThing();

It's important to note that calling performUpdate() causes an update only on that element and not any sub-elements it renders. We've added an issue to potentially add an API to improve that in the future.

Hope that helps.

sorvell commented 3 years ago

Closing based on the previous response. Feel free to re-open the issue if further discussion is necessary. Thanks.