CDI params in @PostConstruct
Asked Answered
S

2

2

I think my question is similar to this but haven't found it to work

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

Why can't I do this with @Named and utilize CDI:

@Named
@RequestScoped
public class DetailsBean {

    private Contacts detailsContact;
    @EJB
    ContactsFacade contactsEJB;
    private int id;

    public DetailsBean() {

        System.out.println("details bean called");
    }

    @PostConstruct
    public void onLoad() {
        detailsContact = contactsEJB.find(id);

}

I'm not able to log the id.

Of course, @ManagedProperty is incompatible with CDI.

*****UPDATE*****

some xhtml:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets"
                template="../template.xhtml"
                xmlns:h="http://java.sun.com/jsf/html"
                xmlns:p="http://primefaces.prime.com.tr/ui"
                xmlns:f="http://java.sun.com/jsf/core">

    <ui:define name="head">
        <f:metadata>               
            <f:viewParam name="paginator" value="#{contactsBean.contactsTablePaginator}"/>
            <f:viewParam name="rows" value="#{contactsBean.contactsTableRows}"/>
        </f:metadata>
    </ui:define>

    <ui:define name="content">
        <p:growl id="growl" showDetail="true"/>

        <p:panel id="contactsPanel" >

            <h:form id ="contactsForm">

                <p:dataTable id="contactsTable" value="#{contactsBean.contacts}" selection="#{detailsBean.detailsContact}" var="contacts" widgetVar="contactsTable"
                             selectionMode="single" rowSelectListener="#{contactsBean.rowSelect}" rowUnselectListener="#{contactsBean.rowUnSelect}"
                             onRowUnselectUpdate="detailsForm" onRowSelectUpdate="detailsForm"
                             paginator="#{contactsBean.contactsTablePaginator}" rows="#{contactsBean.contactsTableRows}" rowsPerPageTemplate="5,10,15,25,50,100"
                             paginatorTemplate="{RowsPerPageDropdown} {FirstPageLink} {PreviousPageLink} {CurrentPageReport} {NextPageLink} {LastPageLink}">

                    <f:facet name="header">                     
                        <p:outputPanel>
                            <h:outputText value="Search:" />
                            <p:inputText id="globalFilter" onkeyup="contactsTable.filter()"  style="width:150px" />                
                        </p:outputPanel>
                    </f:facet>

                    <p:column filterStyle="display:none"  filterBy="#{contacts.name}" headerText="Name" style="width:200px">
                        <h:outputText value="#{contacts.name}" />
                    </p:column>

                    <p:column filterStyle="display:none" filterBy="#{contacts.street}" headerText="Street" style="width:280px">
                        <h:outputText value="#{contacts.street}" />                     
                    </p:column>

                    <p:column filterStyle="display:none" filterBy="#{contacts.city}" headerText="City" style="width:150px">
                        <h:outputText value="#{contacts.city}" />
                    </p:column>

                    <p:column filterStyle="display:none" filterBy="#{contacts.state}" headerText="State" style="width:50px">
                        <h:outputText value="#{contacts.state}" />
                    </p:column>

                    <p:column filterStyle="display:none" filterBy="#{contacts.zip}" headerText="Zip" style="width:100px">
                        <h:outputText value="#{contacts.zip}" />
                    </p:column>

                    <p:column filterStyle="display:none" filterBy="#{contacts.country}" headerText="Country" style="width:150px">
                        <h:outputText value="#{contacts.country}" />
                    </p:column>

                    <p:column filterStyle="display:none" filterBy="#{contacts.sent}" headerText="Sent" style="width:50px">
                        <h:outputText value="#{contacts.sent}" />
                    </p:column>                 

                </p:dataTable>

                <p:ajaxStatus >

                    <f:facet name="start">
                        <h:graphicImage value="../resources/images/ajax-loader-bar.gif" />
                    </f:facet>

                    <f:facet name="complete">
                        <h:graphicImage value="../resources/images/ajax-loader-bar-still.gif" />
                    </f:facet>

                    <f:facet name="default">
                        <h:graphicImage value="../resources/images/ajax-loader-bar-still.gif" />
                    </f:facet>
                </p:ajaxStatus>
                <br />

