Adding custom attribute (HTML5) support to JSF 2.0 UIInput component
This is my way. I added placeholder and data-theme attributes. If you want to add more attributes, you should just add its name to attributes array.
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import com.sun.faces.renderkit.html_basic.TextRenderer;
public class InputRender extends TextRenderer {
@Override
protected void getEndTextToRender(FacesContext context,
UIComponent component,
String currentValue)
throws java.io.IOException{
String [] attributes = {"placeholder","data-theme"};
ResponseWriter writer = context.getResponseWriter();
for(String attribute : attributes)
{
String value = (String)component.getAttributes().get(attribute);
if(value != null) {
writer.writeAttribute(attribute, value, attribute);
}
}
super.getEndTextToRender(context, component, currentValue);
}
}
You should add this to faces-config.xml file.
<render-kit>
<renderer>
<component-family>javax.faces.Input</component-family>
<renderer-type>javax.faces.Text</renderer-type>
<renderer-class>your.package.InputRenderer</renderer-class>
</renderer>
</render-kit>
Adding custom attribute (HTML5) support to Primefaces (3.4)
Instead of homegrowing a custom renderer for every single individual component, you could also just create a single RenderKit
wherein you provide a custom ResponseWriter
wherein the startElement()
method is overriden to check the element name and/or component instance and then write additional attributes accordingly.
Here's a kickoff example of the HTML5 render kit:
public class Html5RenderKit extends RenderKitWrapper {
private RenderKit wrapped;
public Html5RenderKit(RenderKit wrapped) {
this.wrapped = wrapped;
}
@Override
public ResponseWriter createResponseWriter(Writer writer, String contentTypeList, String characterEncoding) {
return new Html5ResponseWriter(super.createResponseWriter(writer, contentTypeList, characterEncoding));
}
@Override
public RenderKit getWrapped() {
return wrapped;
}
}
The HTML5 response writer:
public class Html5ResponseWriter extends ResponseWriterWrapper {
private static final String[] HTML5_INPUT_ATTRIBUTES = { "autofocus" };
private ResponseWriter wrapped;
public Html5ResponseWriter(ResponseWriter wrapped) {
this.wrapped = wrapped;
}
@Override
public ResponseWriter cloneWithWriter(Writer writer) {
return new Html5ResponseWriter(super.cloneWithWriter(writer));
}
@Override
public void startElement(String name, UIComponent component) throws IOException {
super.startElement(name, component);
if ("input".equals(name)) {
for (String attributeName : HTML5_INPUT_ATTRIBUTES) {
String attributeValue = component.getAttributes().get(attributeName);
if (attributeValue != null) {
super.writeAttribute(attributeName, attributeValue, null);
}
}
}
}
@Override
public ResponseWriter getWrapped() {
return wrapped;
}
}
To get it to run, create this HTML5 render kit factory:
public class Html5RenderKitFactory extends RenderKitFactory {
private RenderKitFactory wrapped;
public Html5RenderKitFactory(RenderKitFactory wrapped) {
this.wrapped = wrapped;
}
@Override
public void addRenderKit(String renderKitId, RenderKit renderKit) {
wrapped.addRenderKit(renderKitId, renderKit);
}
@Override
public RenderKit getRenderKit(FacesContext context, String renderKitId) {
RenderKit renderKit = wrapped.getRenderKit(context, renderKitId);
return (HTML_BASIC_RENDER_KIT.equals(renderKitId)) ? new Html5RenderKit(renderKit) : renderKit;
}
@Override
public Iterator<String> getRenderKitIds() {
return wrapped.getRenderKitIds();
}
}
And register it as follows in faces-config.xml
:
<factory>
<render-kit-factory>com.example.Html5RenderKitFactory</render-kit-factory>
</factory>
The JSF utility library OmniFaces has also such a render kit, the Html5RenderKit (source code here) which should theoretically also work fine on PrimeFaces components. However, this question forced me to take a second look again and I was embarrassed to see that the component
argument in ResponseWriter#startElement()
is null
in <p:inputText>
(see line 74 of InputTextRenderer
, it should have been writer.startElement("input", inputText)
instead). I'm not sure if this is intentional or an oversight in the design of the PrimeFaces renderer or not, but you could use UIComponent#getCurrentComponent()
instead to get it.
Update: this is fixed in OmniFaces 1.5.
Noted should be that the upcoming JSF 2.2 will support defining custom attributes in the view via the new passthrough
namespace or the <f:passThroughAttribute>
tag. See also What's new in JSF 2.2? - HTML5 Pass-through attributes.
Thus, so:
<html ... xmlns:p="http://java.sun.com/jsf/passthrough">
...
<h:inputText ... p:autofocus="true" />
(you may want to use a
instead of p
as namespace prefix to avoid clash with PrimeFaces' default namespace)
Or:
<h:inputText ...>
<f:passThroughAttribute name="autofocus" value="true" />
</h:inputText>
JSF 2.0 strips out needed HTML5 attributes
JSF isn't exactly stripping them out. It's just ignoring them because they are not among the supported/known attributes of the component in question. In case of for example <h:inputText>
(which renders by default a HTML <input type="text">
tag), you can find all supported attributes in the view declaration language (VDL) documentation.
To overcome this, you would need to create a custom component or, better, just a custom renderer which overrides the standard <h:inputText>
renderer and takes the custom attributes into account.
JSF Custom component attribute properties with Annotations
There isn't any annotation to do what you'd like.
If you don't know yet how to add attributes to your custom JSF components, you can look here : Adding custom attribute (HTML5) support to JSF 2.0 UIInput component
Add custom html attribute to be rendered for jsf2 component
There is no trivial way to achieve this. Unregistered attribtues are completely ignored. Assuming that you're using Mojarra, your best bet is to extend Mojarra's CheckboxRenderer
with a custom one wherein you override the getEndTextToRender()
method which writes the extra attribute. To get it to run, just register it in faces-config.xml
as a renderer for component family javax.faces.SelectBoolean
and renderer type javax.faces.Checkbox
.
An alternative is to delegate the job to some onload JavaScript.
JSF removes custom HTML component attributes
You don't need to write your own tags, it is sufficient to write your own renderer for the JSF-tags. A renderer which does not omit your custom attributes.
Look here on how to write & configure such a renderer for all your inputs.
Adding bootstrap attribute to custom MenuRenderer in JSF
UPDATE II - ANSWER -
I chose a mixed approach between dilek's code and Vladiator's code. I just need to create one helper class called AditionalAttributesWritter. Also, is a good idea to check out the code implementation.
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.ResponseWriter;
import javax.faces.context.ResponseWriterWrapper;
/**
* @link https://stackoverflow.com/a/7886942/4253946
*/
public class AditionalAttributesWritter extends ResponseWriterWrapper {
private static final String[] ATTRIBUTES = { "data-toggle" };
private ResponseWriter originalResponseWriter;
public AditionalAttributesWritter(ResponseWriter originalResponseWriter) {
super();
this.originalResponseWriter = originalResponseWriter;
}
@Override
// As of JSF 1.2 this method is now public.
public ResponseWriter getWrapped() {
return originalResponseWriter;
}
@Override
public void startElement(String name, UIComponent component) throws IOException {
super.startElement(name, component);
for (String attribute : ATTRIBUTES) {
Object value = component.getAttributes().get(attribute);
if (value != null) {
super.writeAttribute(attribute, value, attribute);
}
}
}
}
and finally the SelectRenderer is like this
import com.sun.faces.renderkit.html_basic.MenuRenderer;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
/**
* @link https://stackoverflow.com/questions/27324024/jsf-selectonemenu-extending-menurenderer-for-displaying-a-plain-text-when-there/27337240#27337240
*/
public class SelectRenderer extends MenuRenderer {
@Override
protected void renderSelect(FacesContext context, UIComponent component) throws IOException {
final ResponseWriter originalResponseWriter = context.getResponseWriter();
context.setResponseWriter(new AditionalAttributesWritter(originalResponseWriter));
super.renderSelect(context, component);
context.setResponseWriter(originalResponseWriter); // Restore original writer.
}
}
and it works. If you think that this approach is not good enough please let me know.
How to add placeholder attribute to JSF input component?
I thought everything that was not JSF was passed to the browswer for rendering?
This assumption is thus wrong. Unspecified component attributes are ignored by the JSF renderers.
You have basically the following options to get it to work:
If you're already on JSF 2.2 or newer, set it as a passthrough attribute.
<... xmlns:a="http://xmlns.jcp.org/jsf/passthrough">
<h:inputText a:placeholder="fill me" />Note that I use a XML namespace prefix of
a
("attribute") instead ofp
as shown in the tutorial, as it would otherwise clash with default XML namespace prefixp
of PrimeFaces.Implement a custom renderer for
<h:inputText>
wherein you explicitly check and write the attribute.Implement a custom component which uses the aforementioned custom renderer.
Implement a JS based solution wherein you grab the element from DOM and explicitly set the attribute.
Look for a component library which supports this out the box. PrimeFaces for example has a
<p:watermark>
for this purpose with nice JS based graceful degradation for browsers which does not support theplaceholder
attribute on inputs.
See also:
- Custom HTML tag attributes are not rendered by JSF
Related Topics
How to Horizontally Center an Unordered List of Unknown Width
Redirect on Select Option in Select Box
How to HTML Encode/Escape a String? Is There a Built-In
How to Curve the Div from Bottom with Image Background
Pre-Populate HTML Form File Input
Disable Double-Tap "Zoom" Option in Browser on Touch Devices
What Are the CSS Properties That Get Elements Out of the Normal Flow
HTML <Base> Tag and Local Folder Path with Internet Explorer
How to Run the CSS3 Animation to the End If the Selector Is Not Matching Anymore
How to Add a Tool Tip to a Span Element
Difference Between Position:Sticky and Position:Fixed
How to View an HTML File in the Browser with Visual Studio Code
HTML Favicon.Ico Won't Show on Google Chrome
Making a Div That Covers the Entire Page
Iframe with External Page Not Working
How to Align Entire HTML Body to the Center
CSS - Relative Positioned Parent Div Not Stretching to Absolute Child Div Height