1

I have a user-defined text, such as

@SessionScoped
public class MyBean {
    private String text = "Life’s but a walking shadow, a poor player  
    that struts and frets his hour upon the stage and 
    then is heard no more.";

    public String getText() {
        return text;
    }
}

Of course the text is not static, but will be loaded from somewhere else. I want the text to be displayed one the page, as in

<h:form id="myForm">
    <h:outputText value="#{myBean.text}" />
</h:form>

Now have a logic in the bean which marks certain words, e.g. every noun, in the text. These words should be rendered as links, as if they were commandLinks. That is, the form should be submitted and I should be able to find out which link was clicked. Something similar was already asked here, here and here, but I am not sure if the solutions given there suit my case.


My best guess right now is to split the text at the marked words into a list of snippets in the bean, e.g.

List<TextSnippet> textSnippets;

class TextSnippet {
    private String precedingText;
    private String markedWord;
    ...
}

such that each text snippet ends with a marked word. Then I would be able to iterate over the list in the xhtml, e.g.

<h:form id="myForm">
    <ui:repeat var="snippet" value="#{myBean.textSnippets}">
        <h:outputText value="#{snippet.precedingText}" />
        <h:commandLink action="#{myBean.clickedOn(snippet.markedWord)}">
            <h:outputText value="#{snippet.markedWord}">
        </h:commandLink>
    </ui:repeat>
</h:form>

However, I feel that this tightly couples the bean (logic of splitting) to the view. Any better ideas?

Kukeltje
  • 11,924
  • 4
  • 19
  • 44
Julian Arz
  • 273
  • 4
  • 11

2 Answers2

1

What I would personally do is try to keep the jsf tree small and implement something like the lines below that I think is more performant (disclamer: no full code coming )

  • Prepare the text serverside in a bean as This is a <div class="linkedWord">specific</div> word that needs a link and so is <div="linkedWord">this</div>
  • Output this in plain html via an <h:outputText value="#{myBean.text}"> (for escaping!)
  • Add a jquery dynamic eventhandler on the class="linkedWord" (so it works for each link) and call a javascript function
  • In that javascript function read the content of the div (or maybe add the text as a data- attribute aas well (like <div class="linkedWord" data-word="specific">specific</div>
  • and call a <h:commandScript> (JSF 2.3 and up) or a o:commandScript for previous JSF versions (or the `p:remoteCommand) and pass the content of the div (or the value of the attribute) as a parameter to a serverside method.

Keep in mind that there is no explicit reason to do everything in a 'JSF' way. Using client-side features and some small integration with JSF is very valid usage. People not doing this often 'blame' JSF but they themselves are effectively the cause of the less optimal behaviour)

See also:

Kukeltje
  • 11,924
  • 4
  • 19
  • 44
0

You seem to go the right way, but I think I can suggest you some improvements. Don't know your exact requirements, but your current structure limits the marked word (which I guess acts as a mere link) to be at the end of the paragraph. What would happen if you have text after it? What about having two marked words? This class might suit better:

class TextSnippet {
    private String text;
    private String linkUrl;
    ...
}

You'll need to build the List<TextSnippet> the way you do, but evaluate the links before, so ui:repeat can access them.

Then, iterate over it. Instead of performing a POST to evaluate where to go, you've got it already, so you can use a h:link if you want to point to somewhere in your application or h:outputLink if it's outside it:

<ui:repeat var="snippet" value="#{myBean.textSnippets}">
    <h:outputText value="#{snippet.text}" rendered="#{empty snippet.linkUrl}" />
    <h:link outcome="#{snippet.linkUrl}" rendered="#{not empty snippet.linkUrl}">
        <h:outputText value="#{snippet.text}">
    </h:link>
</ui:repeat>
Xtreme Biker
  • 28,480
  • 12
  • 120
  • 195