primeng
primeng copied to clipboard
ViewEncapsulation.ShadowDom breaks components
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
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.
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.
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
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.
Is this still an issue?
@raymatos yes, it's still an issue, I'm having the issue for all components has something "dropdown": Dropdown, Calendar, tooltip,...
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:
- Step 1: Download these 2 files from PrimeNg to your project: domhandler.ts and connectedoverlayscrollhandler.ts
- Step 2: update
domhandler.ts
, line 172:
// 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
Hi @yigitfindikli If you ever have time please help to look at this issue. Thank you
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
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?
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?
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
okay I'll update to support for that too (if it's possible :D)
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
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
@yigitfindikli
the issue opened at Oct 29, 2020, more than a year ago, is there any ETA ?
Having the same problem in our team's project. Really waiting for this fix
@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 okay, I'll check and let you know
Hi @vanroda, please upgrade to version 0.0.5
, I added support for confirm popup component
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 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 {
Answer removed.
Still have the same issue within the webcomponents? @cagataycivici
Still not working in shadow dom. >﹏<
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.
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?
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:
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!
the solution proposed seems good, can you apply it? can you share the ETA?