Search code examples
htmlthymeleafnested-table

Thymeleaf nested Set<Object>


I'm having a problem accessing a nested Set of objects. I have defined the below objects :

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "site")
public class Site {
    @Id
    @GeneratedValue(strategy = GerationType.IDENTITY)
    @Column(name="id", updatable=false,nullable=false)
    private Long id;

    private String siteName;
    private String siteLocation;

    @OneToMany(cascade=CascadeType.ALL, mappedBy = "site")
    private Set<Rack> rack = new HashSet<>();
}

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "rack")
public class Rack { 

    @Id
    @GeneratedValue(strategy = GerationType.IDENTITY)
    @Column(name="id", updatable=false,nullable=false)
    private Long id;
    private String rackName;
    private String rackAssetTag;
    private String rackCMDBCode;

    @ManyToOne
    @JoinColumn(name = "site_id")
    private Site site;

    @OneToMany(cascade=CascadeType.ALL, mappedBy = "box")
    private Set<Box> box = new HashSet<>();
}

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@Entity
@Table(name = "box")
public class Box {  
    @Id
    @GeneratedValue(strategy = GerationType.IDENTITY)
    @Column(name="id", updatable=false,nullable=false)
    private Long id;
    private boxAssetTag;
    private boxCMDBCode;

    ManyToOne
    @JoinColumn(name = "rack_id")
    private Rack rack;

}

All relation mapping work tiptop. The problem is when I want to create a nice nested table for this(css formating and conditional thymeleaf validation removed since it's irrelevant) :

<div>
    <table>
        <thead>
            <tr>
                <th>Rack name</th>
                <th>Rack asset tag</th>
                <th>Rack CMDB code</th>
            </tr>
        </thead>
        <tbody>
            <tr th:each="rack:${site.rack}">
                <td th:text="${rack.rackName}"></td>
                <td th:text="${rack.rackAssetTag}"></td>
                <td th:text="${rack.rackCMDBCode}"></td>
            </tr>
            <tr>
                <td>
                    <table>
                        <thead>
                            <tr>
                                <th>Box asset tag</th>
                                <th>Box CMDB code</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr th:each="box:${rack.box}">
                                <td th:text="${box.boxAssetTag}">
                                <td th:text="${box.boxCMDBCode}">
                            </tr>
                        </tbody>
                    </table>
                </td>
            </tr>
        </tbody>
    </table>
</div>

Controller adds one object 'site' to the model that holds all relation. When accessing the page I receive and error: Property or field 'box' cannot be found on null

I think that when I move to the second table thymeleaf looses the context of the object rack created in the outer table. Therefore when I try to invoke the th:each in the inner table the there is no rack object to perform ${rack.box}. The question is how to be able to access the 'deeper' object in thymeleaf without loosing the context of the object above?

Regards, Jarek.


Solution

  • Ok so I've managed to work out a solution. I'll write it up. Maybe someone some day will need it.

    So the idea is to cycle through each object but on the body element not on the row. This lets You have the context of the object much wider

    <div>
    <table>
        <thead>
            <th>Rack name</th>
            <th>Rack asset tag</th>
            <th>Rack CMDB code</th>
        </thead>
        <tbody th:each="rack:${site.rack}">
            <tr>
                <td th:text="${rack.rackName}"></td>
                <td th:text="${rack.rackAssetTag}"></td>
                <td th:text="${rack.rackCMDBCode}"></td>
            </tr>
            <tr>
                <td>
                    <table>
                        <thead>
                            <tr>
                                <th>Box asset tag</th>
                                <th>Box CMDB code</th>
                            </tr>
                        </thead>
                        <tbody>
                            <tr th:each="box:${rack.box}">
                                <td th:text="${box.boxAssetTag}">
                                <td th:text="${box.boxCMDBCode}">
                            </tr>
                        </tbody>
                    </table>
                </td>
            </tr>
        </tbody>
    </table>