primeng icon indicating copy to clipboard operation
primeng copied to clipboard

ViewEncapsulation.ShadowDom breaks components

Open irekBudzowski opened this issue 4 years ago • 27 comments

I'm submitting a ... (check one with "x")

[x] bug report => Search github for a similar issue or PR before submitting
[ ] feature request => Please check if request is not on the roadmap already https://github.com/primefaces/primeng/wiki/Roadmap
[ ] support request => Please do not submit support request here, instead see http://forum.primefaces.org/viewforum.php?f=35

Plunkr Case (Bug Reports) https://stackblitz.com/edit/primeng-autocomplete-demo-aha9nr

Current behavior Enabling ViewEncapsulation.ShadowDom in Angular's component, embedded prime's component doesn't work.

Expected behavior

ViewEncapsulation.ShadowDom in Angular's component should not break embedded prime component.

Minimal reproduction of the problem with instructions

Use ViewEncapsulation.ShadowDom in components with embedded prime's component.

What is the motivation / use case for changing the behavior?

Possibility to use the library in microfrontends, where using ShadowDom is crucial.

  • Angular version: 5.X

Angular version 10.X

  • PrimeNG version: 5.X

PrimeNg version 10.X

irekBudzowski avatar Oct 29 '20 12:10 irekBudzowski

Same problem here. We want to use PrimeNG components inside of web components and the ViewEncapsulation.ShadowDom indeed breaks most components. I just had a deeper look into the calendar. Two functions of the calendar are problematic:

  • Calendar::bindDocumentClickListener: Clicks caught outside of the shadow dom always have the shadow root as event target to hide implementation details -> All clicks are considered to be outside of the calendar.
  • Calendar::bindScrollListener: DomHandler::getScrollableParents fails executing getComputedStyle on the shadow root.

The only solution for me right now would be to fork PrimeNG and fix it or create a directive that overrides those functions of the component.

AKoempel avatar Nov 10 '20 12:11 AKoempel

Thank you @AKoempel. I will try to solve that with directive, as you proposed. Also with some components, I will migrate to material, they updated js recently and it works like a harm! Unless, of course, PrimeFaces will upgrade js in the library too.

irekBudzowski avatar Nov 10 '20 17:11 irekBudzowski

We're also using ViewEncapsulation.ShadowDom and, in our case, it was the behavior of any overlay panel. It appeared and disapeared right away when the user clicked on the component to show (e.g. a user clicks in a multiselect, options appear and disapear very quickly). As @AKoempel said, we ended up overidding "isOutsideClicked(event)" function in primeng-multiselect.js and primeng-dropdown.js

TiagoM-Leite avatar Feb 08 '21 09:02 TiagoM-Leite

Also, since v. 11.2.1, it's breaking with the following error: "Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'"

The root cause seems to be this commit: https://github.com/primefaces/primeng/commit/297beb06e8fc6ac525feab573bcbcf7bab52be16

For now, we're leaving on v. 11.2.0 (on which we had to create directives to override problematic functions in the component).

We're also planning to leave primeng in the near future if these issues persist, since we have to use shaddow dom and clearly primeng isn't compatible with it.

TiagoM-Leite avatar Feb 22 '21 10:02 TiagoM-Leite

Is this still an issue?

raymatos avatar Jul 15 '21 15:07 raymatos

@raymatos yes, it's still an issue, I'm having the issue for all components has something "dropdown": Dropdown, Calendar, tooltip,...

maitrungduc1410 avatar Jul 22 '21 04:07 maitrungduc1410

Here's what I found (with solution below): the issue caused by this line:

https://github.com/primefaces/primeng/blob/master/src/app/components/dom/domhandler.ts#L175

PrimeNg uses window['getComputedStyle'](node, null), and in the case of ShadowDOM it'll throw error because node (which is passed to overflowCheck) is not normal HTML Element

I manage to solve this issue, not much effort:

// from this
let parents = this.getParents(element);

// to this
let parents = this.getParents(element).filter(item => !(item instanceof ShadowRoot));

Explain: we filter out the shadow root when getting parents of element, so later params which is passed to overflowCheck will always be an HTML Element

  • Step 3: create a directive to override a behaviour of PrimeNG:
