How to let JSF pass through HTML attributes [duplicate] - html

This question already has an answer here:
Custom HTML tag attributes are not rendered by JSF
(1 answer)
Closed 2 years ago.
I am using Primefaces 3 in JSF 2 to make a search box. I need to add a non-standard attribute (x-webkit-speech) to the control so you would have something like this...
<p:autoComplete x-webkit-speech="x-webkit-speech" ... />
Since this attribute isn't part of the autoComplete control JSF gives me a 500 error. But when I remove it, the page renders fine. In general, how do you specify pass through attributes on a JSF tag so they are ignored?

JSF by design ignores all custom attributes when rendering HTML.
If you're already on JSF 2.2+, simply specify it as passthrough attribute:
<html ... xmlns:a="http://xmlns.jcp.org/jsf/passthrough">
...
<p:autoComplete a:x-webkit-speech="x-webkit-speech" ... />
If you're not on JSF 2.2 yet, then you need a custom renderer. This is in case of PrimeFaces <p:autoComplete> (and all other components) fortunately relatively simple. It's sufficient to override just the renderPassThruAttributes() method wherein you add the new attribute which you'd like to render to the attrs argument and finally delegate to the super method.
E.g.
package com.example;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import org.primefaces.component.autocomplete.AutoCompleteRenderer;
public class MyAutoCompleteRenderer extends AutoCompleteRenderer {
#Override
protected void renderPassThruAttributes(FacesContext facesContext, UIComponent component, String[] attrs) throws IOException {
String[] newAttrs = new String[attrs.length + 1];
System.arraycopy(attrs, 0, newAttrs, 0, attrs.length);
newAttrs[attrs.length] = "x-webkit-speech";
super.renderPassThruAttributes(facesContext, component, newAttrs);
}
}
To get it to run, register it as follows in your webapp's faces-config.xml:
<render-kit>
<renderer>
<component-family>org.primefaces.component</component-family>
<renderer-type>org.primefaces.component.AutoCompleteRenderer</renderer-type>
<renderer-class>com.example.MyAutoCompleteRenderer</renderer-class>
</renderer>
</render-kit>
(you can find out the component family and renderer type by looking at the source code of AutoComplete class, they're specified as COMPONENT_FAMILY and RENDERER_TYPE constants in there)
No, the #FacesRenderer annotation simply won't work when the purpose is to override custom renderers which are by itselves already registered in a faces-config.xml.

The most Tags can be extended, using the Attribute-Tag from JSF-Ext.
<html xmlns:h="http://java.sun.com/jsf/html" xmlns:e="http://java.sun.com/jsf/ext">
<!-- ... -->
<h:inputText id="name" value="#{bean.name}">
<e:attribute name="placeholder" value="My Name"/>
</h:inputText>
<!-- ... -->
</html>
You can configure it via maven:
<dependency>
<groupId>com.intersult</groupId>
<artifactId>jsf-ext</artifactId>
<version>2.2.0.1</version>
</dependency>
JSF-Ext is a library from http://www.intersult.com/wiki/page/JSF%20Ext

I am not sure if this is possible at all. I would add those attributes on the client side using javascript or jQuery.
You can put el expressions into your javascript code if you want to integrate server-side stuff.

Related

Internazionalization won't work due to problems w jsf propieties [duplicate]

faces-config.xml:
<application>
<locale-config>
<default-locale>ru</default-locale>
<supported-locale>ua</supported-locale>
</locale-config>
</application>
In a bean action method, I'm changing the locale in the current view as follows:
FacesContext.getCurrentInstance().getViewRoot().setLocale(new Locale("ua"));
The problem is that ua Locale is applied, but only per request/view and not for session. Another request/view within the same session resets the locale back to default ru value.
How can I apply the locale for session?
You need to store the selected locale in the session scope and set it in the viewroot in two places: once by UIViewRoot#setLocale() immediately after changing the locale (which changes the locale of the current viewroot and thus get reflected in the postback; this part is not necessary when you perform a redirect afterwards) and once in the locale attribute of the <f:view> (which sets/retains the locale in the subsequent requests/views).
Here's an example how such a LocaleBean should look like:
package com.example.faces;
import java.util.Locale;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;
#ManagedBean
#SessionScoped
public class LocaleBean {
private Locale locale;
#PostConstruct
public void init() {
locale = FacesContext.getCurrentInstance().getExternalContext().getRequestLocale();
}
public Locale getLocale() {
return locale;
}
public String getLanguage() {
return locale.getLanguage();
}
public void setLanguage(String language) {
locale = new Locale(language);
FacesContext.getCurrentInstance().getViewRoot().setLocale(locale);
}
}
And here's an example of the view should look like:
<!DOCTYPE html>
<html lang="#{localeBean.language}"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">
<f:view locale="#{localeBean.locale}">
<h:head>
<title>JSF/Facelets i18n example</title>
</h:head>
<h:body>
<h:form>
<h:selectOneMenu value="#{localeBean.language}" onchange="submit()">
<f:selectItem itemValue="en" itemLabel="English" />
<f:selectItem itemValue="nl" itemLabel="Nederlands" />
<f:selectItem itemValue="es" itemLabel="EspaƱol" />
</h:selectOneMenu>
</h:form>
<p><h:outputText value="#{text['some.text']}" /></p>
</h:body>
</f:view>
</html>
Which assumes that #{text} is already configured in faces-config.xml as below:
<application>
<resource-bundle>
<base-name>com.example.i18n.text</base-name>
<var>text</var>
</resource-bundle>
</application>
Note that <html lang> is not required for functioning of JSF, but it's mandatory how search bots interpret your page. Otherwise it would possibly be marked as duplicate content which is bad for SEO.
See also:
Maven and JSF webapp structure, where exactly to put JSF resources
Internationalization in JSF, when to use message-bundle and resource-bundle?
i18n with UTF-8 encoded properties files in JSF 2.0 application
I see that the problem is also with .properties file name.
Java Locale us codes (lowercase) like: en_gb
But automaticly created locale (by Netbeans) is lowercase_uppercase i.e.: messages_en_GB.properties
Change name to: messages_en_gb.properties
and it should work - if you tried everything
This component f:view is not there your JSF page it will not work and It will shows only default english language.Provide the localae value for this f:view component then it will work fine. I faced the same problem now its working fine.
One small remark to #BalusC great solution. If we have <f:viewAction> which executes some method in backing bean. Locale available from call to FacesContext.getCurrentInstance().getViewRoot().getLocale() inside that method would be locale that is set by user browser or default application locale, not that locale that is set on session bean by user selection(of course they can match if browser locale equals that locale that user selected).
I can stand corrected, because maybe I did something wrong when implementing solution provided by #BalusC.
EDIT. After playing with JSF lifecycle, this behavior with locale is not related to <f:viewAction>, because there is similar behavior also with #PostContruct. <f:view locale="#{localeBean.locale}"> in request(after user selected locale) is executed in render response phase. <f:viewAction> and #PostContruct methods are executed in invoke application phase. That is why logic that is executed in this method do not have access to user selected locale.
Solution that we using when we need correct locale is to inject(CDI) localeBean in other backing bean that contains <f:viewAction> and #PostContruct methods, and then set locale with UIViewRoot#setLocale() from localeBean in beginning of these methods.
If you can use CDI and deltaspike (JSF module) in your environment, you could add the following to your LocaleBean to automatically reset the locale on the current view:
#javax.enterprise.context.SessionScoped
public class LocaleBean implements Serializable {
...
public void resetLocale(#Observes #BeforePhase(JsfPhaseId.RENDER_RESPONSE) PhaseEvent event) {
event.getFacesContext().getViewRoot().setLocale(this.locale);
}
}

Can't apply plain HTML class to a Blazor component

I've tried to use this code in my .NET 5 Blazor project, in .razor file:
<SignedLabel class="some-css-class" Price=123 Currency=Currency.Usd />
where SignedLabel - is a Blazor component and Price, Currency is the component's input parameters. I expect Blazor to treat the class word as an html property and apply the plain HTML class to this component so that I can style this component later on. But Blazor actually treats it as another input parameter for component and crashes whole app rendering with error:
Object of type 'SignedLabel' does not have a property matching the name 'class'
So the questions is
Is it possible to use the class property in a such way?
If yes, how should I do this?
PS: project settings:
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>9</LangVersion>
</PropertyGroup>
...
</Project >
You cannot apply a class to a Component. You can use splatting to capture attributes placed on a Component to pass as parameter to one of the components elements.
SomeComponent.razor
<div #attributes="#CapturedAttributes">
Hello
</div>
#code {
[Parameter(CaptureUnmatchedValues = true)]
public Dictionary<string,object> CapturedAttributes { get; set; }
}
##Usage
<SomeComponent id="fred" class="some-css-class" style="width:100vh" />
Will render:
<div id="fred" class="some-css-class" style="width:100vh" >
Hello
</div>
Docs
You just have to create a Parameter in your component. For example, this works fine. In a Tree component, TopDivClass parameter has been added an used.
In the markup of the component:
<div class="#TopDivClass">
In the code behind of the component:
[Parameter]
public string TopDivClass { get; set; }
In the markup using the component:
<Components.Tree TopDivClass="TreeView" />
The result in the HTML is
<div class="TreeView" ...
I've encountered a similar issue and Google led me here, so I share it for anyone who might hit this as well.
My project setup is .NET 7 and Teams App project template (Blazor Server).
The problem was similar to OP's - I wanted to apply CSS class to Web Component. In my case it was FluentTextField, so I couldn't modify its code.
Although doing this:
<FluentTextField
class="w-100"
Placeholder="Provide the URL"
Required="true"/>
wasn't producing any errors and in browser Dev Tools I could see the class added the element, i.e. <fluent-text-field class='w-100'>, the CSS defined in parent component wasn't applied.
Turns out, it's enough to change "Blazor syntax" to "JS syntax" and everything works fine:
<fluent-text-field
class="w-100"
Placeholder="Provide the URL"
Required="true"/>

