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: ₹<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;
}
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("[email protected]") && 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";
}
}
Product
call itoneProduct
oritem
so that the variable is unique and more easily read. – vphilipnycmethod="GET"
instead ofPOST
? – vphilipnyc