Avoiding redundant DB queries in JSF and conditionally rendering a ui:repeat - jsf

I am using JSF and Hibernate in my application. Say I have a user account, whose questions I want to display using <ui:repeat>, in a <ul> unordered list. I don't want to render the list if there are no questions, and display "No questions" text instead. The way I currently account is the following:
<ul>
<ui:repeat value="#{user.questions}" var="question">
<li>#{question.text}</li>
</ui:repeat>
</ul>
<h:outputText rendered=#{user.questions.size() == 0}">no questions</h:outputText>
There are two problems with this, the stray <ul> tags if there are no questions.
Should I encapsulate it in another panel with again rendered=#{user.questions.size() > 0} because it seems ui:repeat does not accept rendered property.
The second problem is that user.questions.size() is calculated twice (and user.questions is accessed in two different places), does that mean two hits for the same variable in db?

Should I encapsulate it in another panel with again rendered=#{user.questions.size() > 0} because it seems ui:repeat does not accept rendered property.
Yes.
The second problem is that user.questions.size() is calculated twice (and user.questions is accessed in two different places), does that mean two hits for the same variable in db?
Such behaviour should be handled in your model by appropriately scoping and caching the data.
#ManagedBean #RequestScoped
public class DemoBean {
private List<Question> questions;
public List<Question> getQuestions() {
if(questions == null) {
questions = lookupQuestions();
}
return questions;
}
// etc.

Related

create links dynamically

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?
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:
Event binding on dynamically created elements?
http://omnifaces.org/docs/javadoc/2.6/org/omnifaces/component/script/CommandScript.html
https://javaserverfaces.github.io/docs/2.3/vdldocs/facelets/h/commandScript.html
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>

Creating command links dynamically from a managed bean

I have a need to create command links dynamically based on content coming from elsewhere.
When a user clicks on a link it should call a method in a managed bean, and the method needs to know which link was clicked.
I can create the command links using the following code:
JSF:
<h:outputText value="#{myBean.dynamicLinks}" escape="false" />
Bean:
public String getDynamicLinks(){
// Return an html string that contains a set of <a> elements, based on the dynamic content
}
This works fine, but what I can't work out is how my <a> elements can call back into the bean.
This is not the right way to "dynamically" create markup. For that you should be using XHTML, and absolutely not Java and for sure not massage some plain HTML in the model and present it with escape="false". That's plain nonsense. You're basically mingling the view into the model. You need to make the model itself dynamic, not the view. The view must be static and dumb. The view must just present the model to the world. The model itself can be dynamic. You normally achieve that by using a flexible collection, such as List<String>, List<SomeEntity>, etc which you then present using an iterator in the view such as <ui:repeat> or <h:dataTable>.
E.g.
<ui:repeat value="#{bean.links}" var="link">
<h:commandLink value="link" action="#{bean.action(link)}" />
</ui:repeat>
public void action(Link link) {
// ...
}
You see, the action method can know about the pressed link by just inspecting the method argument.
See also:
How to create dynamic JSF form fields
How can I pass selected row to commandLink inside dataTable?

JSF 2.0 dynamic form best practice

Update: for those flagging this to be closed as a duplicate, the supposed duplicate question is nothing like what I am asking. My problem is I do not know until render time what the question set will be, how many questions there will be or what the question types will be so I cannot use the technique described in the "possible duplicate" answer.
Part of our JSF 2.x application has a requirement to render sets of questions to the user where the questions and the question types are not known until run-time. e.g we have something like (getters/setters omitted for clarity) :
public class QuestionSet {
private List<Section> sections;
}
public class Section {
private String sectionTitle;
private List<Question> questions;
private SectionStatus status; // e.g. UNANSWERED, CURRENTLY_ANSWERING,ANSWERED, COMPLETED
}
public class Question {
private String questionText;
private QuestionType questionType; // E.G TEXT, RADIO, LIST, CHECKBOX
private List<String> options; // for RADIO/LIST/CHECKBOX types
private List<String> answers;
}
We need to render each section in a seperate div, depending on it's status (e.g. UNANSWERED would display a div with just the title, ANSWERED would display a div with the section title and a green tick mark, and CURRENTLY_ANSWERING would render a div with the section title and then each question with the appropriate input control based on the question type.
The questions are also dynamic during the run - e.g. if a user answers yes to a radio button question, this may prompt further sub-questions.
I am currently doing this using a binding, i.e.
<h:panelGroup binding = "#{bean.panelGroup}" />
and within the bean's getPanelGroup creating the component tree by hand usin things like HtmlPanelGroup, HtmlOutputText, UIInput with ValueExpressions etc. which works fine but on reading some of BalusC's answers, particlarly to this question: How does the 'binding' attribute work in JSF? When and how should it be used? I am wondering if there is a "better" approach?
One of the things that concerns me is that the getter is called during RECREATE_VIEW for reasons explained in the linked question (after invoking the method referred to in the binding) so unless I take steps to, in RECREATE_VIEW phase, just return the component I created in the last RENDER_RESPONSE phase, this introduces unnecessary expense of recreating something I've just created.
In this case, it also seems pointless that JSF calls my setter to set the thing I just gave it in the getter for the bound property. (my bean is View scope as I will need to use ajax for some of the functionality our users require)
Thoughts/opinions (Especially from the ever helpful BalusC) greatly appreciated...
I don't see much reason to use component binding in this case. You can decide in your view what to render and how. You can have <ui:fragment>/<c:if> to conditionally render elements basing on question type, <ui:repeat>/<c:forEach> to handle the question set, etc.
So, if I understand the workflow correctly, your question set will be determined in e.g. post constructor method:
#PostConstruct
public void init() {
questionSet = service.get();//get it somehow
}
Then you'll have a set of sections and each of these section will contain questions, or answers, and validity is to be checked via AJAX. If I understand you right, then you can have the following view:
<h:form id="q-set">
<ui:repeat value="#{bean.questionSet.sections}" var="section">
<div>#{section.title}</div>
<div class="#{section.status eq 'UNANSWERED' ? 'section-unanswered' : ... }"/>
<ui:fragment rendered="#{section.status eq 'ANSWERED' ?}"><div class="tick"/></ui:fragment> ...
<ui:fragment rendered="#{section.status eq 'ANSWERED' ?}">
<ui:repeat value="#{section.questions}" var="question">
<div>#{question.title}</div>
<ui:fragment rendered="#{question.type eq 'RADIO'}">
<h:selectOneRadio value="#{question.answers[0]}" validator="...">
<f:selectItems value="#{question.options}" var="opt" itemLabel="#{opt}" ... />
<f:ajax ...>
</h:selectOneRadio>
</ui:fragment>
...
</ui:repeat>
</ui:fragment>
</ui:repeat>
</h:form>
It looks like you are going to have too much logic/conditions in your view.
What about generating the view programmatically on the Java side ?
For tricky parts you may resort to JavaScript and JSON.

How to use <ui:repeat> to iterate over a nested list?

Using JSF 2.0, I need to display a table wherein each row contains a link which opens a popup. I have two models: A which has id and List<B> properties and B which has id and name properties. In my backing bean, I have a List<A> property. In my view, I am using <ui:repeat> to iterate over List<A>.
The requirement is, depending on the row that the user clicks, the corresponding List<B> of A needs to be displayed. However, the <ui:repeat> does not accept a nested list to be assigned in the var attribute. Hence, I need to do a lot of workarounds which is not efficient.
How do I efficiently solve this problem?
What you need is to nest another <ui:repeat> tag in your outer iteration:
<ui:repeat value="#{bean.listOfA}" var="a">
...
<ui:repeat value="#{a.listOfB}" var="b">
...
</ui:repeat>
</ui:repeat>
The only thing left that is worth noting is that nested <ui:repeat> tags used to have problems with state management until Mojarra 2.1.15 version (details in jsf listener not called inside nested ui:repeat and in many not so recent questions and their answers), which could result in action listeners not called, etc. but if you're currently on the latest Mojarra JSF implementation - just skip this part altogether.

Adding JSF 2 composite component at runtime from backing bean

Edited question...
Hello,
I would like to load a .xhtml file of my composite component from a backing bean, and add it to the page dynamically. The name of the .xhtml file comes form a variable.
Ex.:
public MyBean (){
String componentFile = "myCompositeComponent.xhtml"
public String addComponentToPage(){
//how do that?...
return null;
}
}
Thank you!
That's not possible. A composite component is template-based and can only be used in views. Your best bet is to repeat exactly the JSF code which you've originally written in the composite component in the model. Better would be to create a full worthy #FacesComponent class which extends UIComponent, complete with a #FacesRenderer. True, it's a tedious and opaque job, but this way you'll end up with a component which is reuseable in both the view and the model by a single code line.
An -ugly- alternative is to place all possible components in the view and use the rendered attribute.
<my:component1 rendered="#{bean.myComponentType == 'component1'}" />
<my:component2 rendered="#{bean.myComponentType == 'component2'}" />
<my:component3 rendered="#{bean.myComponentType == 'component3'}" />
...
Wrap this if necessary in a Facelets tag file so that it can be hidden away and reused in several places.
I don't understand why do you want to add a composite component from a backing bean. I guess you want to make it visible dynamically in case of an event, but for that there is AJAX reRender.
For example you can do the following:
<h:panelGroup id="composite" rendered="#{myBean.renderComponent}">
<my:compositecomponent/>
</h:panelGroup>
The renderComponent property stores a boolean value. You can switch that value and reRender composite with for e.g. Richfaces's <a4j:commandLink>.
Hope that helps, Daniel

Resources