Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Concatenation with conditional produces error #310

Closed
brett53559 opened this issue Jan 31, 2023 · 1 comment
Closed

Concatenation with conditional produces error #310

brett53559 opened this issue Jan 31, 2023 · 1 comment

Comments

@brett53559
Copy link

I'm having an issue where using a th:text method and then using concatenation with a conditional operator is causing the incorrect operation to execute.

Here is the gradle configuration

plugins {
    id 'org.springframework.boot' version '2.6.2'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group = 'org.XYZ'
version = '1.0'
sourceCompatibility = '1.8'

repositories {
    mavenCentral()
}

dependencies {
    //Spring stuff
    implementation 'org.springframework.boot:spring-boot-starter-data-ldap:2.7.4'
    implementation 'org.springframework.boot:spring-boot-starter-data-jpa:2.7.4'
    implementation 'org.springframework.boot:spring-boot-starter-data-rest:2.7.4'
    implementation 'org.springframework.boot:spring-boot-starter-mail:2.7.4'
    implementation 'org.springframework.boot:spring-boot-starter-security:2.7.4'
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf:2.7.4'
    implementation 'org.springframework.boot:spring-boot-starter-web:2.7.4'
    implementation 'org.springframework.boot:spring-boot-starter-websocket'
    implementation 'org.springframework.ldap:spring-ldap-core'
    implementation 'org.springframework.security:spring-security-ldap'
    implementation 'org.springframework.boot:spring-boot-devtools'
    testImplementation 'org.springframework.boot:spring-boot-starter-test:2.7.4'
    testImplementation 'org.springframework.security:spring-security-test:5.7.3'

    //Spring Cloud
    implementation 'org.springframework.cloud:spring-cloud-starter-config:3.1.4'
    implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap:3.1.4'

    //jackson stuff
    implementation 'com.fasterxml.jackson.core:jackson-core:2.13.4'
    implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.4'
    implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:2.13.4'
    implementation 'com.fasterxml.jackson.core:jackson-annotations:2.13.4'

    //webjars
    implementation 'org.webjars:webjars-locator-core'
    implementation 'org.webjars:sockjs-client:1.5.1'
    implementation 'org.webjars:stomp-websocket:2.3.4'

    //Thymeleaf
    implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity5:3.0.4.RELEASE'
    implementation 'org.thymeleaf:thymeleaf'

    //Misc
    implementation 'commons-io:commons-io:2.11.0'
    implementation 'mysql:mysql-connector-java:8.0.30'
    implementation 'com.jcraft:jsch:0.1.55'
    implementation 'org.apache.poi:poi-ooxml:5.2.2'
}

test {
    useJUnitPlatform()
}

Here is the ThymeleafTemplateConfiguration class

package org.XYZ.cafeteriaregistration.config;

import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver;
import org.thymeleaf.templateresolver.ITemplateResolver;
import org.thymeleaf.templateresolver.StringTemplateResolver;

import java.util.Collections;

@Component
public class ThymeleafTemplateConfiguration {

    @Bean
    public TemplateEngine emailTemplateEngine() {
        final SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        // Resolver for TEXT emails
        templateEngine.addTemplateResolver(textTemplateResolver());
        // Resolver for HTML emails (except the editable one)
        templateEngine.addTemplateResolver(htmlTemplateResolver());
        // Resolver for HTML editable emails (which will be treated as a String)
        templateEngine.addTemplateResolver(stringTemplateResolver());
        return templateEngine;
    }

    private ITemplateResolver textTemplateResolver() {
        final ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
        templateResolver.setOrder(1);
        templateResolver.setResolvablePatterns(Collections.singleton("text/*"));
        templateResolver.setPrefix("/mail/");
        templateResolver.setSuffix(".txt");
        templateResolver.setTemplateMode(TemplateMode.TEXT);
        templateResolver.setCharacterEncoding("UTF-8");
        templateResolver.setCacheable(false);
        return templateResolver;
    }

    private ITemplateResolver htmlTemplateResolver() {
        final ClassLoaderTemplateResolver templateResolver = new ClassLoaderTemplateResolver();
        templateResolver.setOrder(2);
        templateResolver.setResolvablePatterns(Collections.singleton("html/*"));
        templateResolver.setPrefix("/mail/");
        templateResolver.setSuffix(".html");
        templateResolver.setTemplateMode(TemplateMode.HTML);
        templateResolver.setCharacterEncoding("UTF-8");
        templateResolver.setCacheable(false);
        return templateResolver;
    }

    private ITemplateResolver stringTemplateResolver() {
        final StringTemplateResolver templateResolver = new StringTemplateResolver();
        templateResolver.setOrder(3);
        // No resolvable pattern, will simply process as a String template everything not previously matched
        templateResolver.setTemplateMode("HTML");
        templateResolver.setCacheable(false);
        return templateResolver;
    }


}

Here is my TemplateRenderService class

package org.XYZ.cafeteriaregistration.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.AbstractContext;
import org.thymeleaf.context.Context;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.spring5.expression.ThymeleafEvaluationContext;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.*;

@Service
public class TemplateRenderService {
    private final TemplateEngine templateEngine;
    private final ApplicationContext applicationContext;

    @Autowired
    public TemplateRenderService(TemplateEngine templateEngine,
                                 ApplicationContext applicationContext) {
        this.templateEngine = templateEngine;
        this.applicationContext = applicationContext;
    }

    public String renderFragment(String fileName, String fragmentName, AbstractContext context) {
        Set<String> fragName = new HashSet<>();
        fragName.add(fragmentName);

        String html = templateEngine.process("fragments/" + fileName, fragName, context);
        return trimHtml(html);
    }

    public String renderTemplate(String fileName, AbstractContext context) {
        String html = templateEngine.process(fileName, context);
        return trimHtml(html);
    }

    private String trimHtml(String html) {
        html = html.replace("\r\n", "");
        html = html.replaceAll(">\\s+<", "><");
        return html.trim();
    }

    public WebContext getTemplateContext(HttpServletRequest request, HttpServletResponse response, ServletContext servletContext) {
        WebContext webContext = new WebContext(request, response, servletContext);
        addSpringContext(webContext);
        return webContext;
    }

    public Context getTemplateContext() {
        //https://stackoverflow.com/questions/31244600/accessing-application-context-beans-from-thymeleaf-email-template
        Context context = new Context();
        addSpringContext(context);
        return context;
    }

    private void addSpringContext(AbstractContext abstractContext) {
        abstractContext.setVariable(ThymeleafEvaluationContext.THYMELEAF_EVALUATION_CONTEXT_CONTEXT_VARIABLE_NAME,
                new ThymeleafEvaluationContext(applicationContext, null));
    }
}

Here is the code block where i assign values to the context for fragment rendering

Context context = templateRenderService.getTemplateContext();
                context.setVariable("name", [[String value]]);
                context.setVariable("registration", [[Custom DAO class]]);
                context.setVariable("removeReason", [[String value]]);
                String emailBody = templateRenderService.renderFragment(EMAIL_FRAG_FILE, "headGroupUserRemoved", context);

Here is the code block of the headGroupUserRemoved fragment

<th:block th:fragment="headGroupUserRemoved()">
    <h3>Hello <span th:text="${name}"></span></h3>
    <p>We found that <span th:text="${registration.getUsers().getName()}"></span> had a registration removed for the following reason "<span th:text="${removeReason}"></span>"</p>
    <p>You are receiving this in case you need to log in and fix their registration.</p>
    <p>Here are the details of the registration that was removed:</p>
    <th:block th:include="~{::registrationDetails()}"></th:block>
    <p>If you have any questions or concerns. Please contact the Dietary department.</p>
</th:block>

<th:block th:fragment="registrationDetails()">
    <ul>
        <li th:text="${'Name: ' + registration.getUsers().getName()}"></li>
        <li th:text="${'Date: ' + registration.getScheduleDateAsString()}"></li>
        <li th:text="${'Room: ' + registration.getRooms().getName()}"></li>
        <li th:text="${'Time: ' + registration.getTimeSlots().getStandardTime()}"></li>
        <li th:text="${'Meal: ' + registration.getMealOptions() != null ? registration.getMealOptions().getSignUpName() : 'Own'}"></li>
    </ul>
</th:block>

In this block the "registration.getMealOptions()" IS null. However, it produces the following error info
I also tried the reverse "'Meal: ' + registration.getMealOptions() == null ? 'Own' : registration.getMealOptions().getSignUpName()" and it was the same error result.

2023-01-30 13:00:53,807 ERROR org.thymeleaf.TemplateEngine [https-jsse-nio-8443-exec-3] [THYMELEAF][https-jsse-nio-8443-exec-3] Exception processing template "fragments/emailfrags::[headGroupUserRemoved]": An error happened during template parsing (template: "class path resource [templates/fragments/emailfrags.html]")
org.thymeleaf.exceptions.TemplateInputException: An error happened during template parsing (template: "class path resource [templates/fragments/emailfrags.html]")
	at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:241)
	at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parseStandalone(AbstractMarkupTemplateParser.java:100)
	at org.thymeleaf.engine.TemplateManager.parseAndProcess(TemplateManager.java:666)
	at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1098)
	at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1059)
	at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1053)
	at org.XYZ.cafeteriaregistration.service.TemplateRenderService.renderFragment(TemplateRenderService.java:33)
	at org.XYZ.cafeteriaregistration.service.EmailService.sendEmailToHeadGroup(EmailService.java:179)
	at org.XYZ.cafeteriaregistration.service.EmailService.sendRoomReservationEmail(EmailService.java:98)
	at org.XYZ.cafeteriaregistration.controller.RoomTimeController.addReservation(RoomTimeController.java:191)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:681)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:764)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.web.servlet.resource.ResourceUrlEncodingFilter.doFilter(ResourceUrlEncodingFilter.java:67)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:327)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:115)
	at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
	at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:122)
	at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:116)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
	at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:126)
	at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
	at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:109)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
	at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
	at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
	at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:219)
	at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:213)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103)
	at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
	at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:132)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
	at org.springframework.security.web.header.HeaderWriterFilter.doHeadersAfter(HeaderWriterFilter.java:90)
	at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:75)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110)
	at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
	at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:55)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
	at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
	at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211)
	at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183)
	at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
	at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:659)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:382)
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:895)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1732)
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
	at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Thread.java:750)
