Web component: How to query children of a slot - web-component

Say I have a native web component, and it has a slot:
<slot id="CONTENT_SLOT" name="content"></slot>
const CONTENT_SLOT = this.shadowRoot.getElementById("CONTENT_SLOT") finds the element, but CONTENT_SLOT.children is empty.
this.shadowRoot.querySelectorAll('[slot="content"]') returns an empty node list too.
document.querySelectorAll('[slot="content"]') finds items in the slot, but for all web components.
How can I get a NodeList of the elements in the content slot for the current web component instance only?

You can get the elements that are assigned to the slot with assignedElements().
this.shadowRoot.querySelector('#CONTENT_SLOT').assignedElements();
The difference between doing this and this.querySelectorAll('[slot="content"]') is that you get the elements that are actually in the slot instead the elements who have the slot attribute. Elements that aren't a direct child of the custom-element and do have the attribute will not be in the <slot>, but they will be found with this.querySelectorAll('[slot="content"]').

Figured it out:
this.querySelectorAll('[slot="content"]')

Related

Handling unexpected mutations properties in Vue3js

I have a vue3 webapp that I use to edit some multi-pages documents.
All the pages are stored in a state with pinia. My pinia state is an object with a pages property that is an array containing all the data of each page.
Each page of each document use a specific template, so I created multiple components to handle each template, and I also have subcomponents for some parts that can be found across multiple templates.
I loop through the pages with a root component, passing a reference to the page object, like it :
<PageWrapper v-for="page in pages" :key="page.id" :page="page" />
Then, inside the PageWrapper component, I use the according page template's component, passing along a reference to the page object (the same with subcomponents if any) :
<PageFirst v-if="props.page.type === 'first'" :page="props.page" />
<PageServices v-if="props.page.type === 'services'" :page="props.page" />
<PageTotal v-if="props.page.type === 'total'" :page="props.page" />
<PageContent v-if="props.page.type === 'content'" :page="props.page" />
I wonder what would be the best way to edit a property of my page object from a subcomponent, as I know that it is a bad practice to mutate the property directly.
Do I have to use events ? Is PageWrapper the good place to catch all the events and the modifications?
Any advice on this matter would be of great help to me.
Thanks a lot for your help!
As the Vue official document point out:
In most cases, the child should emit an event to let the parent perform the mutation.
So the answer is you should let the parent do the job of mutating the props. And it will never be a bad solution.
But, look at your design. Your parent component PageWrapper works just like a wrapper. It does nothing but a container for its child.
The props one-way data flow prevents child components from accidentally mutating the parent's state, which can make your app's data flow harder to understand. But if the parent does not actually handle state and your child component's data does not relate with each other, mutating the props inside the child component will be fine.

Selenium message:stale element reference: element is not attached to the page document [duplicate]

