Spring Boot+Thymeleaf:无法解析 Spring EL 表达式 [英] Spring Boot+Thymeleaf: th not able to resolve a Spring EL expression

查看:40
本文介绍了Spring Boot+Thymeleaf:无法解析 Spring EL 表达式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 spring boot+thymeleaf+neo4j.一切正常,除了 thymeleaf 无法解析模板 product_grid.html 中 th:each 块中使用的product"变量的一些属性,其中包括 th:src="${product.URL}", th:text="${Product.title}" 和 th:action="@{/product/(${Product.getId()})}" 表达式在表单标签中.th:text="${Product.Price}" 正在工作.当我检查浏览器中生成的代码时,src 标签为空(src:""),包含标题标签的文本属性未显示在浏览器中.th:action 工作正常,但是当我单击表单内定义的按钮时,url 更改为 http://localhost:8080/product/?btn=View+Product而不是浏览器控制台中显示的以下代码http://localhost:8080/product/?1

注意:我试图从存储在 neo4j 数据库中的字段中获取图像 url.项目目录为:项目目录图片

模板:product_grid.html

<html xmlns:th="http://www.thymeleaf.org" ><头><meta charset="utf-8"/><meta http-equiv="X-UA-Compatible" content="IE=edge"><title>产品</title><meta name="viewport" content="width=device-width, initial-scale=1"><script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.bundle.min.js"integrity="sha384-feJI7QwhOS+hwpX2zkaeJQjeiwlhOP+SdQDqhgvvo1DsjtiSQByFdThsxD"crossorigin="匿名"></script><script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjPVCUar5"7s/脚本><link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet"integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcFAWJSAGiX"crossorigin="匿名"><身体><nav class="navbar navbar-expand-lg navbar-dark bg-dark"><a class="navbar-brand" href="#">Grada</a><button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"aria-expanded="false" aria-label="切换导航"><span class="navbar-toggler-icon"></span><div class="collapse navbar-collapse" id="navbarSupportedContent"><ul class="navbar-nav mr-auto"><li class="nav-item "><a class="nav-link" href="#">首页<span class="sr-only">(当前)</span></a><li class="nav-item"><a class="nav-link">我最好的产品</a><li class="nav-item"><a class="nav-link" th:href="@{/login}">登录</a><form class="form-inline my-2 my-lg-0"><input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search"><a class="btn btn-outline-success my-2 my-sm-0" href="file:///home/madhav/SPM/Grada/public_html/product.html">搜索</a></表单>

</nav><div class="container text-center"><div class="row"><div th:each="Product:${products}" class="col-lg-3 col-sm-12 col-md-6 my-2 p-auto"><div class="card"><div class="card-body"><img src="http://via.placeholder.com/150x150/888/111" th:src="${Product.URL}" alt="img" class="card-img-top img-thumbnailimg-流体"><div class="card-title Lead" th:text="${Product.title}">一些产品名称</div><div class="card-text">价格:&#8377;<span th:text="${Product.Price}">400</span></div>

<form method="GET" action="/" th:action="@{/product/(${Product.getId()})}"><input type="submit" name="btn" class="form-control btn btn-primary" value="查看产品"><input type="submit" name="btn" class="form-control btn btn-primary" value="加入购物车"></表单>