Caused by: org.attoparser.ParseException: Exception evaluating SpringEL expression: "'Meal: ' + registration.getMealOptions() != null ? registration.getMealOptions().getSignUpName() : 'Own'" (template: "fragments/emailfrags" - line 38, col 13)
	at org.attoparser.MarkupParser.parseDocument(MarkupParser.java:393)
	at org.attoparser.MarkupParser.parse(MarkupParser.java:257)
	at org.thymeleaf.templateparser.markup.AbstractMarkupTemplateParser.parse(AbstractMarkupTemplateParser.java:230)
	... 103 common frames omitted
Caused by: org.thymeleaf.exceptions.TemplateProcessingException: Exception evaluating SpringEL expression: "'Meal: ' + registration.getMealOptions() != null ? registration.getMealOptions().getSignUpName() : 'Own'" (template: "fragments/emailfrags" - line 38, col 13)
	at org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:292)
	at org.thymeleaf.standard.expression.VariableExpression.executeVariableExpression(VariableExpression.java:166)
	at org.thymeleaf.standard.expression.SimpleExpression.executeSimple(SimpleExpression.java:66)
	at org.thymeleaf.standard.expression.Expression.execute(Expression.java:109)
	at org.thymeleaf.standard.expression.Expression.execute(Expression.java:138)
	at org.thymeleaf.standard.processor.AbstractStandardExpressionAttributeTagProcessor.doProcess(AbstractStandardExpressionAttributeTagProcessor.java:144)
	at org.thymeleaf.processor.element.AbstractAttributeTagProcessor.doProcess(AbstractAttributeTagProcessor.java:74)
	at org.thymeleaf.processor.element.AbstractElementTagProcessor.process(AbstractElementTagProcessor.java:95)
	at org.thymeleaf.util.ProcessorConfigurationUtils$ElementTagProcessorWrapper.process(ProcessorConfigurationUtils.java:633)
	at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1314)
	at org.thymeleaf.engine.OpenElementTag.beHandled(OpenElementTag.java:205)
	at org.thymeleaf.engine.Model.process(Model.java:282)
	at org.thymeleaf.engine.ProcessorTemplateHandler.handleOpenElement(ProcessorTemplateHandler.java:1587)
	at org.thymeleaf.engine.TemplateHandlerAdapterMarkupHandler.handleOpenElementEnd(TemplateHandlerAdapterMarkupHandler.java:304)
	at org.thymeleaf.templateparser.markup.InlinedOutputExpressionMarkupHandler$InlineMarkupAdapterPreProcessorHandler.handleOpenElementEnd(InlinedOutputExpressionMarkupHandler.java:278)
	at org.thymeleaf.standard.inline.OutputExpressionInlinePreProcessorHandler.handleOpenElementEnd(OutputExpressionInlinePreProcessorHandler.java:186)
	at org.thymeleaf.templateparser.markup.InlinedOutputExpressionMarkupHandler.handleOpenElementEnd(InlinedOutputExpressionMarkupHandler.java:124)
	at org.attoparser.select.BlockSelectorMarkupHandler.handleOpenElementEnd(BlockSelectorMarkupHandler.java:996)
	at org.attoparser.HtmlElement.handleOpenElementEnd(HtmlElement.java:109)
	at org.attoparser.HtmlMarkupHandler.handleOpenElementEnd(HtmlMarkupHandler.java:297)
	at org.attoparser.MarkupEventProcessorHandler.handleOpenElementEnd(MarkupEventProcessorHandler.java:402)
	at org.attoparser.ParsingElementMarkupUtil.parseOpenElement(ParsingElementMarkupUtil.java:159)
	at org.attoparser.MarkupParser.parseBuffer(MarkupParser.java:710)
	at org.attoparser.MarkupParser.parseDocument(MarkupParser.java:301)
	... 105 common frames omitted
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1011E: Method call: Attempted to call method getSignUpName() on null context object
	at org.springframework.expression.spel.ast.MethodReference.throwIfNotNullSafe(MethodReference.java:154)
	at org.springframework.expression.spel.ast.MethodReference.getValueRef(MethodReference.java:83)
	at org.springframework.expression.spel.ast.CompoundExpression.getValueRef(CompoundExpression.java:70)
	at org.springframework.expression.spel.ast.CompoundExpression.getValueInternal(CompoundExpression.java:91)
	at org.springframework.expression.spel.ast.Ternary.getValueInternal(Ternary.java:58)
	at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:112)
	at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:337)
	at org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(SPELVariableExpressionEvaluator.java:265)
	... 128 common frames omitted

