blob: 845d0193e45af83b09541b6074c08e5457aa89b7 [file] [log] [blame]
~~ Licensed to the Apache Software Foundation (ASF) under one or more
~~ contributor license agreements. See the NOTICE file distributed with
~~ this work for additional information regarding copyright ownership.
~~ The ASF licenses this file to You under the Apache License, Version 2.0
~~ (the "License"); you may not use this file except in compliance with
~~ the License. You may obtain a copy of the License at
~~
~~ http://www.apache.org/licenses/LICENSE-2.0
~~
~~ Unless required by applicable law or agreed to in writing, software
~~ distributed under the License is distributed on an "AS IS" BASIS,
~~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~~ See the License for the specific language governing permissions and
~~ limitations under the License.
------
Creating Custom Components
------
Creating Custom Components
Any JSF Component has the following elements:
* Component class file(s).
* JSP tag class file(s).
* Entry on faces-config.xml.
* Renderer class file(s) (optional).
* TLD file (optional).
* facelet-taglib entry (optional).
* Facelets tag handler files (optional).
Keep all those files up to date is a complex task, but with
myfaces-builder-plugin you just need to take care about:
* Component class file(s).
* Renderer class file(s) (optional).
* Facelets tag handler files (optional).
This example shows step by step what do you need to create a custom
component.
Setting up your project
All information related can be found {{{setup.html}here}}.
Configuration files (faces-config.xml, .tld, facelets taglib)
This {{{config-files.html}page}} shows some examples.
Writing your component class
There are three possibilities for create a component class:
* Write just one file and add all code manually (Example: t:aliasBean).
* Write an abstract class, and let generate all property code, including
saveState/restoreState methods in a concrete child class
(Example: almost all components in tomahawk core and sandbox).
* Write an abstract class (generally package scope) and use template
pattern. In other words, all property code is generated including
saveState/restoreState methods in a concrete child class, but other
code inside the abstract class is copied to the generated class.
* Write a component class manually
In this mode, no code is generated, but the information is chained to
other files to be generated like faces-config.xml, tlds, tag classes, etc.
Below there is an example of it:
-------------------
/**
* The most important points are:
*
* 1. Define componentType, componentFamily and rendererType adding its
* constants (like below) or directly in the annotation.
*
* 2. If the component has a jsp tag, define the "name" and the "tagClass"
* If the tagClass does not exists a new file is generated if make-tags
* goal is set on pom.xml
*
*/
@JSFComponent(
name = "mycomponents:sayHello",
clazz = "org.myorganization.component.sayhello.SayHello",
tagClass = "org.myorganization.component.sayhello.SayHelloTag")
public class SayHello extends UIOutput
{
public static final String COMPONENT_TYPE = "org.myorganization.SayHello";
public static final String DEFAULT_RENDERER_TYPE = "org.myorganization.SayHelloRenderer";
public static final String COMPONENT_FAMILY = "javax.faces.Output";
/** ..... Some custom code goes here .... **/
}
-------------------
* Write a component class using Abstract Pattern
The objective is create a abstract component class
that defines all information required to generate the concrete
component class. All custom code goes in the abstract class, so it
is inherited to the child component.
This pattern is preferred over template mode, because it is more simple
to understand, but there are some cases where this mode cannot be
applied (like in myfaces core api, where the component hierarchy cannot
be changed).
Below there is an example of it:
-------------------
/**
* To generate component classes using abstract pattern
*
* 1. Define the "clazz" file which it is generated
*
* 2. Define componentType, componentFamily and rendererType adding its
* constants (like below) or directly in the annotation.
*
* 3. If the component has a jsp tag, define the "name" and the "tagClass"
* If the tagClass does not exists a new file is generated if make-tags
* goal is set on pom.xml
*
*/
@JSFComponent(
name = "mycomponents:sayHello",
clazz = "org.myorganization.component.sayhello.SayHello",
tagClass = "org.myorganization.component.sayhello.SayHelloTag")
public abstract class AbstractSayHello extends UIOutput
{
public static final String COMPONENT_TYPE = "org.myorganization.SayHello";
public static final String DEFAULT_RENDERER_TYPE = "org.myorganization.SayHelloRenderer";
public static final String COMPONENT_FAMILY = "javax.faces.Output";
/**
* User's first name.
*/
@JSFProperty
public abstract String getName();
}
-------------------
* Write a component class using Template Pattern
The objective is create an abstract (generally package scoped) class
that works as a "template".
-------------------
@JSFComponent(
name = "mycomponents:sayHello",
clazz = "org.myorganization.component.sayhello.SayHello",
tagClass = "org.myorganization.component.sayhello.SayHelloTag")
abstract class _SayHello extends UIOutput
{
public static final String COMPONENT_TYPE = "org.myorganization.SayHello";
public static final String DEFAULT_RENDERER_TYPE = "org.myorganization.SayHelloRenderer";
public static final String COMPONENT_FAMILY = "javax.faces.Output";
/**
* This method is copied to generated SayHello class
**/
public void broadcast(javax.faces.event.FacesEvent event)
throws javax.faces.event.AbortProcessingException
{
//Some custom code goes here
}
/**
* This method is not copied, but @JSFExclude works with fields too!
**/
@JSFExclude
public void doSomething()
{
//Some never used custom code goes here
}
/**
* User's first name.
*/
@JSFProperty
public abstract String getName();
}
-------------------
Generating Component Tag Classes
The goal "make-tags" trigger component jsp tag generation. This goal
checks if the tag class exists and if not, it creates one.
Here there are two scenarios:
* The component class inherits from jsf core UIXXXXX, so the generated
class inherits from javax.faces.webapp.UIComponent(EL)Tag.
* The component class inherits from jsf core HtmlXXX. In this case,
the tag classes where your component inherits is on myfaces core
impl jar, so again there are three options:
* Add myfaces core impl jar as compile dependency and use myfaces
core in your web application.
* Add tomahawk core or core12 to your dependencies, where there
is an alternate tag hierarchy, so your components can work
with the reference implementation. Make sure tomahawk dependency.
should be before myfaces core dependency in the pom, to be sure
that tomahawk model is loaded first.
* Generate a jsf html tag hierarchy in your jar. See tomahawk core
or core12 pom for details.
If you need to write some custom code on tag class, but keep some
code generated, use abstract pattern on tag class. The properties that
needs to be defined on abstract tag class must have inheritedTag="true",
so there are not overriden. See t:tree component for an example.
Adding a Renderer to faces-config.xml file.
The annotations/doclets @JSFRenderer and @JSFRenderKit are used include
renderer configuration to generated faces-config.xml files. Just add it
to your renderer like this:
---------------------------
@JSFRenderer(
renderKitId = "HTML_BASIC",
family = "javax.faces.Output",
type = "org.myorganization.SayHelloRenderer")
public class SayHelloRenderer extends Renderer
{
//Some code goes here
}
---------------------------