skdonthi
skdonthi

Reputation: 1442

thymeleaf th:if - Cannot index into a null value

thymeleaf index.xhtml -

EL1012E: Cannot index into a null value

<div class="mylist" th:each="row,rowStat : *{dataList}">
   Folder: <span th:text="*{dataList[__${rowStat.index}__].folderName}" />
   <div class="invalid-feedback"
        th:if="${dataList[__${rowStat.index}__].folderName == appFolderName}">                           
        Folder already exists. Please choose different folder name.
   </div>
</div>

It is displaying the folderName but not validating th:if and appFolderName is a model attribute [dynamic value].

Upvotes: 0

Views: 919

Answers (1)

andrewJames
andrewJames

Reputation: 21900

For the th:each="row,rowStat : *{dataList}" iterator, I would simplify that code to this:

th:each="row : ${dataList}"

You can think of this as being broadly equivalent to the following Java for-loop:

List<DataItem> dataList = ...; // assume this is populated with DataItems

for (DataItem dataItem : dataList) {
    System.out.println(dataItem.getFolderName());
}

In the above for-loop, we do not need to access the list by index - and the same is also true for the Thymeleaf syntax.


Thymeleaf lets you access fields in an object without needing to refer to the getter method.

So, now that we have our row variable from th:each="row : ${dataList}", we can do this:

<div class="mylist" th:each="row,rowStat : *{dataList}">
    Folder: <span th:text="${row.folderName}" />
    <div class="invalid-feedback"
         th:if="${row.folderName == appFolderName}">                           
        Folder already exists. Please choose different folder name.
    </div>
</div>

In the above code, you can see ${row.folderName} - which means Thymeleaf will invoke the getFolderName() method on the row object. This relies on your object using JavaBean naming standards for your getters.


You can enhance the Thymeleaf th:each processor by adding a second variable - which is what you do in your question: rowStat:

th:each="row,rowStat : ${dataList}"

This gives you access to extra information about the status of the Thymeleaf iterator - you can see a list of all these extra data values here.

These extra values are no needed in your case. But they can be useful in other situations - for example if you want to identify the first or last record in the list, or all even records, and so on.


Your example in the question uses the __${...}__ preprocessing syntax - which is very powerful and can be extremely useful. But again, it is not needed for your basic functionality.


Your example uses both ${...} and *{...} syntaxes to create Thymeleaf variables. It's important to understand the basic differences between them.

The difference is covered in the documentation describing the asterisk syntax:

the asterisk syntax evaluates expressions on selected objects rather than on the whole context. That is, as long as there is no selected object, the dollar and the asterisk syntaxes do exactly the same. And what is a selected object? The result of an expression using the th:object attribute.

The documentation has examples.


Finally, because you are using Spring (as per the tag in your question), then you are actually using Spring's dialect of Thymeleaf and SpEL - the Spring Expression Language.

This is broadly similar to the standard (OGNL) expression language used by the standard (non-Spring) Thymeleaf dialect - but it has several very useful enhancements.

One such enhancement is the safe navigation operator I mentioned in my comment:

${row?.folderName}

Here, the ? will immediately return null if row is null. Without this, you would get a null pointer exception when Thymeleaf attempted to invoke the getFolderName() method on a null row object.

Upvotes: 1

Related Questions