I'm trying to create a recursive list using Thymeleaf. I'm using a simple Java object to model a node which has has two fields, a description and then an array list of child nodes. I'm using the following HTML/Thymeleaf to process the structure but it isn't recursively iterating through to the next level down.
My Java code looks as follows:
public class Node {
public String description;
public ArrayList<Node> children;
}
My Thymeleaf/HTML code is as follows:
<html>
...
<body>
<div th:fragment="fragment_node" th:remove="tag">
<ul th:if="${not #lists.isEmpty(node.children)}" >
<li th:each="child : ${node.children}"
th:text="${child.description}"
th:with="node = ${child}"
th:include="this::fragment_node">List Item</li>
</ul>
</div>
</body>
</html>
If my data structure looks as follows:
Main node 1
Child node 1
Child node 2
Main node 2
Child node 3
Child node 4
I'd expect to get:
<ul>
<li>Main Node 1</li>
<li>
<ul>
<li>Child node 1</li>
<li>Child node 2</li>
</ul>
</li>
<li>Main Node 2</li>
<li>
<ul>
<li>Child node 3</li>
<li>Child node 4</li>
</ul>
</li>
</ul>
However, I only get:
<ul>
<li>Main Node 1</li>
<li>Main Node 2</li>
</ul>
Can anyone spot why this may not be working?
The cause of the problem is
You are trying to th:text and trying to add the description to a <li> as well as you are trying to th:include the fragment inside the same tag <li>.
Your th:include is replaced by the th:text as th:text is processed with priority by default.
Direct solution to your source code
.....
<li th:each="child : ${node.children}" th:inline="text" th:with="node = ${child}">
[[${child.description}]]
<ul th:replace="this::fragment_node">List Item</ul>
</li>
.....
Even thought the above will work as you want, personally I find some design issues in your thymeleaf page.
Better solution using fragment parameters
...
<ul th:fragment="fragment_node(node)" th:unless="${#lists.isEmpty(node.children)}" >
<li th:each="child : ${node.children}" th:inline="text">
[[${child.description}]]
<ul th:replace="this::fragment_node(${child})"></ul>
</li>
</ul>
...
Related
Hi I am try to amke regexp which extract only li tags in ul tags (no ol)
Text:
<ul><li>some text</li></ul>
<ol><li>some text</li></lo>
Extracted
<ul>**<li>**some text</li></ul>
<ol><li>some text</li></lo>
Could you help me ?
Solution 1
Regex solution
/(?<=<ul>\s*(?:<li>.*?<\/li>\s*)*)<li>.*?<\/li>/gi
Demo
If you work in a team and someone else may read your code I advise you to use Solution 2. It's more simple and easy to understand by code reading.
Solution 2
Do it in 2 steps:
Delete all <ol>...</ol> nodes;
Take all <li>...</li> nodes.
*I assume your html is valid and you have no <li> outside <ul> or <ol>.
Code example in JavaScript:
let html = `
<ul>
<li>take this node 1</li>
<li>take this node 2</li>
</ul>
<ol>
<li>exclude this node</li>
<li>exclude this node</li>
</ol>
<ul>
<li>take this node 3</li>
<li>take this node 4</li>
</ul>
<ol>
<li>exclude this node</li>
<li>exclude this node</li>
</ol>
`;
let htmlWithoutOl = html.replace(/<ol>.*?<\/ol>/gis, '');
let matches = htmlWithoutOl.matchAll(/<li>.*?<\/li>/gis);
for (const match of matches) {
console.log(match[0]);
}
This question already has answers here:
Property does not exist on type 'DetailedHTMLProps, HTMLDivElement>' with React 16
(4 answers)
Closed 3 years ago.
I have to build an html popup inside a react component but I get errors
from this code
<div>
<button class="toggle">Button</button>
<div id="link-box">
<ul>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
<li>Link 4</li>
</ul>
</div>
</div>
error:
Type '{ children: string; class: string; }' is not assignable to type 'DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>'.
Property 'class' does not exist on type 'DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>'.
I'd like to accomplish this without a react npm package for dropdown menus.
If I have to do some really rudimentary html with later css styling that okay. But I need some insight into what are the issues I'm facing how to best proceed.
More of File code:
export class Header extends React.Component<IHeaderProps, IHeaderState> {
public handleChange() {
console.log('handling');
}
public render() {
return (
<div>
<button class="toggle">Button</button>
<div id="link-box">
<ul>
<li>Link 1</li>
<li>Link 2</li>
<li>Link 3</li>
<li>Link 4</li>
</ul>
</div>
</div>
);
}
}
About the error you are facing, if you could provide the typescript code of the component then we could look for a solution.
By the way since you are using React you could set a Modal to popup when certain buttons are clicked.
These are some Modal examples .
And this is a Modal tutorial that doesn't require to install any npm package, which may be what you are looking for.
MDN states the following about the [attr~=value] attribute selector:
Represents elements with an attribute name of attr whose value is a
whitespace-separated list of words, one of which is exactly value.
If liquid is not separated by whitespace, then why is it working?
[data-vegetable~="liquid"] {
color: red;
}
Ingredients for my recipe: <i lang="fr-FR">Poulet basquaise</i>
<ul>
<li data-quantity="1kg" data-vegetable>Tomatoes</li>
<li data-quantity="3" data-vegetable>Onions</li>
<li data-quantity="3" data-vegetable>Garlic</li>
<li data-quantity="700g" data-vegetable="not spicy like chili">Red pepper</li>
<li data-quantity="2kg" data-meat>Chicken</li>
<li data-quantity="optional 150g" data-meat>Bacon bits</li>
<li data-quantity="optional 10ml" data-vegetable="liquid">Olive oil</li>
<li data-quantity="25cl" data-vegetable="liquid">White wine</li>
</ul>
It's not separated by whitespace but:
a whitespace-separated list of words
Which mean a list of words where you have whitespace between and if alone no need whitespace because there is nothing to separate.
[data-vegetable~="liquid"] {
color: red;
}
<ul>
<li data-vegetable="liquid other and other">this one</li>
<li data-vegetable="liquid">and this one</li>
<li data-vegetable="liquid ">also this one</li>
<li data-vegetable="another liquid ">also this one</li>
<li data-vegetable="liquid-one">NOT this one !!</li>
<li data-vegetable="another-liquid">NOT this one !!</li>
<li data-vegetable="aliquid">NOT this one !!</li>
</ul>
For the last ones you will need *
[data-vegetable*="liquid"] {
color: red;
}
<ul>
<li data-vegetable="liquid other and other">this one</li>
<li data-vegetable="liquid">and this one</li>
<li data-vegetable="liquid ">also this one</li>
<li data-vegetable="another liquid ">also this one</li>
<li data-vegetable="liquid-one">this one too!!</li>
<li data-vegetable="another-liquid">this one too!!</li>
<li data-vegetable="aliquid">this one too!!</li>
</ul>
A list of words has separators between the words.
If the list contains only one word, there are no separators
What that means is, the attribute (data-vegetable in this case) can have multiple values, assigned to it in one string using whitespace to delimit the values, like data-vegetable="liquid foo bar", which gives data-vegetable three values 'liquid', 'foo' and 'bar'.
The selector can be used to match any of these values, so you will get this element when matching either [data-vegetable~="liquid"], [data-vegetable~="foo"] or [data-vegetable~="bar"].
It said: "whitespace-separated list of words"
That means that it is a list, separated by whitespace. But a list can have only one item. So in that case, no spaces are needed.
I would like to add few Bootstrap classes to <ul id="markdown-toc">. How can I do it?
Input:
* ToC
{:toc}
Output:
<ul id="markdown-toc">
<li>Welcome</li>
<li>Header 1</li>
</ul>
Add the class name you want to apply as an IAL:
* TOC
{:toc}
{: .this-is-my-class}
Then it will generate:
<ul class="this-is-my-class" id="markdown-toc">
<li>..</li>
</ul>
the HTML:
<ul id="nav">
<li id="listItem">a list item</li>
<li id="link01">list item with ID</li>
<li id="link02">another link with ID</li>
<li class="lastItem">Contact</li>
<li class="lastItem">the Very Last List Item</li>
</ul>
the JavaScript:
alert($$('.lastItem').getFirst('li').get('text'));
console returns this error:
TypeError: $$(...).getFirst(...).get is not a function
um...whut? what did i miss? if i take out the getFirst(), it works, but returns, of course, both <li> text contents... which i don't want. just want the first...
halp.
WR!
You trying to call getFirst on Elements array($$ return elements array!) the getFirst() method is only on a dom mootools element and it will return his first child
what you are looking for is this:
alert($$('.lastItem')[0].get('text'));