如何在百里香选择选项中获取对象?

Posted

技术标签:

【中文标题】如何在百里香选择选项中获取对象?【英文标题】:How can get the object in thymeleaf select option? 【发布时间】:2018-03-19 15:04:19 【问题描述】:

在自动完成中,我得到了预期的产品名称。

我想根据所选产品进行一些计算。但在 doCalculation 函数中,我得到的是id 而不是“价格”。所以计算没有按预期工作。

假设如果我更改 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;
    

【问题讨论】:

【参考方案1】:

默认情况下,Select2DataWithConversion 数据类型仅返回将设置为 value 属性的标识符 option 元素和对象的表示形式(在您的情况下为产品名称)为 text option 元素的属性。

这是 select2 组件需​​要构建的最少信息。

https://select2.org/data-sources/formats

但是,正如您在回答中所述,在 Select2 组件中需要更多信息确实很常见。为此,我们重载了Select2DataWithConversion的构造函数,包括一个布尔参数来返回对象的全部信息。

在这里检查这个重载的构造函数:

https://github.com/DISID/springlets/blob/master/springlets-data/springlets-data-commons/src/main/java/io/springlets/data/web/select2/Select2DataWithConversion.java#L76

因此,您只需更改您的 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

希望对你有帮助,

【讨论】:

获取未定义的价格价值 因为在这个 $('#accomidation').find(':selected') 中找不到数据。添加了 console.log(price);并找到了这个 你是如何实现templateSelectionselect2函数的? 我刚刚使用 Spring Roo 创建了一个示例项目,您可以在其中找到解决方案。 github.com/jcagarcia/proofs/tree/master/select2-with-extra-info 只需检查我已应用必要更改的提交即可。 github.com/jcagarcia/proofs/commit/…

以上是关于如何在百里香选择选项中获取对象?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用百里香获取输入用户名的值?

如何设置百里香th:来自其他变量的字段值

如何将对象列表绑定到百里香中的复选框?

如何控制百里香中的href属性

如何在百里香中结合 sec:authorize 和 th:if

如何在百里香中打印数组大小?