0

I want to display different Types of Objects (same supertype) in the same Grid. I want them rendered as Cards, with the help of Template Renderer. Suppose I want to build a webshop for PC-parts. I would have an abstract Class AbstractPcPart.

public abstract class AbstractPcPart {
    private Price price;
    private String name;
    private Int stockQuantity;
    // getters and setters
}

Now I want to have different Parts with different Fields.

public class GraphicsCard extends AbstractPcPart {
    private List<Connector> connections;
    private CoolingSystem coolingSystem;
    private int PciExpressVersion;
    private String cardModel;
    // getters and setters
}
public class Memory extends AbstractPcPart {
    private MemoryConfiguration configuration;
    private String formFactor;
    private int frequency;
    // getters and setters
}

What I already have

I can display one Type in a List. This looks something like this: grid view with memory only Code for this List:

@JsModule("./src/views/parts/card/memory-card.js")
public class PcPartsGrid extends Grid<AbstractPcPart> {
    public PcPartsGrid() {
        super();
        
        addColumn(MemoryCard.getTemplate()
                .withProperty("partCard", MemoryCard::create))
}

public class MemoryCard extends AbstractPcPartCard {
    AbstractPcPart pcPart;
    public MemoryCard (AbstractPcPart pcPart) {
        this.pcPart = pcPart;
    }
    public static MemoryCard create(AbstractPcPart pcPart) {
        return new MemoryCard(pcPart)
    }
    public static TemplateRenderer<AbstractPcPart> getTemplate() {
        return TemplateRenderer.of(
                "<memory-card memory-card='[[item.partCard]]'>"
                + "</memory-card>"
    }

What is the problem?

Now this is fine and all, but when trying to build a grid with different template-renderers this does not work. I tried to add the fallowing Factory.

public class CardFactory {
    public static AbstractPcPart create(AbstractPcPart pcPart) {
        if (pcPart.getClass() == GraphicsCard.class) {
            return GraphicsCardCard.create(pcPart);
        } else if (pcPart.getClass() == Memory.class) {
            return MemoryCard.create(pcPart);
        } else {
            // different Pc Parts
        }
    }

    public static TemplateRenderer<AbstractPcPart> getTemplate(AbstractPcPart pcPart) {
        if (pcPart.getClass() == GraphicsCard.class) {
            return GraphicsCardCard.getTemplate();
        } else if (pcPart.getClass() == Memory.class)  {
            return MemoryCard.getTemplate();
        } else {
            // different Pc Parts
        }
    }
}

And changed the Grid to use this Factory.

@JsModule("./src/views/parts/card/memory-card.js")
@JsModule("./src/views/parts/card/graphics-card-card.js")
public class PcPartsGrid extends Grid<AbstractPcPart> {
    public PcPartsGrid() {
        super();
        
        addColumn(pcPart -> CardFactory.getTemplate(pcPart)
                .withProperty("partCard", CardFactory::create))
}

This is not working as expected, instead of rendering the different Templates in my Grid, it is showing me the references to the template-renderers. template-renderer reference

If this is supposed to work, what am I doing wrong, and what do I need to change, that instead of showing the reference, the template gets rendered?

And if this is expected behaviour, what are viable alternatives?

Edit: I have built an example project and put it on github, for everyone to look at. Example-Project

The Application in the project contains a view vor every different method i used to fill a grid with templates. The fallowing views are present:

  • Original View (thats what i had at the beginning, using template renderer for only one version of pcPart)
  • View using Cardfactory (this factory is intended to use a different template for each type of pcPart) this shows only the references to the objects instead of the templates (I think this is because I am using a lambda inside the addColumn() method that I inherit from the vaadin Grid)
  • View using dom-if elements (this view is using dom-if elements as described in this answer from ollitietavainen, sadly its not working as intended: it’s showing both templates instead of just one)
  • View that is not using a template (this is just a simple grid using value providers)
  • View that is using a component renderer
  • Example-View from Vaadin that also uses a component renderer
Dario Viva
  • 215
  • 1
  • 10

1 Answers1

3

You can use conditional templates to a degree. The <dom-if if="item.isSomething"> template gets stamped if the isSomething property is truthy. So you can use something like

"<template is='dom-if' if='[[item.first]]'>first template</template>
<template is='dom-if' if='[[item.second]]'>second template</template>
<template is='dom-if' if='[[item.third]]'>third template</template>"

which renders first template if item.first is true and so on. You need to supply the first, second and third properties to the TemplateRenderer with something like .withProperty("first", item -> item.isFirst() ).withProperty("second", item -> item.isSecond()) and so on.

ollitietavainen
  • 3,900
  • 13
  • 30
  • thank you very much for the quick answer. It is sadly still not working, I will prepare a minimal runnable example. The problem seems to come from the create method. running in debugger mode, the breakpoint in the create point is not reached. – Dario Viva Oct 12 '21 at 19:56
  • That's a good point. You should not need to create a new instance of the class to set to a property. Instead, you provide a list of items to the Grid (either with setItems or through setDataProvider). The properties you set to the template renderer using .withProperty() are values from a bean instance; typically they are basic types like Strings, Integers or Booleans. Especially when you're using "dom-if", the value you map to the "if" attribute should be a Boolean. – ollitietavainen Oct 13 '21 at 06:56
  • sorry @ollietavainen for needing such a long time. i had different problems, my IDE was crashing a lot, but in the end i was able to create a project that includes the different methods i tried out. your method looks promising, but its not working as intended. I also found out about a completly different method using component renderer. this is working good. althoug experts at vaadin have made a [performance comparison](https://vaadin.com/blog/using-the-right-r) between template renderer and component renderer, and component renderer is significantly slower. – Dario Viva Oct 21 '21 at 14:22
  • Yes, ComponentRenderer is a possibility, but it is definitely something that you need to be careful about, because like you noticed, with complex Grids the performance impact will compound. – ollitietavainen Oct 22 '21 at 05:32
  • well these are not my measurements. I think, as I am creating a new instance anyway (also for the template renderer) it should not really make a huge difference. Yes for the template renderer it is not strictly neccesairy to create a new instance, but this is the way other projects are built in my team, and I dont want to reinvent the wheel completly. My question now is what did I do wrong with your dom-if method. It renders both templates instead of just the one I need. – Dario Viva Oct 22 '21 at 06:40
  • Maybe you can post a new question about that? – ollitietavainen Oct 22 '21 at 06:43
  • I have now posted a new question: https://stackoverflow.com/q/69675347/17133024 thanks for helping, if you or someone else can solve this problem, then I will also accept this answer. If you want I can accept the answer now, but I feel the other question is connected to this problem. – Dario Viva Oct 22 '21 at 10:33