Add components based on string variables in Blazor

We're creating a dynamic page of components in Blazor. The intention is to have dynamic applets displayed on a page. The idea is that we have a list of strings which correspond to Component names. We read through the string list and for each one, instantiate a blazor component or render fragment. These are just simple components, no passed in parameters or the like. ie:
string[] componentsStrings = {"Component1", "Component2"};
Expected output:
<Component1 />
<Component2 />
We can't come up with a way to do this. It seems like a fairly standard thing to do, but perhaps not? Does anyone know if this is even possible?
You will have to programmatically create a component which adds your custom components on the page using RenderTreeBuilder.
Chris Sainty has a blog post on this which you can read here: https://chrissainty.com/building-components-via-rendertreebuilder/
Basically there is an override for BuildRenderTree in the ComponentBase class which can be used:
public class Menu : ComponentBase
{
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
base.BuildRenderTree(builder);
builder.OpenElement(0, "nav");
builder.AddAttribute(1, "class", "menu");
}
}
Here is another tutorial.
Some tips from here:
Place base.BuildRenderTree(builder); at the start of the
BuildRenderTree method , not at the end.
Always start with the value 0 for the sequence parameter.

How to check Custom Element is registered?

Some method creates new instance of my custom element (created with polymer) and attaches it on page. But I want to check is Element registered before add it and print error to console in bad case. I mean what if I forgot import component html declaration:
<!--I forgot write it in my HTML file -->
<!--<link rel="import" href="packages/visualytik/vis_starter.html">-->
So, in case when I forgot import I want to print error in console.
I know one tricky method:
import 'my_custom_component.dart';
Element component = new Element.tag('my-custom-component');
bool registered = component is MyCustomComponent;
But it's hard method because we should create component first and have to import MyCustomComponent.dart in dart file. Can I check it in other way? Something like:
document.isRegistered('my-custom-component');
Update3
You can also use the new #HtmlImport annotation. If you import the class, then you can be sure you also have imported the HTML of the element. See also https://stackoverflow.com/a/29710355/217408
Update2
See Hunting down unregistered elements
Update
Use a custom constructor in your elements class and do the registration there but only if it wasn't done already.
class MyCustomComponent extends ... {
bool _isRegistered;
bool get isRegistered => _isRegistered;
factory MyCustomComponent() {
if(!isRegistered) {
registerElement();
_isRegistered = true;
}
return new Element.tag('my-custom-element');
}
}
and then create new instances like
new MyCustomElement();
and you can always be sure the element is registered only once (but you always need to use this constructor of course).
Original
If you register your elements by calling document.RegisterElement() yourself instead of relying on Polymer for example, you need to hold a reference to the constructor reference document.RegisterElement() returns, otherwise you won't be able to create an instance of the element.
Therefore you just need to check if you already have a reference to the constructor. See also https://developer.mozilla.org/en-US/docs/Web/API/Document/registerElement

