How to extend a Primefaces component (as Composite Component or Java class) to add or modify properties

UI Components for JSF
Post Reply
enclle
Posts: 2
Joined: 02 Jun 2016, 08:39

17 Jan 2018, 15:10

Hello All,

I want to extend the Dialog component, so that it has some other components already set and also to have some properties set with default values, to not repeat every time.

First, I tried to extend as a JSF Composite Component, but I don't know if it's possible to set default properties in CC, I tried using JSTL <c:set> but it did not work.

So I think that the best is to create a class extending org.primefaces.component.dialog. { Dialog or DialogRenderer }, but I don't know what method I have to override to do it.

All that I want is to do something like:

<my:customDialog name="aName" title="A Title For The Dialog" controller="#{aController}">
<variable custom content>
</my:customDialog>

Then, in CustomDialog (class or CC) I set id and widgetVar to be "aName", header to be title, set other properties to some default, and create some other components, like buttons, that uses the controller property.

Code for Composite Component I tried (DON'T WORK):

...
<body>
<cc:interface componentType="org.primefaces.component.Dialog" >
<cc:attribute name="name" required="true" />
<cc:attribute name="controller" required="true" />
<cc:attribute name="title" required="true" />
</cc:interface>
<cc:implementation>
<c:set property="id" value="#{cc.attrs.nome}" />
<c:set property="widgetVar" value="#{cc.attrs.nome}" />
<c:set property="modal" value="true" />
<c:set property="resizable" value="false" />
<c:set property="appendTo" value="@(body)" />
<c:set property="header" value="#{cc.attrs.title}" />
<h:form id="form">
<h:panelGroup id="display">
<p:panelGrid columns="1">
<cc:insertChildren />
</p:panelGrid>
<p:commandButton actionListener="#{cc.attrs.controller.save}"
value="Save" update="display,@(body)"

onclick="#{cc.attrs.name}.hide()" immediate="true" ajax="true"
resetValues="true">
</p:commandButton>
<p:commandButton ajax="true" global="false"
actionListener="#{cc.attrs.controller.cancel}" value="Cancel"
onclick="#{cc.attrs.name}.hide()" immediate="true" resetValues="true">
</p:commandButton>
</h:panelGroup>
</h:form>
</cc:implementation>
</body>
...

What I want to know is, if it's possible to set properties in CC, how to do it, or, if not possible, what method I have to override from dialog class, and what class (Dialog or DialogRenderer).

God bless you,

Leandro.

kukeltje
Expert Member
Posts: 9605
Joined: 17 Jun 2010, 13:34
Location: Netherlands

17 Jan 2018, 22:57

why do you (think you) need the c:set? And don't use forms inside a component. Might result in nested forms and others not able to find the resulting problems. And why not just put a dialog inside it?

Jan Eckert
Posts: 84
Joined: 11 Sep 2014, 10:13
Location: Brussels, Belgium

18 Jan 2018, 09:34

Hi Leandro,

your question touches some quite advanced topic/problems of custom component development, which is why I want to ask you to be more specific about your question because your text is a little contradictory:
On one hand you ask how to modify an existing component (here: org.primefaces.component.Dialog) to have some pre-set attributes. On the other side you offer a composite component implementation that uses a lot of different components assembled together which hints at completely different needs.

So what exactly do you want to do? i mean in terms of a use case (eg "I want to use a Dialog with two input fields and a caption that always reads 'LeandroDialog'"!)

The reason for this question is to determine whether you need to go for a composite component (which is what you tried, you are bundling UIComponents together via XHTML), a custom component (no XHTML, a component class and renderer class registered in a taglib instead) or a mix of the two, each requiring quite a longish answer.

Even not knowing your exact needs, there are various issues with the code you provided and it won't work indeed, but pointing those out might be a waste of time if a composite turns out not to be the right path of solution.

@kukeltje's: Usually you'd be right about your comment with the form within a dialog but in this specific case the dialog seems to be going to be hardcoded to appendToBody which is save regarding nested forms.

kukeltje
Expert Member
Posts: 9605
Joined: 17 Jun 2010, 13:34
Location: Netherlands

18 Jan 2018, 11:08

@JanEckert: Well, yes and no... it still may not be a nested form BEFORE the dialog is moved from its rendered position in the DOM to right before the body close tag. That part happens client-side after the initial rendering and it still is illegal to have nested forms before that move.

Jan Eckert
Posts: 84
Joined: 11 Sep 2014, 10:13
Location: Brussels, Belgium

18 Jan 2018, 12:10

kukeltje wrote:
18 Jan 2018, 11:08
@JanEckert: Well, yes and no... it still may not be a nested form BEFORE the dialog is moved from its rendered position in the DOM to right before the body close tag. That part happens client-side after the initial rendering and it still is illegal to have nested forms before that move.
Good point, thanks for the food for thought.
Primefaces 6.1+
Wildfly 11

enclle
Posts: 2
Joined: 02 Jun 2016, 08:39

20 Jan 2018, 00:14

kukeltje wrote:
17 Jan 2018, 22:57
why do you (think you) need the c:set? And don't use forms inside a component. Might result in nested forms and others not able to find the resulting problems. And why not just put a dialog inside it?
As I wrote, what I want is to set the properties of the component inside itself, but it isn't possible. What I tried is to set the Id and the widgetVar properties with the value of the name attribute.
What I want to create is a "pre-filled" form dialog, so I just need to add the input components/labels, instead of coding the entire dialog every time.
The composite with a <p:dialog> inside works, but then I have to refer to it with 1 indirection level. For example, in update property, instead of writing update=" :myDialog1 :myDialog2" (just an example) I have then to write update=":myDialog1:theDialog :myDialog2:theDialog".
Jan Eckert wrote:
18 Jan 2018, 09:34
Hi Leandro,

your question touches some quite advanced topic/problems of custom component development, which is why I want to ask you to be more specific about your question because your text is a little contradictory:
On one hand you ask how to modify an existing component (here: org.primefaces.component.Dialog) to have some pre-set attributes. On the other side you offer a composite component implementation that uses a lot of different components assembled together which hints at completely different needs.

So what exactly do you want to do? i mean in terms of a use case (eg "I want to use a Dialog with two input fields and a caption that always reads 'LeandroDialog'"!)

The reason for this question is to determine whether you need to go for a composite component (which is what you tried, you are bundling UIComponents together via XHTML), a custom component (no XHTML, a component class and renderer class registered in a taglib instead) or a mix of the two, each requiring quite a longish answer.

Even not knowing your exact needs, there are various issues with the code you provided and it won't work indeed, but pointing those out might be a waste of time if a composite turns out not to be the right path of solution.

@kukeltje's: Usually you'd be right about your comment with the form within a dialog but in this specific case the dialog seems to be going to be hardcoded to appendToBody which is save regarding nested forms.
Well, I have to wrote in various places the same code for a form inside a dialog, with save and cancel buttons, for various "register forms". So, what better than create a template, right?
So first I "templatized" it, but I had problems with duplicated ids, etc... So I moved to create a "composite component", but then I can't set the properties of the composite component inside itself (to set the id and widgetVar to be the value of name), so then I moved to create the component in java, with no success too (but this is because of my lack of knowledge).

kukeltje
Expert Member
Posts: 9605
Joined: 17 Jun 2010, 13:34
Location: Netherlands

20 Jan 2018, 11:38

I understand the composite component. I just asked why you (think you) need the c:set. You can use the EL directly in the places where you need the other variable (all but the id one). Then that part should work... setting the ID dynamically on child components does not work and even if it did, setting the id based on a name attribute might be confusing when troubleshooting.

Defining (extending?) an existing class is not how this is meant to be used afaik. So take a step back. Put a `<p:dialog>` as the first tag. But all in there and try.

Jan Eckert
Posts: 84
Joined: 11 Sep 2014, 10:13
Location: Brussels, Belgium

25 Jan 2018, 10:21

Hey again!

Sorry for the late answer. but honestly I wrote an elaborate answer just to throw it away in the end because - in all arrogance - I think what you need to know is not what you are asking for. But first your question:
What I want to know is, if it's possible to set properties in CC, how to do it, or, if not possible, what method I have to override from dialog class, and what class (Dialog or DialogRenderer).
it is possible to set properties within the cc. All the properties you define in the interface (and are potentially set when you re-use the tag) are accessible via the implicit #{cc}-object. Attributes you wish to hardcode can simply be provided within the implementation. If you want to create a CC however, your syntax is way off. Here a concise example:

Code: Select all

<ui:component
    xmlns="http://www.w3.org/1999/xhtml"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:p="http://primefaces.org/ui"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:ui="http://java.sun.com/jsf/facelets"
    xmlns:cc="http://java.sun.com/jsf/composite">
    
    <cc:interface>
        <cc:attribute name="renderText" type="java.lang.Boolean" />
    </cc:interface>
    
    <cc:implementation>
      <p:outputText rendered="#{cc.attrs.renderText}"  value="I am here!"/>   
    </cc:implementation>
</ui:component>
Please note that I removed the attribute 'component-type' from the interface tag. Referencing org.primefaces.component.Dialog here makes btw no sense for many reasons. Neither do these c:sets belong there. I know this way to go about it from Facelet tag files and I think this is your true problem. I dare say that you are royally confused about the different mechanism JSF 2 offers for code re-usage. There is a ridiculously well-written, concise article by the one and only out there. Dig it until you get it.

https://stackoverflow.com/questions/682 ... ponen?rq=1

Hit us up with questions if you still have problems or we missed something from your question.
Primefaces 6.1+
Wildfly 11

Post Reply

Return to “PrimeFaces”

  • Information
  • Who is online

    Users browsing this forum: No registered users and 45 guests