I'm trying to build some select boxes with dependencies on other select boxes. I'm fairly new to Seam - just to add this. Here is my JSF - for test purposes I'm trying to display a dataTable
<f:facet name="header">Profil</f:facet>
<a:form ajaxSubmit="true" reRender="testTable">
<s:decorate id="techTypeField" template="layout/edit.xhtml">
<ui:define name="label">Choose tech</ui:define>
<h:selectOneRadio id="techType" value="#{technologyType}" onchange="submit()">
<s:selectItems value="#{technologyTypes}" var="elem" label="#{elem.name}" id="typeId"/>
<s:convertEntity/>
</h:selectOneRadio>
</s:decorate>
</a:form>
<div style="clear:both"/>
<h:dataTable var="productLine" id="testTable" value="#{productLines}" rendered="#{productLines.rowCount > 0}">
<h:column >
<f:facet name="header">pl</f:facet>
#{productLine.id}
</h:column>
</h:dataTable>
My backing bean
package de.ac.dmg.productfinder.workflow;
import java.util.List;
import javax.ejb.Remove;
import javax.ejb.Stateful;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.jboss.seam.annotations.Factory;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.datamodel.DataModel;
import org.jboss.seam.annotations.datamodel.DataModelSelection;
import org.jboss.seam.international.StatusMessages;
import org.jboss.seam.log.Log;
import de.ac.entity.Machine;
import de.ac.entity.ProductLine;
import de.ac.entity.TechnologyType;
#Stateful
#Name("Profil")
public class ProfilBean implements IProfil {
#PersistenceContext
private EntityManager entityManager;
#Logger
private Log log;
#DataModel(value = "technologyTypes")
private List<TechnologyType> technologyTypes;
#DataModelSelection(value = "technologyTypes")
private TechnologyType technologyType;
#DataModel(value = "productLines")
private List<ProductLine> productLines;
#DataModel(value = "machines")
List<Machine> machines;
#In
StatusMessages statusMessages;
// add additional action methods
#Factory("technologyTypes")
public void loadTechTypes() {
technologyTypes = entityManager.createQuery("Select t from TechnologyType t").getResultList();
}
#Factory("productLines")
public void loadProductLinies() {
System.out.println(technologyType);
productLines = entityManager.createQuery("select p from ProductLine p where p.technologyType = :type ")
.setParameter("type", technologyType).getResultList();
}
public void next() {
// implement your business logic here
log.info("Profil.next() action called with: #{Profil.value}");
statusMessages.add("next #{Profil.value}");
}
#Remove
public void destroy() {
}
}
I can set the tech type on time - after one click it isn't refreshed sadly. What I'm doing wrong here?
When selecting a 'technology type', you need an action which reloades the product types based on the selected techtype. Something like action=#{Profil.loadProductLines()}
Related
JVM Version: 14.0.2+12-46
myfaces-api 2.3.9
myfaces-impl 2.3.9
apache-tomee-webprofile-8.0.4
I am using the following converter for an simple entity class:
package view;
import entities.Chain;
import managed.Chains;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.convert.FacesConverter;
import javax.inject.Inject;
import javax.inject.Named;
#Named
#FacesConverter(value = "chainConverter", managed = true)
public class ChainConverter implements Converter<Chain> {
#Inject
Chains chains;
#Override
public Chain getAsObject(FacesContext context, UIComponent component, String value) {
if (value != null && !value.trim().isEmpty()) {
Chain found = chains.getAvailable().stream()
.filter(chain -> chain.getAbbreviation().equals(value))
.findFirst()
.orElse(null);
return found;
}
return null;
}
#Override
public String getAsString(FacesContext context, UIComponent component, Chain chain) {
if (chain != null && !chain.toString().isEmpty()){
return chain.getAbbreviation();
}else{
return null;
}
}
}
Given the example at: https://www.primefaces.org/showcase/ui/input/oneMenu.xhtml
I try to use the converter as follows:
<p:selectOneMenu id="chain" value="#{chains.chain}" var="c" converter="chainConverter">
<f:selectItem itemLabel="== Selecte Chain ==" />
<f:selectItems value="#{chains.available}" var="chain" itemLabel="#{chain.name}" itemValue="#{chain}" />
<p:column>
#{c.name}
</p:column>
<p:ajax event="valueChange" listener="#{inventories.refreshSurvey}" update="#form" />
</p:selectOneMenu>
The above "converter=" reference produces the following error:
org.primefaces.application.exceptionhandler.PrimeExceptionHandler.logException null
java.lang.NullPointerException
at org.apache.myfaces.cdi.converter.FacesConverterCDIWrapper.getAsString(FacesConverterCDIWrapper.java:69)
at org.primefaces.renderkit.SelectRenderer.getOptionAsString(SelectRenderer.java:207)
at org.primefaces.component.selectonemenu.SelectOneMenuRenderer.encodeOption(SelectOneMenuRenderer.java:561)
at org.primefaces.component.selectonemenu.SelectOneMenuRenderer.encodeSelectItems(SelectOneMenuRenderer.java:524)
at org.primefaces.component.selectonemenu.SelectOneMenuRenderer.encodeHiddenSelect(SelectOneMenuRenderer.java:211)
If I change the converter reference to an el expression as follows:
converter="#{chainConverter}"
I get the error:
org.primefaces.application.exceptionhandler.PrimeExceptionHandler.logException Cannot convert [entities.Chain#928f] of type [class java.lang.String] to [class entities.Chain]
javax.el.ELException: Cannot convert [entities.Chain#928f] of type [class java.lang.String] to [class entities.Chain]
at org.apache.el.lang.ELSupport.coerceToType(ELSupport.java:548)
at org.apache.el.parser.AstValue.setValue(AstValue.java:203)
at org.apache.el.ValueExpressionImpl.setValue(ValueExpressionImpl.java:263)
at org.apache.webbeans.el22.WrappedValueExpression.setValue(WrappedValueExpression.java:93)
at org.apache.webbeans.el22.WrappedValueExpression.setValue(WrappedValueExpression.java:93)
However, the weirdest thing is that in one project it works while in the other project it does not. For example in this sample project it works as expected: https://github.com/dannymk/PrimefacesTest
I have a nagging suspicion that there is a difference in a library version someplace in this project. However, I really can't pinpoint the problem even after changing the pom file to match a working projects pom file. This is definitely a head scratcher to me.
I'm making a web file browser using ZK Components and find a block. Is there any way to update the ListBox model using the selected item of the listbox?
The use case is when traversing the files and folder, the user click the folder, and the list is refreshed with the content of the selected folder. The selection event is triggered and for regular file, it handle well, but not the folder.
My Code:
myfilesvm.zul
<zk>
<window apply="org.zkoss.bind.BindComposer"
viewModel="#id('vm') #init('com.my.zk.mvvm.MyFilesViewModel')">
<hlayout>
<listbox vflex="true" hflex="1" model="#load(vm.files)"
id="fileBrowser" selectedItem="#bind(vm.selectedFile)">
<auxhead>
<auxheader colspan="3">File List</auxheader>
<auxheader colspan="3">
<hlayout>
<!-- breadcrumb, implemented later -->
</hlayout>
</auxheader>
</auxhead>
<listhead>
<listheader label="Name" />
<listheader label="Size" />
<listheader label="Modified" />
</listhead>
<template name="model" var="file">
<listitem>
<listcell label="#load(file.name)" />
<listcell label="#load(file.length())" />
<listcell label="#load(file.lastModified())" />
</listitem>
</template>
</listbox>
</hlayout>
<separator />
</window>
</zk>
MyFilesViewModel.java
package com.my.zk.mvvm;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Arrays;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.bind.annotation.Init;
import org.zkoss.bind.annotation.NotifyChange;
import org.zkoss.zul.Filedownload;
import org.zkoss.zul.ListModel;
import org.zkoss.zul.ListModelList;
import com.my.zk.FileCrumbManager;
public class MyFilesViewModel {
private static Logger log = LoggerFactory.getLogger(MyFilesViewModel.class);
// AuthenticationService authService = new AuthenticationServiceImpl();
// UserCredential cre = authService.getUserCredential();
String homeFolder = "D:\\path\\home";
ListModel<File> files = new ListModelList<File>(Arrays.asList(FileCrumbManager.populateList(new File(homeFolder))));
File selectedFile;
#Init
public void init() { // Initialize
}
public ListModel<File> getFiles() {
return files;
}
#NotifyChange({ "selectedFile" })
public void setFiles(ListModel<File> files) {
this.files = files;
}
public File getSelectedFile() {
return selectedFile;
}
public void pilihFile() {
if (getSelectedFile().isDirectory()) {
log.info("File is a directory");
this.files = new ListModelList<File>(
Arrays.asList(FileCrumbManager.populateList(new File(getSelectedFile().getAbsolutePath()))));
} else {
try {
Filedownload.save(getSelectedFile(), null);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
log.error(e.getMessage());
}
}
}
public void setSelectedFile(File selectedFile) {
this.selectedFile = selectedFile;
pilihFile();
}
}
Appreciate for any help. Thank you.
The correct way to refresh ListModelList in pilihFile() is:
this.files.clear();
this.files.addAll(Arrays.asList(FileCrumbManager.populateList(new File(getSelectedFile().getAbsolutePath()))));
Because Listbox is model-driven rendering, you should control the rendering by manipulating the model object. When you call methods of ListModelList, it will notify Listbox to render into a browser. If you replace this.files with a new object, ZK doesn't know it. That's why your browser doesn't have the update.
package de.gdv.sp.configuration;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.captcha.botdetect.web.servlet.CaptchaServlet;
#Configuration
public class CaptchaConfiguration {
#Bean(name = "captchaServlet")
public ServletRegistrationBean captchaServlet() {
return new ServletRegistrationBean(new CaptchaServlet(), "/kontakt");
}
}
I am trying to implement BotDetect Captcha in our Spring MVC/Boot project.When I tried to create servlet with annotations( without web.xml) I always get the following screen:screenshot of http://localhost:8080/kontakt
Moreover when I write the HTML code of this captcha, I get the following result.Botdetect Captcha does not show picture
<botDetect:captcha id="exampleCaptcha"/>
<div class="validationDiv">
<input id="captchaCode" type="text" name="captchaCode"
value="${basicExample.captchaCode}"/>
<input type="submit" name="submit" value="Submit" />
<span class="correct">${basicExample.captchaCorrect}</span>
<span class="incorrect">${basicExample.captchaIncorrect}</span>
</div>
How can I solve this problem?
[BotDetect Captcha website][3]
You can try either:
extend WebApplicationInitializer
package de.gdv.sp.configuration;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.captcha.botdetect.web.servlet.CaptchaServlet;
#Configuration
public class CaptchaConfiguration extends WebApplicationInitializer {
#Bean(name = "captchaServlet")
public ServletRegistrationBean captchaServlet() {
return new ServletRegistrationBean(new CaptchaServlet(), "/kontakt");
}
}
move your bean definition into a class that extends WebApplicationInitializer.
#Configuration
public class WebXMLReplacement extends WebApplicationInitializer {
//other configurations
#Bean(name = "captchaServlet")
public ServletRegistrationBean captchaServlet() {
return new ServletRegistrationBean(new CaptchaServlet(), "/kontakt");
}
}
There is Standard way to Register your custom servlet in Spring MVC application. You need to Create Initializer Class by implementing WebApplicationInitializer.
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration.Dynamic;
import org.springframework.web.WebApplicationInitializer;
import com.captcha.botdetect.web.servlet.CaptchaServlet;
public class MyServletInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext)
throws ServletException {
Dynamic myServlet = servletContext.addServlet("kontakt", CaptchaServlet.class);
myServlet.addMapping("/kontakt");
}
}
You could use this approach to register DispatcherServlet
manually.Similarly, you can register listeners and filters by creating a new implementation
of WebApplicationInitializer.
I have seen a few postings related to this. However, I seem to be seeing something that I do not understand. Following is a section of my code:
package com.mvctest;
import java.util.List;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import com.dao.User;
#Controller
public class LoginController {
#RequestMapping("/newAccount")
public String createAccountScreen (Model model) {
model.addAttribute("user", new User());
return "createaccountscreen";
}
#RequestMapping(value="/createAccount", method=RequestMethod.POST)
public String createAccount (#Valid User user, BindingResult result) {
if (result.hasErrors()) {
List<ObjectError> errors = result.getAllErrors();
for(ObjectError error : errors) {
System.out.println(error.getDefaultMessage());
}
return "createaccountscreen";
}
return "accountcreated";
}
Then in my jsp file createaccountscreen.jsp I have the following:
<sf:form method="post"
action="${pageContext.request.contextPath}/createAccount"
commandName="user">
This works fine.
However if I changed the addAttribute () call as following
model.addAttribute("myuser", new User());
and changed the commandName in the jsp to
<sf:form method="post"
action="${pageContext.request.contextPath}/createAccount"
commandName="myuser">
I get the following error
java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'myuser' available as request attribute
Essentially, I replace 'user' with 'myuser'.
Bean class is 'User'. Does that mean the commandName needs to be 'user'?
I have made a new Seam validator:
package validators;
import java.io.Serializable;
import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.context.FacesContext;
import javax.faces.validator.ValidatorException;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.faces.Validator;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.log.Log;
import org.jboss.seam.log.Logging;
#Name("roCountyValidator")
#Validator
#BypassInterceptors
public class RoCountyValidator implements javax.faces.validator.Validator,
Serializable {
/**
*
*/
private static final long serialVersionUID = -3876319398131645955L;
Log log = Logging.getLog(RoCountyValidator.class);
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
log.info("validating....!");
if (String.valueOf(value).equals("Arad"))
((UIInput) component).setValid(true);
else {
((UIInput) component).setValid(false);
FacesMessage message = new FacesMessage();
message.setDetail("Invalid county");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
}
}
The problem is that I do not know how to use it directly from jsf...
The following does not work....
I have declared it in a special taglib file: myvalidators.taglib.xml
<facelet-taglib>
<namespace>http://example.com/jsf/my/validators</namespace>
<tag>
<tag-name>roCountyValidator</tag-name>
<validator>
<validator-id>roCountyValidator</validator-id>
</validator>
</tag>
and tried to use it like:
<h:inputText id="someField" value="#{booking.creditCardName}"
required="true" label="County">
<my:roCountyValidator/>
<h:message for="someField"/>
</h:inputText>
Can you tell me where I am wrong?
Thanks.
Two ways to solve this.
One, is to use as #BalusC has written.
You don't need to define anything in faces-config.xml
<h:inputText id="cc" required="true" value="#{booking.creditCardName}">
<f:validator validatorId="roCountyValidator"/>
<f:attribute name="oldCreditCardNumber" value="#{booking.creditCardName}" />
<s:validate />
</h:inputText>
Here you can even bind the old credit card number, if you want to check that also.
Then in your validate method:
public void validate(FacesContext context, UIComponent component,
Object value) throws ValidatorException {
log.info("validating....!");
String oldCreditcard = String.valueOf(component.getAttributes().get("oldCreditCardNumber"));
String newCreditCard = (String) value;
if(SomeClass.isCorrectCreditcard(newCreditCard)) {
//You don't need to setValid(false), this is done automatically
Map<String, String> messages = Messages.instance();
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR, messages.get("wrongCreditCardNumber"), messages
.get("wrongCreditCardNumber")));
}
}
The other way, is to use the validator tag in <h:inputText>
You don't even need to create a #Validator class, as long as it's a seam component and if you use the same method signature.
I use a validator component for all my general validators
#Name("validator")
#Scope(ScopeType.EVENT)
#BypassInterceptors
public class Validator {
public void positiveInteger(FacesContext context, UIComponent toValidate, Object value) {
String val = (String) value;
try {
int v = Integer.parseInt(val);
if (v < 0)
throw new NumberFormatException();
} catch (NumberFormatException e) {
((UIInput) toValidate).setValid(false);
FacesMessages.instance().addToControlFromResourceBundle(toValidate.getId(), "invalid.integer");
}
}
}
Now you can add the validator:
<h:inputText value="#{foo.bar}" required="true" validator="#{validator.positiveInteger}">
<s:validate/>
<h:inputText>
I have no idea about the Seam part, it might have different approaches for this, but in standard JSF, you normally define it as <validator> in faces-config.xml.
<validator>
<validator-id>roCountyValidator</validator-id>
<validator-class>validators.RoCountyValidator</validator-class>
</validator>
and use it as follows:
<h:inputText>
<f:validator validatorId="roCountyValidator" />
</h:inputText>
Solution found:).
Forget about taglibs and stuff!
Use it like:
<h:inputText id="someField" value="#{booking.creditCardName}"
required="true" label="County" validator="roCountyValidator">
<h:message for="someField"/>
</h:inputText>
Please remark that
validator="roCountyValidator"
it shouldn't be used like EL expression !!! (my first wrong decision)
So the advantage of using Seam + #Validator: Seam will transform that component in the background to a jsf validator so you do no longer need jsf validator tags or any other configuration in faces-config.xml.