Programmatically create HTML form fieldset tag with JSF

In my Java code I want to programmatically create a <fieldset> tag that I can use in my JSF form.
The setup of my form looks like this:
Application app = FacesContext.getCurrentInstance().getApplication();
HtmlForm form = (HtmlForm) app.createComponent(HtmlForm.COMPONENT_TYPE);
form.setStyleClass("pure-form pure-form-stacked");
As you can see I use HtmlForm.COMPONENT_TYPE as an identifier for the JSF UI component but I haven't found an identifier for a fieldset so I tried:
UIComponent fieldset = app.createComponent("fieldset");
form.getChildren().add(fieldset);
Unfortunately this is not working so I have to come up with another solution. Do you have any ideas?
Is there a general approach how HTML tags (which are unknown in the JSF context) can be created?
You can try the following:
Theres a component called <f:verbatim> which you would use in xhtml like this:
<f:verbatim escape="false">
<fieldset id="blah"></fieldset>
</f:verbatim>
To achieve that programmaticlly you can add this component like this:
String fieldsetHTMLText ="<fieldset id=\"blah\"></fieldset>";
UIOutput verbatim = new UIOutput();
verbatim.setRendererType("javax.faces.Text");
verbatim.getAttributes().put("escape", false);
verbatim.setValue(fieldsetHTMLText);
I found three solutions to my problem. The first one is to use PrimeFaces, the second one is to use MyFaces Tomahawk and the third one is to use a JSF Verbatim UI component with string input. I will shortly list up code samples and the differences between the solutions.
1 PrimeFaces
With an include of the PrimeFaces components suite (and it's Apache Commons FileUpload dependency) one can use the Fieldset class to programatically create a fieldset on-the-fly. The bad thing on that is, that the PrimeFaces Fieldset component is depends on a PrimeFaces JavaScript file so instead of the plain fieldset, one will get a fieldset and a JavaScript include which is way too much.
import org.primefaces.component.fieldset.Fieldset;
...
form.getChildren().add(new Fieldset());
2 MyFaces Tomahawk
The UI component set Tomahawk also comes with a Fieldset component that can be used to create an HTML fieldset programatically. If the Fieldset of Tomahawk will be used, then one will get a plain and nice-looking fieldset tag. The bad thing here is that Tomahawk is an extension to MyFaces and MyFaces itself is a whole JavaServer Faces implementation which should not be used alongside standard JSF.
import org.apache.myfaces.custom.fieldset.Fieldset
...
form.getChildren().add(new Fieldset());
3 JSF Verbatim UI Component
The standardized and hacky way is to use a JSF Verbatim UI component. Within a verbatim component you are allowed to put any HTML needed. With this little trick we can create a verbatim tag:
UIOutput fieldset = new UIOutput();
fieldset.setRendererType("javax.faces.Text");
fieldset.getAttributes().put("escape", false);
fieldset.setValue("<fieldset></fieldset>");
The code shown above renders a fieldset HTML element but because it is a string and the tag inside the string is closed you cannot programatically append anything to that tag, so this won't work:
form.getChildren().add(fieldset);
To generate an HTML tag that can be used for nesting of elements, each opening and closing tag must be put in an own Varbatim component which makes this solution very text heavy:
UIOutput fieldsetStart = new UIOutput();
fieldsetStart.setRendererType("javax.faces.Text");
fieldsetStart.getAttributes().put("escape", false);
fieldsetStart.setValue("<fieldset>");
UIOutput fieldsetClose = new UIOutput();
fieldsetClose.setRendererType("javax.faces.Text");
fieldsetClose.getAttributes().put("escape", false);
fieldsetClose.setValue("</fieldset>");
HtmlInputText inputText = (HtmlInputText) app.createComponent(HtmlInputText.COMPONENT_TYPE);
form.getChildren().add(fieldsetStart);
form.getChildren().add(inputText);
form.getChildren().add(fieldsetClose);
Conclusion:
None of the solutions shown is really elegant. PrimeFaces und MyFaces have large dependencies and the standard JEE way requires practally much writing effort. I had hoped to find a nice solution to produce unknown / custom HTML elements, such as: document.createElement("fieldset");.
If anyone knows a way to do that, please post the solution.