【问题标题】:Setting bean's attribute设置bean的属性
【发布时间】:2019-03-23 10:27:07
【问题描述】:

我正在使用 JSF 构建一个简单的电子商店。有一个包含所有产品列表的页面 (product-list.xhtml),然后是每个产品的详细信息页面 (product.xhtml)。产品列表.xhtml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
<h:head>

</h:head>
<h:body>
    <f:view>
        <h:outputText value="Number of items in the cart: #{shoppingCart.numberOfSelectedProducts}"/>
        <h:link value="Cart" outcome="cart.xhtml"/>

        <h:dataTable value="#{productController.products}" var="p">
            <h:column>
                #{p.name}
                <h:commandLink action="product.xhtml" value="Detail">
                    <f:actionListener target="#{product.id}" value="#{p.id}"/>
                </h:commandLink>
            </h:column>

            <h:column>

            </h:column>
        </h:dataTable>
    </f:view>
</h:body>

</html>

product.xhtml 如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
<head></head>
<!--f:metadata>
    <f:event type="preRenderView" listener="#{shoppingCart.loadSelectedProduct(param.id)}"/>
</f:metadata -->
<body>
    <f:view>
        <h:graphicImage value="resources/images/img.jpg"/>
        <h:outputLabel value="The value of shoppingCart.selectedProduct is: #{shoppingCart.selectedProduct.id}"/>
    </f:view>
</body>
</html>

当我在调试器中运行应用程序时,该值已正确设置为 selectedProduct 属性,但随后无法在页面中访问 - product.xhtml 的输出如下所示(如果所选产品的 ID 为 4):

The value of shoppingCart.selectedProduct is:

主页上的产品是从 RequestScoped bean 的 (productController) 属性加载的。 ShoppingCart 是一个带有注入 productController 实例的 SessionScoped bean。

package main.java;

import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import java.io.Serializable;
import java.util.List;

@Named
@SessionScoped
public class ShoppingCart implements Serializable {

    private List<Product> selectedProducts;
    private int numberOfSelectedProducts;
    private Product selectedProduct;

    public Product getSelectedProduct() {
        return selectedProduct;
    }

    public void setSelectedProduct(Product selectedProduct) {
        this.selectedProduct = selectedProduct;
    }

    public List<Product> getSelectedProducts() {
        return selectedProducts;
    }

    public void setSelectedProducts(List<Product> selectedProducts) {
        this.selectedProducts = selectedProducts;
    }

    public int getNumberOfSelectedProducts() {
        return numberOfSelectedProducts;
    }

    public void setNumberOfSelectedProducts(int numberOfSelectedProducts) {
        this.numberOfSelectedProducts = numberOfSelectedProducts;
    }
}
import javax.annotation.PostConstruct;
import javax.faces.view.ViewScoped;
import javax.inject.Inject;
import javax.inject.Named;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

@ViewScoped
@Named
public class ProductController implements Serializable {

    private List<Product> products;
    @Inject
    private ShoppingCart shoppingCart;

    public void setProducts(List<Product> products) {
        this.products = products;
    }

    public ShoppingCart getShoppingCart() {
        return shoppingCart;
    }

    public void setShoppingCart(ShoppingCart shoppingCart) {
        this.shoppingCart = shoppingCart;
    }

    public List<Product> getProducts() {
        return products;
    }

    //v teto metode se pote budou nacitat produkty z DB
    @PostConstruct
    public void loadItems(){
        products = new ArrayList<>();
        products.add(new Product(1, "Nazev1", 11, "Popis1"));
        products.add(new Product(2, "Nazev2", 22, "Popis2"));
        products.add(new Product(3, "Nazev3", 33, "Popis3"));
        products.add(new Product(4, "Nazev4", 44, "Popis4"));
        products.add(new Product(5, "Nazev5", 55, "Popis5"));
        products.add(new Product(6, "Nazev6", 66, "Popis6"));
        products.add(new Product(7, "Nazev7", 77, "Popis7"));
    }

    public Product findById(final int id) {
        List<Product> product = products.stream().filter(p -> p.getId() == id).limit(1).collect(Collectors.toList());
        return product.get(0);
    }
}
package main.java;

import javax.annotation.PostConstruct;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
import java.io.Serializable;

@Named
@ViewScoped
public class Product implements Serializable {
    private int id;
    private String name;
    private double price;
    private String description;

    public Product(int id, String name, double price, String description) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.description = description;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @PostConstruct
    public void init(){
        System.out.println("Product created");
    }
}

【问题讨论】:

  • 请编辑您的问题以提供minimal reproducible example
  • 添加了更多信息。
  • 关于您上次编辑@Spasitel,您可能建议使用f:setPropertyActionListenerf:actionListener 没有属性targetvalue

标签: jsf cdi


【解决方案1】:

如果您像这样向 @SessionScoped bean 添加 @PostConstruct 方法:

@PostConstruct
public void init() {
    System.out.println("ShoppingCart.init()");
}

您会注意到每个请求都会实例化您的 bean 多次 - 每个 #{shoppengCart...} 表达式至少实例化一次。这是因为在 CDI 环境中使用已弃用的注解 @javax.faces.bean.SessionScoped 的行为类似于 @NoneScoped

您应该使用javax.enterprise.context.SessionScoped 作为您的购物车。

为了显示所选产品,我建议添加 @ViewScoped bean(javax.faces.view.ViewScopednot javax.faces.bean.ViewScoped)甚至 @RequestScope,如果你不要做AJAX的东西。 (javax.enterprise.context.RequestScoped不是 javax.faces.bean.RequestScoped)。

您使用哪个 IDE - 它没有通知您您使用的 sope 已弃用吗?

更多阅读请见:How to choose the right bean scope?

除了作用域问题,你可能应该使用f:viewParam而不是f:event将URL查询参数注入bean:

代替:

<f:metadata>
    <f:event type="preRenderView" listener="#{shoppingCart.loadSelectedProduct(param.id)}"/>
</f:metadata>

这样做:

<f:metadata>
     <f:viewParam name="id" value="#{productDisplayBean.selectedProductId}"/>
</f:metadata>

【讨论】:

  • @Kukeltje 我没能找到一个 wiki 风格的 Q/A 来解释在跳转到 CDI 时必须在 @ManagedBean 旁边迁移范围 -> @Named。你找到一个吗?或者它可能值得写一个?这不是一个常见但反复出现的问题。
  • 谢谢。我已经更改了范围以及 product-list.xhtml 中的代码,以改用 Product bean。但是,该链接不起作用。当我在调试器中运行代码时,product.id 的设置器永远不会被调用。
  • @Spasitel f:actionListener 没有属性target 也没有value。从编辑#4开始,您已经污损了您的问题以提出新问题。顺便说一句:我编辑了我的答案,添加了一个提示,说明如何将 URL 参数传递到 bean 属性中。
猜你喜欢
  • 2012-08-04
  • 1970-01-01
  • 2011-06-03
  • 1970-01-01
  • 2014-11-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多