0

I have got a composite component:

<cc:interface>
    <cc:attribute name="value" required="true">
</cc:interface>
<cc:implementation>
    <h:outputText value="#{cc.attrs.value}"/>
    <h:commandButton action="#{internalBean.someAction}"/>
</cc:implementation>

And I would like to change the #{cc.attrs.value} by #{internalBean.someAction}, in other words: change the (String) value of user defined (external) bean by a method of my composite component. How I can do it?

Thanks.

Petr Dušek
  • 627
  • 4
  • 12
  • 26
  • I don't think you can. `h:outputText` is expecting a value expression so I don't see how you can use a method expression. Maybe I'm not understanding the question so can you clarify. – Andy Jul 15 '13 at 19:11
  • Ok, I will explain it more exactly: 'value' is a property of an external bean (f.e.: ) and 'someAction()' is a method of my internal bean (inside the composite component). And I would like to get this value inside the method and change it. I may do it with a lookup - FacesContext... – Petr Dušek Jul 15 '13 at 19:24
  • trying to understadn one sec – Andy Jul 15 '13 at 19:26
  • I think that's it right ? What JSF version are you using ? – Andy Jul 15 '13 at 19:30
  • One more question. When you make that change, you also what that change to be seen by the external bean right ? So say external bean had a field with value Peter and when you pass it and change it to a new value say "Truck". the next time you print the value of external bean will it be Truck or Peter ? – Andy Jul 15 '13 at 19:37
  • Truck, because I would like to use it like a normal component (no composite). – Petr Dušek Jul 15 '13 at 19:52
  • ok, let me see what I can do – Andy Jul 15 '13 at 19:54
  • I am using JSF 2.0 - and yes, you are right. I would like to use the composite component with a external bean as a normal component (but I want to modify the values of the bean inside the component - in methods). – Petr Dušek Jul 15 '13 at 20:00
  • Ok, give me 30 minutes. Writing a quick example. – Andy Jul 15 '13 at 20:09
  • Sorry, took slightly longer than expected. Let me know what you think. – Andy Jul 15 '13 at 20:55

3 Answers3

3

UPDATE

One way I can think of is to use <f:setPropertyActionListener>.

<cc:interface>
    <cc:attribute name="value" required="true"/>
</cc:interface>
<cc:implementation>
    <h:outputText value="#{cc.attrs.value}"/>
    <h:commandButton action="#{internalBean.someAction}">
        <f:setPropertyActionListener value="#{cc.attrs.value}" target="#{internalBean.stringValueFromExternalBean}"/>
    </h:commandButton>
</cc:implementation>
Andy
  • 5,900
  • 2
  • 20
  • 29
  • Thanks a lot. But I am afraid, I need another solution, because I am developing a composite component for programmers. So they will implementing the external bean, not me. This means, that I can't set up neither a type of the bean nor a name. Is it possible to do it in some general way via @ManagedProperty? – Petr Dušek Jul 16 '13 at 13:57
  • @PetrDušek I'm not sure. I can try to look. The problem is you want the change to be reflected to the other other so I'm trying to figure out how you might know the object type. – Andy Jul 16 '13 at 14:35
  • The only thing I need is this: pass the String property from an external bean and manipulate it on the internal side. Is this allowed?: @ManagedProperty(value = "#{cc.attrs.value}") private String value; – Petr Dušek Jul 16 '13 at 15:32
  • I don't need to know the object type, I may write it confusing - I only want to deal with the String property. – Petr Dušek Jul 16 '13 at 15:35
  • @PetrDušek Then option #1 should work for you. I mean the one in my answer when you use `f:setPropertyActionListener`. I am passing a String from the external managed bean to the internal one. And you can manipulate it as you want. what is it that you don't like with it ? – Andy Jul 16 '13 at 15:48
  • @PetrDušek I'll expand on it a little more. – Andy Jul 16 '13 at 15:53
  • @PetrDušek What I'm trying is this. If you want to `@ManagedProperty(value = "#{cc.attrs.value}") private String value;` (assuming that's possible). Whatever change you do inside internal bean will not be reflected on external bean. Remember that String is immutable and interned. One you "change" it, external bean will still point to the original value. you could use a `StringBuilder`...Let me test to see if that works. – Andy Jul 16 '13 at 16:01
  • It is not an universal solution. It requires an user-click on the internal component (commandButton). But I need a normal component behaviour: every server side action (so the external action as well) will update the value. – Petr Dušek Jul 16 '13 at 16:02
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/33569/discussion-between-andy-and-petr-dusek) – Andy Jul 16 '13 at 16:05
  • @PetrDušek I'm going to give you the `StringBuilder` option just in case. – Andy Jul 16 '13 at 17:32
  • `StringBuilder` part in answer is confusing and nonsense. Sure, `String` itself is immutable, but bean property not. JSF/EL just calls **setter** with new value. – BalusC Jul 17 '13 at 12:01
  • @BalusC Oh no! I got the two mixed up. Thanks for catching that now I feel so silly. Updating answer. – Andy Jul 17 '13 at 13:52
2

But it is not necessary to use StringBuilder:

 <composite:interface>
        <composite:attribute name="value" required="true"/>
    </composite:interface>
 <cc:implementation>
 ...
 <f:setPropertyActionListener target="#{cc.attrs.value}" value="#{internalBean.value}"/>
...
</cc:implementation>

Where values are normal String. It works fine!

Petr Dušek
  • 627
  • 4
  • 12
  • 26
  • I apologize Petr. Somewhere along the way I failed to see the entire picture (**setter** being called). BalusC explains why this works in the comments. Ignore the `StringBuilder` part (but keep in mind you can use `type` in the future to pass objects other than `String`). Thank you so much for pointing this out and again sorry for the confusion. – Andy Jul 17 '13 at 15:27
  • It is ok, Andy, thanks you for a productive discusion. See you! – Petr Dušek Jul 18 '13 at 07:11
0

I have finally found the best solution ever. It works immediately as a normal component - every change updates the external bean property:

public void setValue(String value) {
    this.value = value;
    FacesContext facesContext = FacesContext.getCurrentInstance();
    ELContext elContext = facesContext.getELContext();
    ValueExpression valueExpression = facesContext.getApplication().getExpressionFactory()
            .createValueExpression(elContext, "#{cc.attrs.value}", String.class);
    valueExpression.setValue(elContext, value);
}
Petr Dušek
  • 627
  • 4
  • 12
  • 26