0
votes

First of all I want to say that I'm new in JSF.

I want to create simple composite component which will be available of editing articles. It should work in this way:

  1. Composite component looks like this <my:article article="#{interestedBean.article}" />
  2. ArticleBean is responsible for procesing data of composite component (here is save method)
  3. Each page which will want to use article need to add composite component to the view and Article object to the backing bean
  4. Article object will be passed to the composite component and its value will be changed in ArticleBean

The problem is that I don't know how to pass entity (Article object) from one bean (interested bean) to another (ArticleBean) through view.

Example (pseudocode; Lets imagine that Article entity is simple String object so we don't need to use converter):

// input bean
public class HomePageBean {
    private Article article;
    @PostConstruct
    public void init() {
        this.article = new Article();
        this.article.setText("welcome on home page");
    }
    public void setArticle(Article article) {
        this.article = article;
    }
    public Article article() {
        return article; // on real page article will be taken from database
    }
}

// view
<h:form>
    <h:outputText value="#{articleBean.article.text}">
        <f:attribute name="value" value="#{homePageBean.article.text}" />
    </h:outputText>
</h:form>

// output bean
public class ArticleBean {
    private Article article;
    public void setArticle(Article article) {
        this.article = article;
    }
    public Article getArticle() {
        return article;
    }
    public void save() {
         // save article data to database
    }
}

// entity
public class Article {
    private article text;
    public String getText() {
        return text;
    }
    public void setText(String text) {
        this.text = text;
    }
}

The problem is that SecondBean.entity.text value is not set. How can I pass parameter to backing bean from view? I was trying to set Article value using @ManagedProperty(value="#{param.article}") but <h:outputText> is in form so the value is passed as randomformname:article.

Sorry for my English

1
Welcome to stackoverflow! Be sure to understand the basic functioning of JSF and communication in the framework before moving on to composite components. An extremely valuable assets are the Java EE tutorial and BalusC tutorial, which is essentially an overview of many JSF features.skuntsel

1 Answers

2
votes

Actually the question needs clarification. So I've got two basic ideas on what you might want to do.

Simple sharing of information on one view

If you want to share information on the same view you can inject managed beans in each other with @ManagedProperty. Just remember that injected bean whould not have a lesser scope than the bean it is injected into. Actually, if all you need is an article object then I don't see the reason to use two managed beans in your situation, but still, you can do it with

@ManagedBean
@RequestScoped
public class BaseBean {

    private Article article;

    @ManagedProperty(value="#{injectedBean}")
    private InjectedBean injectedBean;

}

To initialize both properties of the managed beans that you want to be the same, you can use the init method annotated with @PostConstruct (but there are many alternatives), like in here

@PostConstruct
public void init() {
    Article article = new Article("Welcome");
    this.article = article;
    injectedBean.setArticle(article);
}

Share information between views

In this scenario the user selects an article he wants to edit on the first view and passes it around for the second view. In this scenario a user selectes an article he wants to manipulate on one page (welcome.xhtml) and the actual manipulation happens on the other page (manipulation.xhtml).

The common approach is that each page is baced up by its own bean, so in the abovementioned setup the 'base bean' will lose its injection. So, welcome.xhtml view will use some article objects that popped up on that page from somewhere and pass it for manipulation to the second view on button click.

The beans will be the same with reagrd to the article object.

@ManagedBean
@RequestScoped
public class BaseBean {

    private Article article;

}

@ManagedBean
@RequestScoped
public class InjectedBean {

    private Article article;

}

And the actual passing will happen during the button click, like in

    <h:form>
        <h:commandButton value="Manipulation" action="manipulation.xhtml">
            <f:setPropertyActionListener target="#{injectedBean.article}" value="#{baseBean.article}"/>
        </h:commandButton>
    </h:form>

In the code, the property action listener method is instantiating a bean for the second view and sets it property with the property of a base bean.

Using get parameters for page loading

Sometimes it makes more sence not to include a command button to trigger navigation to the next view, but provide for a simple link to editing the page. It may be achieved by using <f:param> tag. There are two basic ways of handling parameters in JSF: by using page actions / preRenderView events or by injecting them directly in your managed beans with @ManagedProperty.

  1. Using Page actions / preRenderView events

The job will be done by the target view

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

for preRenderView event and

<f:metadata>
    <f:viewParam id="articleId" name="articleId" value="#{injectedBean.id}" />
    <f:viewAction action="#{injectedBean.initEvent}" />
</f:metadata>

for page action. The managed bean (InjectedBean object) will have the following init method

public void initEvent() {
    if (id == null) {
        String message = "No id specified in request";
        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(message));
        return;
    }
    //use your service method to load the article
    //article = articleService.findById(id);
    //and add messages appropriately
}
  1. Using @ManagedProperty annotation

The job will be done by the following bean method, annotated with @PostConstruct, and parameter dependency injection. Remember, for the setup to work the bean must be @RequestScoped, but there are workaround for other bean scopes.

@ManagedProperty(value="#{param.articleId}")
private Integer id;

@PostConstruct
public void initPostConstruct() {
    if (id == null) {
        String message = "No id specified in request";
        FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(message));
        return;
    }
    //use your service method to load the article
    //article = articleService.findById(id);
    //and add messages appropriately
}

Any way the bean will be initialized during the next view (manipulation.xhtml). Extremely useful comparison of these two ways, provided by BalusC, can be found here.

Navigation to this view may be handled, for example, via a simple <h:link>, like in

<h:link value="Manipulate" outcome="manipulation.xhtml" >
    <f:param name="articleId" value="#{baseBean.article.id}" />
</h:link>