                <p:commandLink value="View All" action="#{contactsBean.viewAll}"   /> &nbsp;
                <p:commandLink value="Default View" action="#{contactsBean.viewDefault}"  /> &nbsp;
                <p:commandLink value="Advanced Search" action="search?faces-redirect=true" />

                <br />           

            </h:form>

            <br />

        </p:panel>

        <br />
        <br />

        <h:form id="detailsForm">

            <p:panel id="detailsPanel" visible="#{detailsBean.visible}" >

                <h:panelGrid id="detailsPanelGrid" cellpadding="2" cellspacing="2" columns="3"   >

                    <h:outputText value="Name :" />
                    <p:inputText id="name" value="#{detailsBean.detailsContact.name}" style="width:400px" />
                    <p:message for="name" />

                    <h:outputText value="Email :" />
                    <p:inputText id="email" value="#{detailsBean.detailsContact.email}" style="width:400px" validatorMessage="Must be a valid email address. EX: [email protected]"  >
                        <f:validateRegex pattern="[a-zA-Z0-9]+@[a-zA-Z0-9]+\.[a-zA-Z0-9]+"/>
                        <p:ajax event="blur" update="emailMsg" />
                    </p:inputText>
                    <p:message id="emailMsg" for="email" />

                    <h:outputText value="Street :" />
                    <p:inputText id="street" value="#{detailsBean.detailsContact.street}" style="width:400px" />
                    <p:message for="street" />

                    <h:outputText value="City :" />
                    <p:inputText id="city" value="#{detailsBean.detailsContact.city}" style="width:400px" />
                    <p:message for="city" />

                    <h:outputText value="State :" />
                    <p:inputText id="state" value="#{detailsBean.detailsContact.state}" style="width:400px" validatorMessage="Length is greater than 2" >
                        <f:validateLength maximum="2" />
                        <p:ajax event="blur" update="stateMsg" />
                    </p:inputText>
                    <p:message id="stateMsg" for="state" />

                    <h:outputText value="Country :" />
                    <p:inputText id="country" value="#{detailsBean.detailsContact.country}" style="width:400px" />
                    <p:message for="country" />

                    <h:outputText value="Phone :" />
                    <p:inputText id="phone" value="#{detailsBean.detailsContact.phone}" style="width:400px"/>
                    <p:message for="phone" />

                    <h:outputText value="Guests :" />
                    <p:inputText id="guests" value="#{detailsBean.detailsContact.guests}" style="width:400px"/>
                    <p:message for="guests" />

                    <h:outputText value="Arrival :" />
                    <p:calendar id="arrival" value="#{detailsBean.detailsContact.arrival}" showOn="button" />
                    <p:message for="arrival" />

                    <h:outputText value="Departure :" />
                    <p:calendar id="departure" value="#{detailsBean.detailsContact.departure}" showOn="button" />
                    <p:message for="departure" />

                    <h:outputText value="Message :" />
                    <p:inputTextarea id="message" effectDuration="30" style="width:400px;height:100px;" value="#{detailsBean.detailsContact.message}"  />
                    <p:message for="message" />

                    <h:outputText value="Departure :" />
                    <p:calendar id="inserted" value="#{detailsBean.detailsContact.inserted}" showOn="button"/>
                    <p:message for="inserted" />

                    <h:outputText value="Sent :" />
                    <h:selectBooleanCheckbox id="sent" value="#{detailsBean.detailsContact.sent}" />
                    <p:message for="sent" />
                    <br />

                </h:panelGrid>

                <p:commandButton value="Submit" action="#{detailsBean.updateContactDetails}"  update="contactsForm, growl, stateMsg" />
                <p:commandButton value="Close" action="#{detailsBean.handleClose}" update="contactsForm, detailsForm" />

            </p:panel>

        </h:form>

    </ui:define>

</ui:composition>

more java code:

package com.atlanticpkg.view.beans;

import com.atlanticpkg.model.entities.Contacts;
import com.atlanticpkg.util.FacesUtils;
import java.io.Serializable;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.context.SessionScoped;
import javax.inject.Named;
import javax.faces.application.FacesMessage;
import javax.faces.context.FacesContext;
import javax.inject.Inject;

@Named(value = "detailsBean")
@RequestScoped
public class EditBean {

    private Contacts detailsContact;
    private boolean visible = false;
    @Inject
    ContactsBean contactsBean;

