Hello!
I am currently trying to replace the TimeSlider Widget with the new arcgis-time-slider web component within my Angular project. (Docs)
For the widget, we attached popovers to the start time and end time divs by searching them in the DOM via their classes (".esri-time-slider__min").
I know that I now need to access the ShadowDom of the Timeslider component to find these divs, but I struggle to find the correct lifecycle method to attach this script. I tried the "arcgisReady" event of the TimeSlider and also waiting for "componentOnReady()" within "ngAfterViewInit", but the ShadowDom is never fully rendered (empty div) and the TimeSlider appears empty in the UI at that point of time using browser break points (exept for saying "No Time Extent"). I even tried to use "afterEveryRender" and "arcgisPropertyChange" but even if the state of the TimeSlider is "ready" the ShadowDom is still empty.
I tried to set the time extent two different ways, both of them resulted in the same issue as described:
1. Setting the time extent in "ngAfterViewInit", where I access the TimeSlider component to set the view and to apply settings (like initial playRate and fullTimeExtent)
2. Setting the time extent via "[timeExtent]="timeExtent()"" in the Angular template to bind the time extent directly to a time extent signal which either is computed to the current date or a configured date
Could you please help me to figure out how to correctly access the ShadowDom of the new TimeSlider component?
HTML Template:
I'm not sure if it's the best way, but I've done similar things through the use of MutationObservers. For example, I have a workflow to make some adjustments to a Popover, and MutationObservers solve the problem of when the elements are available. After the object is instantiated (and referred to via "this._popover"), I have something like this:
this._popoverObserver = new MutationObserver(this._onPopoverChange.bind(this));
this._popoverObserver.observe(this._popover, {childList:true,subtree:true});
And then an event handler elsewhere in my module:
_onPopoverChange: function() {
if (this._popover.shadowRoot) {
this._popoverObserver.disconnect();
this._popoverObserver = null;
var popoverObserver = new MutationObserver(function() {
var headingElement = this._popover.shadowRoot.querySelector("div.heading");
if (headingElement) {
headingElement.style.fontSize = "var(--calcite-font-size--2)";
var shadowRoot = headingElement.parentNode.querySelector("calcite-action")?.shadowRoot;
if (shadowRoot) {
popoverObserver.disconnect();
popoverObserver = new MutationObserver(function() {
var buttonElement = shadowRoot.querySelector("button");
if (buttonElement) {
popoverObserver.disconnect();
popoverObserver = null;
buttonElement.style.outline = "none";
buttonElement.style.border = "none";
}
});
popoverObserver.observe(shadowRoot, {childList:true,subtree:true});
}
}
}.bind(this));
popoverObserver.observe(this._popover.shadowRoot, {childList:true,subtree:true});
}
}
Once I get a reference to the element I need, I disconnect the existing observer, and then use additional observers to work my way down the tree and make the necessary adjustments.
This issue has come up for me occasionally with other web components (especially those that haven't been fully ported to web component implementations yet and are essentially just wrappers around a widget). However, in this case, I'm able to access the shadow DOM as expected by only using `componentOnReady()`. See the reproduction here: https://codepen.io/fdeters/pen/vEGbBam?editors=1011
I know next to nothing about Angular. I wonder if something in the Angular side of your implementation is causing an issue? Or maybe you need an `await map.viewOnReady()` somewhere as well?
If nothing else works, you could try using a MutationObserver to watch for the element you need. See the function below, which could be adapted to deal with the shadow DOM:
/**
* @Param {string} selector A CSS selector for the element.
* @Param {HTMLElement} [container=null] A container to watch within. Setting
* container as close to the target element's parent as possible will improve
* performance. If no container is provided, it will default to document.body.
* @returns {Promise<HTMLElement>}
*/
export function waitForElement(selector, container = null) {
return new Promise((resolve) => {
const observer = new MutationObserver((mutations, observer) => {
const root = container ? container : document;
const element = root.querySelector(selector);
if (element) {
observer.disconnect();
resolve(element);
}
});
const observerRoot = container ? container : document.body;
observer.observe(observerRoot, {
childList: true,
subtree: true,
});
});
}