@Directive({
  selector: '[bindScrollListener]',
})
export class bindScrollListenerDirective {
  constructor(
    @Host() @Self() @Optional() public hostSel: Dropdown) {

    // Nothing is changed here, I just simply use ConnectedOverlayScrollHandler from the 2 files we downloaded
    hostSel.bindScrollListener = () => {
      if (!hostSel.scrollHandler) {
        hostSel.scrollHandler = new ConnectedOverlayScrollHandler(hostSel.containerViewChild.nativeElement, (event: any) => {
          if (hostSel.overlayVisible) {
            hostSel.hide();
          }
        });
      }

      hostSel.scrollHandler.bindScrollListener();
    }

    // override PrimeNG to make it support ShadowDOM
    hostSel.isOutsideClicked = (event: any) => {
      const target = event.target.shadowRoot ? event.path[0] : event.target
      return !(this.hostSel.el.nativeElement.isSameNode(target) || this.hostSel.el.nativeElement.contains(target) || (this.hostSel.overlay && this.hostSel.overlay.contains(<Node> target)));
    }
  }
}

Finally register the directive in declarations in app.module and start using your component as usual:

<p-dropdown bindScrollListener ....>

What I actually changed:

  • Filter out Shadow Root element when getting parents of an element: https://github.com/primefaces/primeng/blob/master/src/app/components/dom/domhandler.ts#L172
  • Make component isOutsideClicked supports ShadowDOM:
hostSel.isOutsideClicked = (event: any) => {
  const target = event.target.shadowRoot ? event.path[0] : event.target
  return !(this.hostSel.el.nativeElement.isSameNode(target) || this.hostSel.el.nativeElement.contains(target) || (this.hostSel.overlay && this.hostSel.overlay.contains(<Node> target)));
}

P/s: for each component that has this issue you need to create a directive for it (above I use dropdown)


Hi @cagataycivici, I don't know if you aware of this issue, but if this one is fixed it'll make PrimeNg much better cuz it can run in all kind of encapsulation environments that Angular has. Also Shadow DOM is a common case when developers want to embed their Angular components into any other web app.

If my solution is valid, I'd happy to create a PR for that

maitrungduc1410 avatar Jul 23 '21 10:07 maitrungduc1410

Hi @yigitfindikli If you ever have time please help to look at this issue. Thank you

maitrungduc1410 avatar Jul 26 '21 09:07 maitrungduc1410

For anyone is having this issue, I've published a small lib with directives that fix this problem: https://github.com/maitrungduc1410/primeng-shadowdom-directives

maitrungduc1410 avatar Sep 21 '21 15:09 maitrungduc1410

For anyone is having this issue, I've published a small lib with directives that fix this problem: https://github.com/maitrungduc1410/primeng-shadowdom-directives

Hi @maitrungduc1410, thank you, I was waiting for some update here, I have a problem with filters in datatable, the <p-columnFilter "></p-columnFilter> element. In the library that you provided is there any solution for this element?

ContrRus avatar Sep 22 '21 05:09 ContrRus

For anyone is having this issue, I've published a small lib with directives that fix this problem: https://github.com/maitrungduc1410/primeng-shadowdom-directives

Hi @maitrungduc1410, thank you, I was waiting for some update here, I have a problem with filters in datatable, the <p-columnFilter "> element. In the library that you provided is there any solution for this element?

It's PrimeNg table right?

maitrungduc1410 avatar Sep 22 '21 05:09 maitrungduc1410

For anyone is having this issue, I've published a small lib with directives that fix this problem: https://github.com/maitrungduc1410/primeng-shadowdom-directives

Hi @maitrungduc1410, thank you, I was waiting for some update here, I have a problem with filters in datatable, the <p-columnFilter "> element. In the library that you provided is there any solution for this element?

It's PrimeNg table right?

Yeah of course, below is my question for this issue on stackoverflow https://stackoverflow.com/questions/69117441/viewencapsulation-shadowdom-and-primeng-not-all-styles-are-presented

ContrRus avatar Sep 22 '21 05:09 ContrRus

okay I'll update to support for that too (if it's possible :D)

maitrungduc1410 avatar Sep 22 '21 05:09 maitrungduc1410

Hi @ContrRus , I just looked into the question you posted on StackOverflow, it seems not related to this Github issue, I posted my answer for your question

maitrungduc1410 avatar Sep 22 '21 06:09 maitrungduc1410

Hello Team,

@yigitfindikli , Would be really great if we could get the fix for this in upcoming primeng version. I think it is quite relevant now as we use micro front end architecture and we may need to integrate the angular micro front ends to existing web application as remote modules with shadow DOM encapsulation

Vishnuram1988 avatar Oct 03 '21 16:10 Vishnuram1988

@yigitfindikli

the issue opened at Oct 29, 2020, more than a year ago, is there any ETA ?

shahafm-netapp avatar Nov 03 '21 07:11 shahafm-netapp

Having the same problem in our team's project. Really waiting for this fix

dmluzhin avatar Nov 12 '21 10:11 dmluzhin

@maitrungduc1410, thanks for the directives. Is it possible to add one for ConfirmPopup ? I tried based on psdTooltipDirective but didn't work. Thank you