    public EditBean() {
    }

    @PostConstruct
    public void onLoad() {
    }

    public void handleClose() {

        this.setVisible(false);
        this.setDetailsContact(new Contacts());
    }

    public void updateContactDetails() {

        try {

            contactsBean.getContactsEJB().edit(detailsContact);
            FacesUtils.addMessage(detailsContact.getName() + " was updated successfully!");

        } catch (Exception e) {
            FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(FacesMessage.SEVERITY_ERROR, "ERROR", e.toString()));
        }

    }
}

and even more:

@Named(value = "contactsBean")
@RequestScoped
public class ContactsBean {

    @Inject
    EditBean editBean;
    @EJB
    private ContactsFacade contactsEJB;
    private List<Contacts> contacts = new ArrayList<Contacts>();
    private boolean contactsTablePaginator = true;
    private int contactsTableRows = 10;
    private Contacts selectedContact = new Contacts();

    public ContactsBean() {
    }

    @PostConstruct
    public void onLoad() {

        updateContactsList();
    }

    public String viewDefault() {

        contactsTablePaginator = true;
        contactsTableRows = 10;
        return "index?faces-redirect=true&includeViewParams=true";
    }

    public String viewAll() {

        contactsTablePaginator = false;
        contactsTableRows = 100;
        return "index?faces-redirect=true&includeViewParams=true";
    }

    public void updateContactsList() {

        contacts.clear();
        contacts = contactsEJB.findAll();
    }

    public void rowSelect(SelectEvent event) {

        editBean.setVisible(true);
        editBean.setDetailsContact((Contacts) event.getObject());
    }

    public void rowUnSelect(UnselectEvent event) {

        editBean.setVisible(false);
        editBean.setDetailsContact(new Contacts());

    }
}

The inputText boxes populate fine. But soon as I hit submit it says that the values are null. This code works perfectly with SessionScope.

WARNING: /admin/index.xhtml @104,109 value="#{detailsBean.detailsContact.name}": Target Unreachable, 'null' returned null javax.el.PropertyNotFoundException: /admin/index.xhtml @104,109 value="#{detailsBean.detailsContact.name}": Target Unreachable, 'null' returned null

I can see that it's calling the EditBean when I select the data table. It then calls it again when I click the submit button.

Strade answered 14/2, 2011 at 4:43 Comment(3)
I believe @PostConstruct would get called only at the bean initialization phase. Therefore your onLoad() method is probably not getting invoked.Babe
Related: stackoverflow.com/questions/4888942/…Emergence
The problem sounds like sometimes the values are null, sometimes not, so it appears that the bean isn't, perhaps, loading the parameters when desired.Wynne
F
4

The lifecycle doesn't allow your approach.

At first, the bean is created ( constructor ). After that, there is executed dependency injection which is followed by @PostConstruct method and after that the JSF file is evaluated. And the viewParam is in that file. So you have to register another listener which is called after filling view params.

I have a solution for @RequestScope beans, but if the bean's scope is longer ( like View ) then this method is executed after each request ( including AJAX ) which is not probably desired.

Use this for request scope beans:

<f:metadata>
   <f:viewParam id="id" name="id" value="#{detailsBean.id}"/>
   <f:event type="preRenderView" listener="#{detailsBean.onLoad}" />
</f:metadata>

For @ViewScope beans I am using this "hack" which works but probably is not best practise. It does same thing but probably it isn't the correct approach.

#{detailsBean.onLoad()}
<f:metadata>
   <f:viewParam id="id" name="id" value="#{detailsBean.id}"/>
</f:metadata>

I hope that this is helpful for you.


EDIT:

you are using a lot of AJAX here. This calls have to land in at least ViewScoped beans. View Scope is similar to RequestScope, but it takes a quite longer - til the page is left.

But I haven't read it all, there is a lot of code and if the ViewScope doesn't help then maybe you should provide the small piece of problematic code to be chance there to find and focus on the real problem.

Fevre answered 14/2, 2011 at 9:53 Comment(6)
Thanks for that. So it seems viewParams are more for SessionScoped beans? This is interesting because I was thinking the exact opposite. I was trying to have a new bean rendered for every page and nothing kept from page to page. I was thinking request scope was the best way to do this. I guess not. It still didn't fix my other problem where detailsContact is null when I go to update it. It's like as soon as hit the submit button it creates a new detailsContact. I thought request was a little longer than that.Strade
@dham: I think that the best scope for pages is request or view ( when you are using AJAX ). Longer scope is redundant. Using event listener with RequestScope is good. And what is your another problem? I cannot find it explained. It would be nice if you can show us some code and describe it because I don't understand it.Fevre
Hi I will update my post with the problem. I have datatable then I select a row and that takes me to another page. I use a request scope to use an EJB to populate the inputText fields. I'm using the listener method that you have showed me. The fields populate fine but when I go to submit the changes I get an error that the fields are actually null. It's very weird. I also have a the same problem even if I don't go to another page to edit.Strade
Is it possible to use ViewScoped with Named annotations? Doesn't seem to be working with that. I'm guessing I'll have to use ManagedBean javax.faces annotations.Strade
@dham I don't know, I always use ManagedBean with property nameFevre
@ViewScoped doesn't work with CDI beans(@Named). Oracle simply forgot this, but Seam3 portable extensions can add this again. Furthermore, in your preRenderView method use FacesContext.isPostback() to do actions only once for @ViewScoped beans.Belvabelvedere
H
7

I encountered the exact same problem than you, and solved it by using the external context (containing GET parameters) instead of f:viewParam.

In your @PostConstruct method, just get your parameter with something like

FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap();
Halfslip answered 25/1, 2012 at 13:33 Comment(0)
F
4

The lifecycle doesn't allow your approach.

At first, the bean is created ( constructor ). After that, there is executed dependency injection which is followed by @PostConstruct method and after that the JSF file is evaluated. And the viewParam is in that file. So you have to register another listener which is called after filling view params.

I have a solution for @RequestScope beans, but if the bean's scope is longer ( like View ) then this method is executed after each request ( including AJAX ) which is not probably desired.

Use this for request scope beans:

<f:metadata>
   <f:viewParam id="id" name="id" value="#{detailsBean.id}"/>
   <f:event type="preRenderView" listener="#{detailsBean.onLoad}" />
</f:metadata>

For @ViewScope beans I am using this "hack" which works but probably is not best practise. It does same thing but probably it isn't the correct approach.

#{detailsBean.onLoad()}
<f:metadata>
   <f:viewParam id="id" name="id" value="#{detailsBean.id}"/>
</f:metadata>

I hope that this is helpful for you.


EDIT:

you are using a lot of AJAX here. This calls have to land in at least ViewScoped beans. View Scope is similar to RequestScope, but it takes a quite longer - til the page is left.

But I haven't read it all, there is a lot of code and if the ViewScope doesn't help then maybe you should provide the small piece of problematic code to be chance there to find and focus on the real problem.

Fevre answered 14/2, 2011 at 9:53 Comment(6)
Thanks for that. So it seems viewParams are more for SessionScoped beans? This is interesting because I was thinking the exact opposite. I was trying to have a new bean rendered for every page and nothing kept from page to page. I was thinking request scope was the best way to do this. I guess not. It still didn't fix my other problem where detailsContact is null when I go to update it. It's like as soon as hit the submit button it creates a new detailsContact. I thought request was a little longer than that.Strade
@dham: I think that the best scope for pages is request or view ( when you are using AJAX ). Longer scope is redundant. Using event listener with RequestScope is good. And what is your another problem? I cannot find it explained. It would be nice if you can show us some code and describe it because I don't understand it.Fevre
Hi I will update my post with the problem. I have datatable then I select a row and that takes me to another page. I use a request scope to use an EJB to populate the inputText fields. I'm using the listener method that you have showed me. The fields populate fine but when I go to submit the changes I get an error that the fields are actually null. It's very weird. I also have a the same problem even if I don't go to another page to edit.Strade
Is it possible to use ViewScoped with Named annotations? Doesn't seem to be working with that. I'm guessing I'll have to use ManagedBean javax.faces annotations.Strade
@dham I don't know, I always use ManagedBean with property nameFevre
@ViewScoped doesn't work with CDI beans(@Named). Oracle simply forgot this, but Seam3 portable extensions can add this again. Furthermore, in your preRenderView method use FacesContext.isPostback() to do actions only once for @ViewScoped beans.Belvabelvedere

© 2022 - 2024 — McMap. All rights reserved.