La soluzione che ho trovato era di estendere e ri-implementare i metodi encodeMarkup per i renderer di input. Volevo una soluzione più generale, ma dopo aver esaminato il codice sorgente Primefaces, non ho visto alcun hook generico per i renderer di componenti per aggiungere attributi personalizzati. Il markup è scritto nei metodi encodeMarkup(FacesContext context, InputText inputText)
dei renderer. Richiama la gerarchia di classi su renderPassThruAttributes(FacesContext context, UIComponent component, String[] attributes)
ma si nutre solo di array String [] finali statici da org.primefaces.util.HTML
.
Nel mio caso volevo il supporto per l'attributo 'autofocus' su InputMask, InputText, InputTextarea e Password. Inoltre, l'implementazione è la stessa per ogni componente, quindi passerò attraverso l'implementazione di "autofocus" nel componente InputText, ma dovrebbe essere ovvio come possa essere esteso per supportare più attributi e più componenti.
Per estendere/sovrascrivere un renderer, è necessario disporre della sorgente Primefaces disponibile e trovare il metodo encodeMarkup e copiarne il contenuto. Ecco l'esempio per InputTextRenderer:
protected void encodeMarkup(FacesContext context, InputText inputText) throws IOException {
ResponseWriter writer = context.getResponseWriter();
String clientId = inputText.getClientId(context);
writer.startElement("input", null);
writer.writeAttribute("id", clientId, null);
writer.writeAttribute("name", clientId, null);
writer.writeAttribute("type", inputText.getType(), null);
String valueToRender = ComponentUtils.getValueToRender(context, inputText);
if(valueToRender != null) {
writer.writeAttribute("value", valueToRender , null);
}
renderPassThruAttributes(context, inputText, HTML.INPUT_TEXT_ATTRS);
if(inputText.isDisabled()) writer.writeAttribute("disabled", "disabled", null);
if(inputText.isReadonly()) writer.writeAttribute("readonly", "readonly", null);
if(inputText.getStyle() != null) writer.writeAttribute("style", inputText.getStyle(), null);
writer.writeAttribute("class", createStyleClass(inputText), "styleClass");
writer.endElement("input");
}
Estendere/Override del renderer con il proprio (vedi commenti per il codice importante):
public class HTML5InputTextRenderer extends InputTextRenderer {
Logger log = Logger.getLogger(HTML5InputTextRenderer.class);
//Define your attributes to support here
private static final String[] html5_attributes = { "autofocus" };
protected void encodeMarkup(FacesContext context, InputText inputText) throws IOException {
ResponseWriter writer = context.getResponseWriter();
String clientId = inputText.getClientId(context);
writer.startElement("input", null);
writer.writeAttribute("id", clientId, null);
writer.writeAttribute("name", clientId, null);
writer.writeAttribute("type", inputText.getType(), null);
String valueToRender = ComponentUtils.getValueToRender(context, inputText);
if (valueToRender != null) {
writer.writeAttribute("value", valueToRender, null);
}
renderPassThruAttributes(context, inputText, HTML.INPUT_TEXT_ATTRS);
//Make an extra call to renderPassThruAttributes with your own attributes array
renderPassThruAttributes(context, inputText, html5_attributes);
if (inputText.isDisabled())
writer.writeAttribute("disabled", "disabled", null);
if (inputText.isReadonly())
writer.writeAttribute("readonly", "readonly", null);
if (inputText.getStyle() != null)
writer.writeAttribute("style", inputText.getStyle(), null);
writer.writeAttribute("class", createStyleClass(inputText), "styleClass");
writer.endElement("input");
}
}
Configurazione l'override di rendering in faces-config.xml
<?xml version='1.0' encoding='UTF-8'?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
version="2.0">
<!-- snip... -->
<render-kit>
<renderer>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.InputTextRenderer</renderer-type>
<renderer-class>com.mycompany.HTML5InputTextRenderer</renderer-class>
</renderer>
</render-kit>
<!-- snip... -->
</faces-config>
e just-in-caso se non ha avuto il faces-config configurato nel web.xml aggiuntivo:
<context-param>
<param-name>javax.faces.CONFIG_FILES</param-name>
<param-value>
/WEB-INF/faces-config.xml, /faces-config.xml
</param-value>
</context-param>
quindi di utilizzare questo in qualche markup:
<p:inputText id="activateUserName" value="${someBean.userName}"
autofocus="on">
</p:inputText>
Nota: JSF non è felice con gli attributi che non hanno valori. Mentre l'autofocus in HTML5 non utilizza un valore, JSF genera un errore se non ne viene fornito uno, quindi assicurati di definire un valore di throw-away quando aggiungi tali attributi.
Ottimo, sembra molto più bello della soluzione per componente. Sono sicuro che casi d'uso specifici potrebbero rendere gli argomenti in entrambi i modi. Se avrò tempo, proverò a farlo perché sembra che sarebbe molto più rapido di distribuire tutti i componenti. – Rich
UIComponent # getCurrentComponent (FacesContext) sembra essere la firma, quindi nel contesto del metodo startElement che non è disponibile. Ho visto nel commit Omnifaces che hai utilizzato Components.getCurrentCompnent() che non richiede il contesto, sebbene si tratti di una classe OmniFaces che non è disponibile solo in PrimeFaces. A quel punto per me sarebbe più pericoloso dover sostituire anche qualsiasi componente che potrebbe passare null a startElement. Mi suggerisci di inserire OmniFaces o esiste un altro modo? – Rich
Il modulo 'Components # getCurrentComponent()' OmniFaces si limita a delegare a 'UIComponent # getCurrentComponent()'. Non sei chiaro se lo hai testato o meno, ma funziona per me. – BalusC