</html>`

产品型号:Product.html

package com.grada.ecommerce.Models;导入 com.grada.ecommerce.Models.Seller;导入 org.neo4j.ogm.annotation.*;导入 java.util.HashSet;导入 java.util.Set;@NodeEntity(label = "产品")公开课产品{公共产品(){}公共产品(字符串标题,双倍价格,整数数量,浮动评级,字符串描述,字符串网址,字符串公司){this.title = 标题;this.Rating = 评分;this.Description = 描述;this.Price = 价格;this.Quantity = 数量;this.URL = url;this.Company = 公司;}@Id@GeneratedValue私人长ID;@Property(name = "title")公共字符串标题;@Property(name = "评分")公众持股量评级;@Property(name = "描述")公共字符串描述;@Property(name = "价格")公开双价;@Property(name = "数量")公共整数数量;@Property(name = "公司")公共弦乐公司;@Property(name = "URL")公共字符串 URL;@覆盖公共字符串 toString(){返回 this.title;}公共长 getId() {返回标识;}公共字符串 getTitle(){返回标题;}@Relationship(type = "Sells", direction = Relationship.INCOMING)公开卖方卖方;}

产品控制器.java

package com.grada.ecommerce.Controllers;导入 com.grada.ecommerce.Models.Product;导入 com.grada.ecommerce.Models.Seller;导入 com.grada.ecommerce.Services.ProductService;导入 com.grada.ecommerce.Services.SellerService;导入 org.springframework.beans.factory.annotation.Autowired;导入 org.springframework.stereotype.Controller;导入 org.springframework.ui.Model;导入 org.springframework.web.bind.annotation.ModelAttribute;导入 org.springframework.web.bind.annotation.PathVariable;导入 org.springframework.web.bind.annotation.RequestParam;导入 org.springframework.web.bind.annotation.RequestMapping;导入 org.springframework.web.bind.annotation.RequestMethod;@控制器公共类产品控制器{最终产品服务产品服务;最终的 SellerService 卖家服务;@自动连线公共产品控制器(产品服务产品服务,卖家服务卖家服务){this.productService = productService;this.sellerService = 卖家服务;}@RequestMapping(value = "/", method = RequestMethod.GET)公共字符串索引(模型模型){可迭代的<产品>产品 = productService.products();model.addAttribute("产品", 产品);返回产品网格";}@RequestMapping(value = "/product", method = RequestMethod.GET)public String ShowProduct(@RequestParam(value = "id") Long id, Model model){产品产品 = productService.findProductByID(id);如果(产品 == 空)返回重定向:/";model.addAttribute("product", product);返回productid";}@RequestMapping(value = "/add")公共字符串添加产品(模型模型){model.addAttribute("product", new Product());返回添加";}@RequestMapping(value = "/add", method = RequestMethod.POST)public String AddProduct(@ModelAttribute Product product){productService.addProduct(product);返回重定向:/";}@RequestMapping(value = "/delete", method = RequestMethod.GET)公共字符串删除产品(模型模型){model.addAttribute("product", new Product());返回删除";}@RequestMapping(value = "/delete", method = RequestMethod.POST)public String DeleteProduct(@ModelAttribute Product product){productService.deleteProduct(product);返回重定向:/";}@RequestMapping(value = "/login", method = RequestMethod.GET)public String LoginPage(模型模型){返回登录";}@RequestMapping(value = "/login", method = RequestMethod.POST)public String Authenticate(模型模型,字符串用户名,字符串密码){if (username.equalsIgnoreCase("seller@fake.com") && password.equals("fakeseller")){可迭代的<产品>产品 = productService.products();model.addAttribute("产品", 产品);返回登录";}别的返回重定向:/登录";}@RequestMapping(value = "/policy", method = RequestMethod.GET)公共字符串 PolicyPage(){返回政策";}}

解决方案

欢迎来到 SO.

Product 类中为您的变量包含 setX 方法.Thymeleaf 需要这些来绑定.

IMO,一个很好的方法是使用 Project Lombok 并简单地使用 @Data 注释您的类.然后你甚至不需要指定 getter 或 setter(或你的 toString()).

对变量使用小写,因为按照惯例,首字母大写的变量指的是类,而不是实例变量.

如前所述,在提交数据时,请在表单中使用 POST 而不是 GET.

使用速记 @GetMapping@PostMapping 使其更易于阅读.

I am using spring boot+thymeleaf+neo4j. Everything is working fine except that thymeleaf is not able to resolve a few of the attributes of the 'product' variable used in the th:each block in the template product_grid.html, which includes th:src="${product.URL}", th:text="${Product.title}" and the th:action="@{/product/(${Product.getId()})}" expression in form tag. The th:text="${Product.Price}" is working. When I check the code produced in the browser the src tag is empty (src:""), the text attribute containing the title tag is not shown in the browser. The th:action works fine but when I click the button defined inside the form, the url changes to http://localhost:8080/product/?btn=View+Product instead of the following code which is shown in the browser console http://localhost:8080/product/?1

Note: I am trying to get the image url from a field which is stored in neo4j database. The project directory is: project directory image

Template:product_grid.html

<html xmlns:th="http://www.thymeleaf.org" >
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Products</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.bundle.min.js" integrity="sha384-feJI7QwhOS+hwpX2zkaeJQjeiwlhOP+SdQDqhgvvo1DsjtiSQByFdThsxO669S2D"
            crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm"
          crossorigin="anonymous">
</head>

<body>


<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
    <a class="navbar-brand" href="#">Grada</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent"
            aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav mr-auto">
            <li class="nav-item ">
                <a class="nav-link" href="#">Home
                    <span class="sr-only">(current)</span>
                </a>
            </li>
            <li class="nav-item">
                <a class="nav-link">My Best Products</a>
            </li>

            <li class="nav-item">
                <a class="nav-link" th:href="@{/login}">Login</a>
            </li>
        </ul>
        <form class="form-inline my-2 my-lg-0">
            <input class="form-control mr-sm-2" type="search" placeholder="Search" aria-label="Search">
            <a class="btn btn-outline-success my-2 my-sm-0" href="file:///home/madhav/SPM/Grada/public_html/product.html">Search</a>
        </form>
    </div>
</nav>
<div class="container text-center">
    <div class="row">
        <div th:each="Product:${products}" class="col-lg-3 col-sm-12 col-md-6 my-2 p-auto">
            <div class="card">
                <div class="card-body">
                    <img src="http://via.placeholder.com/150x150/888/111" th:src="${Product.URL}" alt="img" class="card-img-top img-thumbnail img-fluid">
                    <div class="card-title lead" th:text="${Product.title}">Some product name</div>
                    <div class="card-text">Price: &#8377;<span th:text="${Product.Price}">400</span></div>
                </div>
                <form method="GET" action="/" th:action="@{/product/(${Product.getId()})}">
                    <input type="submit" name="btn" class="form-control btn btn-primary" value="View Product">
                    <input type="submit" name="btn" class="form-control btn btn-primary" value="Add to Cart">
                </form>
            </div>
        </div>
    </div>
</div>

</body>
</html>`