I am trying to learn the PageFactory model. I understood the fact that when we do a initElements, the WebElements are located. Say for example, I click on a webelement and because of which there is a change in one of the other webelements in DOM. Now, obviously I would get a StaleElementReferenceException here. How would I resolve this issue?
Should I find that specific WebElement again knowing the fact that there can be a change in the WebElement's properties in the DOM? or is there an another way to handle this?
StaleElementReferenceException
StaleElementReferenceException extends WebDriverException and indicates that the previous reference of the element is now stale and the element reference is no longer present on the DOM of the page.
Common Reasons
The common reasons behind facing StaleElementReferenceException are as follows:
The element has been deleted entirely.
The element is no longer attached to the DOM.
The webpage on which the element was part of has been refreshed.
The (previous) element has been deleted by a JavaScript or AjaxCall and is replaced by a (new) element with the same ID or other attributes.
Solution : If an (old) element has been replaced with new identical one, the simple strategy would be to use findElement() or findElements to look out for the element again.
Answering your queries
When we do a initElements, the WebElements are located : When you call initElements() method, all the WebElements of that page will get initialized. For example,
LoginPageNew login_page = PageFactory.initElements(driver, LoginPageNew.class);
This line of code will initialize all the static WebElements defined within the scope of the LoginPageNew.class whenever and wherever it is invoked from your Automation Script.
I click on a webelement and because of which there is a change in one of the other webelements in DOM : This is pretty much possible.
As an example, in general invoking click() on a <input> tag wouldn't trigger any change of any of the WebElements on the HTML DOM.
Where as invoking click() on a <button> tag or <a> tag may call a JavaScript or a Ajax which inturn may delete an element or can replace the (previous) element by a (new) element with the same ID or other attributes.
Conclusion
So, if WebDriver throws a StaleElementReferenceException, that implies even though the element still exists, the reference is lost. We should discard the current reference we have and replace it by locating the WebElement once again when it gets attached to the DOM. That means you have to again reinitialize the class through initElements() method which inturn reinitializes all the WebElements defined in that page.
Solution
If a old element has been replaced with new identical one, the simple strategy would be to invoke WebDriverWait inconjunction with ExpectedConditions to look out for the element.
You can find relevant detailed discussions in:
How to add explicit wait in PageFactory in PageObjectModel?
References
Here are the references of this discussion:
Stale Element Reference Exception
Class StaleElementReferenceException
Selenium: How to tell if RemoteWebDriver.findElements(By) can throw StaleElementReferenceException at all?
This is a known problem with the PageFactory implementation.
If you are unlucky enough for the element to become stale in the instant between the element being found, and then the element being clicked upon, you will get this error. Unfortunately the PageFactory code does not try to find the element again if it has become stale and it throws an Exception.
I would classify this as a bug with PageFactory, it should auto re-find the element if it ever becomes stale (unless the #CacheLookup annotation is used).
The suggestion to recall initElements isn't going to fix anything, you only need to init the elements once because that binds a Java proxy class to the element in question. The page factory implementation is supposed to remove the possibility of StaleElementReferenceExceptions (hence why this is a bug)
Stale element exception is thrown in two cases
The element is no longer attached to the DOM.
The element has been deleted entirely.
When this happen you wrap your code in try catch block then you can loop and retry as many times as you need until it succeeds.
public void waitForElementPresent(final By by, int timeout){
WebDriverWait wait = (WebDriverWait)new WebDriverWait(driver,timeout)
.ignoring(StaleElementReferenceException.class);
wait.until(new ExpectedCondition<Boolean>(){
#Override
public Boolean apply(WebDriver webDriver) {
WebElement element = webDriver.findElement(by);
return element != null && element.isDisplayed();
}
});
}

Data model in child component

I would like to know what the best practice is for a communication between parent/child components. I have read this blogpost about communication and components states but haven't found the right answer for my problem.
Following components are considered.
My parent component is a List, which renders several Tasks (child component) from objects in the parent component.
So my questions are:
Is it best practice to pass the object to each Task component?
When a value has changed in the Task component, how does the parent component know about this? Because the parent should know about the infos of all children.
So is this a right pattern to use?
In my Parent Component I have this in the render function:
<Task key={index} taskdata={taskobj} />
My Task child component looks like this:
render() {
return (
<input type="text" name="wunsch" defaultValue={this.props.task.title}/>
);
}
So if the value of the input field will change, does taskobj in the parent component also change? In this example no. But what would be the right pattern here?
Basically when you want to pass information from parent to children you use props, when you want to pass information from child to parent you pass a function to a child as a prop and then call it when you need to update a parent.
You can read more in official docs
Also you can take a look Reflux.
A simple library for unidirectional dataflow architecture inspired by ReactJS Flux
In React, data flows one way
I wasn't really aware of this React concept.
So after reading this link in the ReactJS Doc I decided to the onChange/setState() way as ReactLink is already deprecated.
So when a change in the model happens in the child component I call a method in the parents component to update (setState) my data.

'data-renderif' in Batman.js

What exactly does the data-renderif attribute do with Batman.js?
I initially thought that it would render the DOM node only if the attribute value would be true. But then there's data-insertif that does that as expected.
data-renderif is only meant to defer evaluating the bindings of a node's children until the binding value is updated to true. You probably want to use it in conjunction with data-insertif or data-removeif.
The dataChange method for this binding can be viewed here. This function basically just removes the data-renderif attribute and initializes the children node's bindings.

EnumChildWindows doesn't return direct children

I am trying to create an application that dumps all processes and their children controls to a text file. I used EnumChildWindow but it visits all children including children's childdren. I can't create hierarchy using this method.
Is there another way of doing this?
Thanks
You should instead use FindWindowEx() and specify NULL for the hwndChildAfter parameter.
This will enumerate all direct descendant child windows.

Resources