When I change it to this code

<th:block th:fragment="registrationDetails()">
    <ul>
        <li>Name: <span th:text="${registration.getUsers().getName()}"></span></li>
        <li>Date: <span th:text="${registration.getScheduleDateAsString()}"></span></li>
        <li>Room: <span th:text="${registration.getRooms().getName()}"></span></li>
        <li>Time: <span th:text="${registration.getTimeSlots().getStandardTime()}"></span></li>
        <li>Meal: <span th:text="${registration.getMealOptions() != null ? registration.getMealOptions().getSignUpName() : 'Own'}"></span></li>
    </ul>
</th:block>

There is no issues.

@brett53559
Copy link
Author

brett53559 commented Jan 31, 2023

This is not an error with Thymeleaf but instead a oversight on my part.

The code should have read

<th:block th:fragment="registrationDetails()">
    <ul>
        <li th:text="${'Name: ' + registration.getUsers().getName()}"></li>
        <li th:text="${'Date: ' + registration.getScheduleDateAsString()}"></li>
        <li th:text="${'Room: ' + registration.getRooms().getName()}"></li>
        <li th:text="${'Time: ' + registration.getTimeSlots().getStandardTime()}"></li>
        <li th:text="${'Meal: ' + (registration.getMealOptions() != null ? registration.getMealOptions().getSignUpName() : 'Own')}"></li>
    </ul>
</th:block>

Parenthesis surrounding the conditional operation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant