如何获取 thymeleaf select 选项中的对象?
How can get the object in thymeleaf select option?
在自动完成中,我得到了预期的产品名称。
我想根据所选产品做一些计算。但是在 doCalculation 函数中,我得到的是 id
而不是 'price'。所以计算没有按预期工作。
假设如果我更改 String idExpression = "#{price}";
,则计算会按预期进行,但订单未保存。由于出现如下错误
Failed to convert property value of type [java.lang.String] to required type [com.myapp.domain.Product] for property product; nested exception is
org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@javax.persistence.OneToOne
@io.springlets.format.EntityFormat com.myapp.domain.Product] for value 2500; nested exception is java.lang.IllegalStateException: Parsers are not allowed to return null: io.springlets.format.EntityParser@2201ba1c
所以我想在获取价格的同时进行计算,保存功能不应该被破坏。现在第一个或第二个对我有用。
ProductsCollectionThymeleafController.java
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE, name = "select2", value = "/s2")
@ResponseBody
public ResponseEntity<Select2DataSupport<Product>> select2(GlobalSearch search, Pageable pageable,
Locale locale) {
Page<Product> products = getProductService().findAll(search, pageable);
String idExpression = "#{id}";
Select2DataSupport<Product> select2Data =
new Select2DataWithConversion<Product>(products, idExpression, getConversionService());
return ResponseEntity.ok(select2Data);
}
OrderCollectionThymeleafController.java
@PostMapping(name = "create")
public ModelAndView create(@Valid @ModelAttribute Order order, BindingResult result,
Model model) {
if (result.hasErrors()) {
populateForm(model);
return new ModelAndView("/order/create");
}
Order newOrder = getOrderService().save(order);
UriComponents showURI = getItemLink().to(OrderItemThymeleafLinkFactory.SHOW)
.with("order", newOrder.getId()).toUri();
return new ModelAndView("redirect:" + showURI.toUriString());
}
orderview.html
<form class="form-horizontal validate" method="POST" data-th-object="${order}" data-th-action="@{${collectionLink.to('create').with('order', order.id)}}">
<fieldset id="containerFields">
<div class="form-group has-error has-feedback" data-z="3c00987d" id="servicio-product-field" data-th-classappend="${#fields.hasErrors('product')}? 'has-error has-feedback'" data-th-class="form-group" data-th-with="collectionLink=${@linkBuilder.of('ProductsCollectionThymeleafController')}">
<label for="product" class="col-md-3 control-label" data-th-text="#{label_servicio_product}">Product</label>
<div class="col-md-6">
<!-- Select2 -->
<select data-th-field="*{product}" onChange="doCalculation()" class="form-control dropdown-select-ajax" data-allow-clear="true" data-data-ajax--url="${collectionLink.to('select2')}" data-ajax--cache="true" data-ajax--delay="250" data-ajax--data-type="json" data-data-placeholder="#{info_select_an_option}">
<option data-th-unless="*{product} == null" data-th-value="*{product.id}" data-th-text="*{{product}}" selected="selected">Product</option>
</select>
<span data-th-classappend="${#fields.hasErrors('product')}? 'glyphicon glyphicon-remove form-control-feedback'" class="glyphicon glyphicon-remove form-control-feedback" data-th-if="${#fields.hasErrors('product')}" aria-hidden="true"></span>
<span id="product-error" class="help-block" data-th-if="${#fields.hasErrors('product')}" data-th-errors="*{product}">Error message.</span>
</div>
</div>
<script>
function doCalculation() {
var price = document.getElementById("product").value;
alert("price: " + price);
// Do some calculation
}
doCalculation();
</script>
</fieldset>
</form>
Product.java
@RooJavaBean
@RooToString
@RooJpaEntity
@RooEquals(isJpaEntity = true)
@Entity
@EntityFormat
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String productName;
@Min(1L)
@NumberFormat
private Integer price;
@OneToOne(fetch = FetchType.LAZY)
@EntityFormat
private Order order;
public static final String ITERABLE_TO_ADD_CANT_BE_NULL_MESSAGE = "The given Iterable of items to add can't be null!";
public static final String ITERABLE_TO_REMOVE_CANT_BE_NULL_MESSAGE = "The given Iterable of items to add can't be null!";
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getPrice() {
return this.price;
}
public void setPrice(Integer price) {
this.price = price;
}
public String getProductName() {
return this.productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public Order getOrder() {
return this.order;
}
public void setOrder(Order order) {
this.order= order;
}
}
Order.java
@RooJavaBean
@RooToString
@RooJpaEntity
@RooEquals(isJpaEntity = true)
@Entity
@EntityFormat
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Version
private Integer version;
@OneToOne(cascade = { javax.persistence.CascadeType.MERGE,
javax.persistence.CascadeType.PERSIST }, fetch = FetchType.LAZY, mappedBy = "order")
@RooJpaRelation(type = JpaRelationType.AGGREGATION)
@EntityFormat
private Product product;
public static final String ITERABLE_TO_ADD_CANT_BE_NULL_MESSAGE = "The given Iterable of items to add can't be null!";
public static final String ITERABLE_TO_REMOVE_CANT_BE_NULL_MESSAGE = "The given Iterable of items to add can't be null!";
/**
* This `equals` implementation is specific for JPA entities and uses the
* entity identifier for it, following the article in
* https://vladmihalcea.com/2016/06/06/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/
*
* @param obj
* @return Boolean
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
// instanceof is false if the instance is null
if (!(obj instanceof Order)) {
return false;
}
return getId() != null && Objects.equals(getId(), ((Order) obj).getId());
}
/**
* This `hashCode` implementation is specific for JPA entities and uses a
* fixed `int` value to be able to identify the entity in collections after
* a new id is assigned to the entity, following the article in
* https://vladmihalcea.com/2016/06/06/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/
*
* @return Integer
*/
public int hashCode() {
return 31;
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getVersion() {
return this.version;
}
public void setVersion(Integer version) {
this.version = version;
}
public Product getProduct() {
return this.product;
}
public void setProduct(Product product) {
this.product = product;
}
public void addToProduct(Product product) {
if (product == null) {
removeFromProduct();
} else {
this.product = product;
product.setOrder(this);
}
}
public void removeFromProduct() {
if (this.product != null) {
product.setOrder(null);
}
this.product = null;
}
}
默认情况下,Select2DataWithConversion
数据类型仅 returns 将设置为 option
元素的 value
属性的标识符和对象的表示(在您的例子中是产品名称)作为 option
元素的 text
属性。
这是构建select2组件所需的最少信息。
https://select2.org/data-sources/formats
但是,正如您在回答中所述,在 Select2 组件中需要更多信息是很常见的。出于这个原因,我们重载了 Select2DataWithConversion
的构造函数,包括一个布尔参数 return 对象的全部信息。
在这里检查这个重载的构造函数:
所以,你只需要改变你的ProductsCollectionThymeleafController.java来使用它:
Select2DataSupport<Product> select2Data = new Select2DataWithConversion<Product>(products, idExpression, getConversionService(), true);
现在您的 select2 组件将接收额外信息,您需要在选项创建期间将其存储在 select2 选项的 data-*
属性中。为此,请使用提供 select2 组件的 templateSelection
函数。
https://select2.org/programmatic-control/retrieving-selections#using-a-jquery-selector
现在,您的 doCalculation
应该获得所选的选项,然后是 data-price
属性。
<script>
function doCalculation() {
var price = $('#product').find(':selected').data('price');
alert("price: " + price);
//Do some calculation
}
doCalculation();
</script>
仅此而已!
编辑:我刚刚创建了以下项目,您可以在其中找到您想要的行为:https://github.com/jcagarcia/proofs/tree/master/select2-with-extra-info
只需检查以下提交中的必要更改:https://github.com/jcagarcia/proofs/commit/105c18f7ad0da4d1e2089fbf71d4f27ccdb60689
希望对您有所帮助,
在自动完成中,我得到了预期的产品名称。
我想根据所选产品做一些计算。但是在 doCalculation 函数中,我得到的是 id
而不是 'price'。所以计算没有按预期工作。
假设如果我更改 String idExpression = "#{price}";
,则计算会按预期进行,但订单未保存。由于出现如下错误
Failed to convert property value of type [java.lang.String] to required type [com.myapp.domain.Product] for property product; nested exception is
org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@javax.persistence.OneToOne
@io.springlets.format.EntityFormat com.myapp.domain.Product] for value 2500; nested exception is java.lang.IllegalStateException: Parsers are not allowed to return null: io.springlets.format.EntityParser@2201ba1c
所以我想在获取价格的同时进行计算,保存功能不应该被破坏。现在第一个或第二个对我有用。
ProductsCollectionThymeleafController.java
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE, name = "select2", value = "/s2")
@ResponseBody
public ResponseEntity<Select2DataSupport<Product>> select2(GlobalSearch search, Pageable pageable,
Locale locale) {
Page<Product> products = getProductService().findAll(search, pageable);
String idExpression = "#{id}";
Select2DataSupport<Product> select2Data =
new Select2DataWithConversion<Product>(products, idExpression, getConversionService());
return ResponseEntity.ok(select2Data);
}
OrderCollectionThymeleafController.java
@PostMapping(name = "create")
public ModelAndView create(@Valid @ModelAttribute Order order, BindingResult result,
Model model) {
if (result.hasErrors()) {
populateForm(model);
return new ModelAndView("/order/create");
}
Order newOrder = getOrderService().save(order);
UriComponents showURI = getItemLink().to(OrderItemThymeleafLinkFactory.SHOW)
.with("order", newOrder.getId()).toUri();
return new ModelAndView("redirect:" + showURI.toUriString());
}
orderview.html
<form class="form-horizontal validate" method="POST" data-th-object="${order}" data-th-action="@{${collectionLink.to('create').with('order', order.id)}}">
<fieldset id="containerFields">
<div class="form-group has-error has-feedback" data-z="3c00987d" id="servicio-product-field" data-th-classappend="${#fields.hasErrors('product')}? 'has-error has-feedback'" data-th-class="form-group" data-th-with="collectionLink=${@linkBuilder.of('ProductsCollectionThymeleafController')}">
<label for="product" class="col-md-3 control-label" data-th-text="#{label_servicio_product}">Product</label>
<div class="col-md-6">
<!-- Select2 -->
<select data-th-field="*{product}" onChange="doCalculation()" class="form-control dropdown-select-ajax" data-allow-clear="true" data-data-ajax--url="${collectionLink.to('select2')}" data-ajax--cache="true" data-ajax--delay="250" data-ajax--data-type="json" data-data-placeholder="#{info_select_an_option}">
<option data-th-unless="*{product} == null" data-th-value="*{product.id}" data-th-text="*{{product}}" selected="selected">Product</option>
</select>
<span data-th-classappend="${#fields.hasErrors('product')}? 'glyphicon glyphicon-remove form-control-feedback'" class="glyphicon glyphicon-remove form-control-feedback" data-th-if="${#fields.hasErrors('product')}" aria-hidden="true"></span>
<span id="product-error" class="help-block" data-th-if="${#fields.hasErrors('product')}" data-th-errors="*{product}">Error message.</span>
</div>
</div>
<script>
function doCalculation() {
var price = document.getElementById("product").value;
alert("price: " + price);
// Do some calculation
}
doCalculation();
</script>
</fieldset>
</form>
Product.java
@RooJavaBean
@RooToString
@RooJpaEntity
@RooEquals(isJpaEntity = true)
@Entity
@EntityFormat
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String productName;
@Min(1L)
@NumberFormat
private Integer price;
@OneToOne(fetch = FetchType.LAZY)
@EntityFormat
private Order order;
public static final String ITERABLE_TO_ADD_CANT_BE_NULL_MESSAGE = "The given Iterable of items to add can't be null!";
public static final String ITERABLE_TO_REMOVE_CANT_BE_NULL_MESSAGE = "The given Iterable of items to add can't be null!";
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getPrice() {
return this.price;
}
public void setPrice(Integer price) {
this.price = price;
}
public String getProductName() {
return this.productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public Order getOrder() {
return this.order;
}
public void setOrder(Order order) {
this.order= order;
}
}
Order.java
@RooJavaBean
@RooToString
@RooJpaEntity
@RooEquals(isJpaEntity = true)
@Entity
@EntityFormat
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Version
private Integer version;
@OneToOne(cascade = { javax.persistence.CascadeType.MERGE,
javax.persistence.CascadeType.PERSIST }, fetch = FetchType.LAZY, mappedBy = "order")
@RooJpaRelation(type = JpaRelationType.AGGREGATION)
@EntityFormat
private Product product;
public static final String ITERABLE_TO_ADD_CANT_BE_NULL_MESSAGE = "The given Iterable of items to add can't be null!";
public static final String ITERABLE_TO_REMOVE_CANT_BE_NULL_MESSAGE = "The given Iterable of items to add can't be null!";
/**
* This `equals` implementation is specific for JPA entities and uses the
* entity identifier for it, following the article in
* https://vladmihalcea.com/2016/06/06/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/
*
* @param obj
* @return Boolean
*/
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
// instanceof is false if the instance is null
if (!(obj instanceof Order)) {
return false;
}
return getId() != null && Objects.equals(getId(), ((Order) obj).getId());
}
/**
* This `hashCode` implementation is specific for JPA entities and uses a
* fixed `int` value to be able to identify the entity in collections after
* a new id is assigned to the entity, following the article in
* https://vladmihalcea.com/2016/06/06/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/
*
* @return Integer
*/
public int hashCode() {
return 31;
}
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public Integer getVersion() {
return this.version;
}
public void setVersion(Integer version) {
this.version = version;
}
public Product getProduct() {
return this.product;
}
public void setProduct(Product product) {
this.product = product;
}
public void addToProduct(Product product) {
if (product == null) {
removeFromProduct();
} else {
this.product = product;
product.setOrder(this);
}
}
public void removeFromProduct() {
if (this.product != null) {
product.setOrder(null);
}
this.product = null;
}
}
默认情况下,Select2DataWithConversion
数据类型仅 returns 将设置为 option
元素的 value
属性的标识符和对象的表示(在您的例子中是产品名称)作为 option
元素的 text
属性。
这是构建select2组件所需的最少信息。
https://select2.org/data-sources/formats
但是,正如您在回答中所述,在 Select2 组件中需要更多信息是很常见的。出于这个原因,我们重载了 Select2DataWithConversion
的构造函数,包括一个布尔参数 return 对象的全部信息。
在这里检查这个重载的构造函数:
所以,你只需要改变你的ProductsCollectionThymeleafController.java来使用它:
Select2DataSupport<Product> select2Data = new Select2DataWithConversion<Product>(products, idExpression, getConversionService(), true);
现在您的 select2 组件将接收额外信息,您需要在选项创建期间将其存储在 select2 选项的 data-*
属性中。为此,请使用提供 select2 组件的 templateSelection
函数。
https://select2.org/programmatic-control/retrieving-selections#using-a-jquery-selector
现在,您的 doCalculation
应该获得所选的选项,然后是 data-price
属性。
<script>
function doCalculation() {
var price = $('#product').find(':selected').data('price');
alert("price: " + price);
//Do some calculation
}
doCalculation();
</script>
仅此而已!
编辑:我刚刚创建了以下项目,您可以在其中找到您想要的行为:https://github.com/jcagarcia/proofs/tree/master/select2-with-extra-info 只需检查以下提交中的必要更改:https://github.com/jcagarcia/proofs/commit/105c18f7ad0da4d1e2089fbf71d4f27ccdb60689
希望对您有所帮助,