vanroda avatar Nov 15 '21 11:11 vanroda

@vanroda okay, I'll check and let you know

maitrungduc1410 avatar Nov 15 '21 15:11 maitrungduc1410

Hi @vanroda, please upgrade to version 0.0.5, I added support for confirm popup component

maitrungduc1410 avatar Nov 16 '21 03:11 maitrungduc1410

I really wish this would be fixed in the newer version of primeng. It's been over a year. I too have my components break once I switch to ViewEncapsaulation.ShadowDom. In the meantime, thank you @maitrungduc1410 for providing us some type of workaround. Could you tell me if your lib of directives is going to get DataView? https://www.primefaces.org/primeng/showcase/#/dataview Our grid layout breaks and displays our items vertically. Also, the p-paginator that comes as part of the DataView also breaks, we lose the p-paginator-first, p-paginator-prev, p-paginator-next, and p-paginator-last arrows.

leopardy avatar Dec 10 '21 18:12 leopardy

@leopardy this Github issue only happens to "overlay" components, in your case there's no such component usage in DataView, I did check, and it works fine inside ShadowDOM. I think you probably import css incorrectly

Remember that when working with ShadowDOM, global style defined in styles.css won't work. Also beware of using :ng-deep it doesn't work well with ShadowDOM, don't use :ng-deep in this case.

The way it works is you have to place css import in any component which is ShadowDOM encapsulated. Example:


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: [
    './app.component.scss',

    // ---------------- IMPORT PRIMENG STYLE HERE
    "../../../../node_modules/primeicons/primeicons.css",
    "../../../../node_modules/primeng/resources/themes/saga-blue/theme.css",
    "../../../../node_modules/primeng/resources/primeng.min.css"
  ],
  encapsulation: ViewEncapsulation.ShadowDom
})
export class AppComponent {

maitrungduc1410 avatar Dec 11 '21 10:12 maitrungduc1410

Answer removed.

Cichy3D avatar Apr 25 '22 14:04 Cichy3D

Still have the same issue within the webcomponents? @cagataycivici

cmyk1 avatar Jun 09 '22 15:06 cmyk1

Still not working in shadow dom. >﹏<

drekthral avatar Aug 02 '22 15:08 drekthral

Sorry right now, ShadowDOM is not supported and I'm not sure how it can be right now. Any ideas are appreciated considering the current design agnostic theming approach of PrimeNG.

For web components though, we'll create PrimeElements aka PrimeUI with lit or similar which will have a styling architecture suited for WC. PrimeNG supports NONE and EMULATED for now.

cagataycivici avatar Aug 02 '22 16:08 cagataycivici

Sorry right now, ShadowDOM is not supported and I'm not sure how it can be right now. Any ideas are appreciated considering the current design agnostic theming approach of PrimeNG.

For web components though, we'll create PrimeElements aka PrimeUI with lit or similar which will have a styling architecture suited for WC. PrimeNG supports NONE and EMULATED for now.

Any rough estimate when PrimeElements could be created?

drekthral avatar Aug 03 '22 11:08 drekthral

Any ideas are appreciated considering the current design agnostic theming approach of PrimeNG.

@cagataycivici What do you think of the approach by @maitrungduc1410 ?

If my solution is valid, I'd happy to create a PR for that

As I understand, the root problem is with "overlay" behaviors of components. Other than that, simply using styleUrls in the component that has encapsulation: ViewEncapsulation.ShadowDom seems to be enough. However, of course overlays are a big part of primeng, so that needs fixing too, and that's what @maitrungduc1410 has done.

A quick guess from me (I haven't really looked well into how @maitrungduc1410 did it): solving this is probably just a matter of being careful to create all overlay elements also inside the shadow root, rather than appending document.body directly.

Thank you all!! This package is awesome :rocket:

papb avatar Aug 18 '22 00:08 papb

For my case, the following patch seems to be enough for now:

import { DomHandler } from 'primeng/dom';

// Patch it to use `.parentElement` instead of `.parentNode`.
// See https://github.com/primefaces/primeng/issues/9471
// See https://github.com/primefaces/primeng/blob/ddcc166fe48d336012f2dfa9a2deb6af08f98593/src/app/components/dom/domhandler.ts#L164-L166
DomHandler.getParents = function(element, parents = []) {
  const parent = element.parentElement;
  return parent ? this.getParents(parent, [...parents, parent]) : parents;
};

Many thanks to @maitrungduc1410!

papb avatar Aug 18 '22 22:08 papb

the solution proposed seems good, can you apply it? can you share the ETA?

fabiocastagnino avatar Oct 11 '22 16:10 fabiocastagnino