Product model:Product.html

package com.grada.ecommerce.Models;


import com.grada.ecommerce.Models.Seller;
import org.neo4j.ogm.annotation.*;


import java.util.HashSet;
import java.util.Set;


@NodeEntity(label = "Product")
public class Product
{
    public  Product()
    {
    }

    public Product(String title, Double price, int quantity, float rating, String description, String url, String company)
    {
        this.title  = title;
        this.Rating = rating;
        this.Description = description;
        this.Price = price;
        this.Quantity = quantity;
        this.URL = url;
        this.Company = company;
    }

    @Id
    @GeneratedValue
    private Long id;

    @Property(name = "title")
    public String title;
    
    @Property(name = "Rating")
    public float Rating;
    @Property(name = "Description")
    public String Description;
    @Property(name = "Price")
    public Double Price;
    @Property(name = "Quantity")
    public int Quantity;
    @Property(name = "Company")
    public String Company;
    @Property(name = "URL")
    public String URL;


    @Override
    public String toString()
    {
        return this.title;
    }

    public Long getId() {
        return id;
    }

    public String getTitle()
    {
        return title;
    }

   @Relationship(type = "Sells", direction = Relationship.INCOMING)
   public Seller Seller;

}

ProductController.java

package com.grada.ecommerce.Controllers;

import com.grada.ecommerce.Models.Product;
import com.grada.ecommerce.Models.Seller;
import com.grada.ecommerce.Services.ProductService;
import com.grada.ecommerce.Services.SellerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class ProductController
{

    final ProductService productService;
    final SellerService sellerService;

    @Autowired
    public ProductController(ProductService productService, SellerService sellerService)
    {
        this.productService = productService;
        this.sellerService = sellerService;
    }

    @RequestMapping(value = "/", method = RequestMethod.GET)
    public String Index(Model model)
    {
        Iterable<Product> products  = productService.products();
        model.addAttribute("products", products);
        return "product_grid";
    }

    @RequestMapping(value = "/product", method = RequestMethod.GET)
    public String ShowProduct(@RequestParam(value = "id") Long id, Model model)
    {
        Product product = productService.findProductByID(id);
        if(product == null)
            return "redirect:/";
        model.addAttribute("product", product);
        return "productid";
    }

    @RequestMapping(value = "/add")
    public String AddProduct(Model model)
    {
        model.addAttribute("product", new Product());
        return "add";
    }

    @RequestMapping(value = "/add", method = RequestMethod.POST)
    public String AddProduct(@ModelAttribute Product product)
    {
        productService.addProduct(product);
        return "redirect:/";
    }

    @RequestMapping(value = "/delete", method = RequestMethod.GET)
    public String DeleteProduct(Model model)
    {
        model.addAttribute("product", new Product());
        return "delete";
    }

    @RequestMapping(value = "/delete", method = RequestMethod.POST)
    public String DeleteProduct(@ModelAttribute Product product)
    {
        productService.deleteProduct(product);
        return "redirect:/";
    }

    @RequestMapping(value = "/login", method = RequestMethod.GET)
    public String LoginPage(Model model)
    {
        return "login";
    }

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public String Authenticate(Model model, String username, String password)
    {
        if (username.equalsIgnoreCase("seller@fake.com") && password.equals("fakeseller"))
        {
            Iterable<Product> products  = productService.products();
            model.addAttribute("products", products);
            return "loggedin";
        }

        else
            return "redirect:/login";
    }

    @RequestMapping(value = "/policy", method = RequestMethod.GET)
    public String PolicyPage()
    {
        return "policies";
    }
}

解决方案

Welcome to SO.

Include setX methods for your variables in the Product class. Thymeleaf needs these to bind.

IMO, a great way to do this is to use Project Lombok and simply annotate your class with @Data. Then you won't even need to specify getters or setters (or your toString()) at all.

Use lower-case for your variables since by convention variables with a capital first letter refers to the class, not an instance variable.

As mentioned, use POST instead of GET in your form since you are submitting data.

Use the shorthand @GetMapping and @PostMapping to make it easier to read.

这篇关于Spring Boot+Thymeleaf:无法解析 Spring EL 表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
其他开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