How to include a custom vaadin template inside dom-repeat

Hi,

I’m stuck and am hoping someone has some example code on how to do this. Basically I want to display a list of products using dom-repeat. Each product has its own template which is also a vaadin component. The results I’ve had so far are limited to either nothing displays, or that I get an error sub-templates are not supported.

My template code looks like:

<dom-repeat items="[[products]
]">
	<template>
		<product-card-template id="{{tagId}}"/>
	</template>
</dom-repeat>

Then I dynamically set the templates setId() to the same as the tagId value.

How do I code the product-card-template so that the above works? Because the dom-repeat is inside a bootstrap container, I can’t use vaadin-grid or vaadin-board.

Any help would be appreciated.

Might be just the tag is not properly closed, try with .
See https://github.com/Polymer/polymer/issues/628

Thanks Haijian, the closing tag sorted out half the problem. The template now displays, but I don’t know how to bind the data to the Java companion class. I tried the @Uses but this results in the injection not supported in sub-templates exception.

Any other ideas?

I’ve tried to narrow down the use case to just display a list of images. So my code looks like:

<dom-repeat items="[[products]
]">
	<template>
		<img id="{{item.tagId}}" src="{{item.imageSource}}" class="img-fluid">
	</template>
</dom-repeat>

and on the Java side:

StreamResource resource = new StreamResource("image.webp", imagesource);
Image img = new Image();
img.setSrc(resource);
img.setId(new String("productimage" + i));
product.setTagId(img.getId().get());
product.setImageSource(img.getSrc());

How do I bind the image to the html? I can’t see how the above with img.setId() would perform the binding. Perhaps another way to ask the question is how to create elements dynamically?

Hi Franz, I think you are on the right track, how does it not work? Image not correctly displayed or what?

No, the image is not displayed. I then tried this with just a h1 tag and also no joy. I tried attaching the h1 tag element to the dom.

            H1 heading = new H1(); //ElementFactory.createHeading1();
            heading.setText(product.getName());
            heading.setId("heading" + i);
            product.setTagId(heading.getId().get());
            getElement().appendChild(heading.getElement());

Also no luck.

Sorry, I’m a bit confused of what you are trying to achieve. Maybe I can understand it better when I see the whole code. Is it an open source project? Can I check it from Gitlab for somewhere?

Hi Haijian,

Thanks for your offer to help. Unfortunately the code is proprietary.

What I am trying to achieve is a common pattern: I need to be able to repeat a number of vaadin components in a dom-repeat. All the examples in the Vaadin documentation show a dom-repeat with plain text like the below example (taken from the Vaadin docs section on “Creating Template Contents Dynamically Based on a List of Items”):

<dom-module id="employees-list">
    <template>
        <table>
            <tr on-click="processElement">
                <th>Name</th><th>Title</th><th>Email</th>
            </tr>
            <dom-repeat items="[[employees]
]">
                <template>
                    <tr on-click="handleClick" id="[[item.name]
]">
                        <td>{{item.name}}</td>
                        <td>{{item.title}}</td>
                        <td>{{item.email}}</td>
                    </tr>
                </template>
            </dom-repeat>
        </table>
    </template>
    <script>
        class EmployeesList extends Polymer.Element {
            static get is() {return 'employees-list'}
        }
        customElements.define(EmployeesList.is, EmployeesList);
    </script>
</dom-module>

Here the repeated content is just plain text, i.e. “{{item.email}}”. I want to be able to repeat a Vaadin component, e.g. a Button, or link or Image or some component, and bind its data and events to the Java code. So in the example above, I would like to also have a image showing the photo of the employee, with the image content coming from the backend DB. And then also have the Image clickable to show the detailed employee page or something like that.

I would guess that this is a common use case, I just can’t find any documentation on how this can be done. Hope this makes sense.

Checked out beverage buddy starter if you haven’t yet https://github.com/vaadin/beverage-starter-flow?
I think it uses some components there inside
The reveiew-listh.html https://github.com/vaadin/beverage-starter-flow/blob/master/src/main/webapp/frontend/src/views/reviewslist/reviews-list.html
and ReviewList.java https://github.com/vaadin/beverage-starter-flow/blob/master/src/main/java/com/vaadin/starter/beveragebuddy/ui/views/reviewslist/ReviewsList.java
should be helpful.

Thanks for the links Haijian, they were very useful and solved 90% of my problems. It seems the trick is to declare the component in html and not in Java and then just register for the events in the Java code.

What this does not yet solve is how to generate dynamic content, for e.g. an image from a DB, inside the dom-repeat. In this case the dynamic content must be declared in Java.

Hi Franz, I think you can use <slot> to achieve such purpose.
The client template would be sth like

<dom-repeat items="[[products]
]" index-as="id">
	<template>
		<slot "img-slot-[[id]
]">
	</template>
</dom-repeat>

The server side Java code would be sth like

for(int i=0; i<products.size(); i++){
	StreamResource resource = new StreamResource("image.webp", imagesource);
	Image img = new Image();
	img.setSrc(resource);
	Element imageElement = img.getElement();
	imageElement.setAttribute("slot", "img-slot-"+i);
	getElement().appendChild(imageElement);
}

Documentation regarding using <slot> can be found here https://github.com/vaadin/flow-and-components-documentation/blob/master/documentation/polymer-templates/tutorial-template-components-in-slot.asciidoc

In summary, the idea is that you leave some slot in your template for dynamic content, and on server side you can then create those components dynamically and use Element api to append the elements of your dynamic components as children elements.

I stumbled onto this post today trying to find an answer.

Instead of dealing with <dom-repeat> elements, I did the following:

Client-side - Add a <div>, and give it an id:

<div id="container"></div>

Server-side - Inject the div via id:

@Id("container")
private Div containerDiv;

Then just add your custom components to the div:

for (int i=0; i < list.size(); i++) {
	
	CustomComponent cc = new CustomComponent();
	containerDiv.add(cc);
}

Perhaps this is too simple for the original post, but I hope this helps others who are strugging with <dom-repeat>.

Bryan