0

I have a simple CDI bean. The problem is that when I invoke method removeCustomer from the JSF page, method getCustomers() is executed before removeCustomer instead the opposite way. First I thought that producer itself is the problem but I'm getting same behavior if I put the list inside the controller. When I get response page the row in the table is not removed ether it is removed from the database. When I refresh page once more I don't see the deleted row.

@Named
@RequestScoped
public class CustomersController {

    @Inject
    private CustomerService customerService;

    @Produces()
    @RequestScoped
    @Named("customers")
    public List<Customer> getCustomers() {
        return customerService.findCustomers();
    }

    /**
     * @param customerId
     */
    public String removeCustomer(Long customerId) {
        customerService.deleteCustomer(customerId);
        return null;
    }

}

<h:form id="customers-form">
    <h:dataTable value="#{customers}" var="customer"
    styleClass="table">

    <h:column>
        <f:facet name="header">#{msg['customer.name']}</f:facet>
    #{customer.name}
</h:column>

    <h:column>
        <f:facet name="header">#{msg['customer.vat']}</f:facet>
    #{customer.vat}
</h:column>
    <h:column>
        <a
            href="#{facesContext.externalContext.requestContextPath}/customers/customer.xhtml?id=#{customer.id}">#{msg['global.edit']}</a>
    </h:column>
    <h:column>
        <h:commandButton styleClass="btn btn-danger"
            action="#{customersController.removeCustomer(customer.id)}"
            value="#{msg['global.delete']}" aria-hidden="true">
            <f:ajax render="@form"></f:ajax>
        </h:commandButton>
    </h:column>
</h:dataTable>

I'm using JBoss WildFly beta 1 but I'm guessing that it uses stable JSF 2.x version.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
nikolafon
  • 11
  • 3

2 Answers2

1

Check this answer

The method getCostumers is called before because the EL expresion (#customers) is being evaluated several times while processing the request (more details in the link).

The solution is to make your getters and setters "dumb". Just make them change a variable of your class. Avoid logic with side effects (like customerService.findCustomers();). If you need initialization, you can perform it in a @PostConstruct method or in a method fired by a JSF event).

Also, take into account that with a @RequestScoped bean, a new instance of the bean will be created for every request (either to render the page, or to save changes to the values).

Community
  • 1
  • 1
SJuan76
  • 24,532
  • 6
  • 47
  • 87
  • Thanks. I understand now. But anyway I would like to use request scope. I don't want to use view scope or session scope. How can this be done that way? Is it possible at all? – nikolafon Nov 19 '13 at 07:56
  • Sorry, I concentrated in the "getter is being called" issue and didn't think about the wider problem. Are you sure your `removeCustomer` method is being called at all? Anyway, it looks like you need more a `listener` than an action method (http://stackoverflow.com/questions/12222687/jsf-fajax-listener-vs-commandbutton-action) – SJuan76 Nov 19 '13 at 11:55
0

Actually it is pretty easy to achieve what I want. Here is updated CDI bean.

@Named @RequestScoped public class CustomersController {

@Inject
private CustomerService customerService;
@Inject
private List<Customer> customers;

@Produces()
@RequestScoped
@Named("customers")
public List<Customer> getCustomers() {
    return customerService.findCustomers();
}

/**
 * @param customerId
 */
public void removeCustomer(Customer customer) {
    customerService.deleteCustomer(customer.getId());
    customers.remove(customer);
}

}

As you can see. On ajax request to remove the customer producer is invoked before method removeCustomer. Logically because in restore view phase jsf is restoring view and evaluating EL expressions and this is happening before event processing phase. So we have list of customers in request scope and we need to inject it in controller bean so we can remove from the list selected customer in the removeCustomer method. I hope I was clear enough.

nikolafon
  • 11
  • 3