wicket form event onsubmit not working at V6.X - ajax

I have a page like this.
HTML
<form wicket:id="form" onsubmit="alert('form submit')">
<input type="text" name="name">
<!-- Wicket1.5.X form.onsubmit working -->
<!-- Wicket6.X form.onsubmit can not work -->
<input wicket:id="ajaxFallbackButton" type="submit">
<!-- form.onsubmit works fine -->
<input wicket:id="submitBtn" type="submit">
</form>
Java
Form<Void> form = new Form<Void>("form");
form.setOutputMarkupId(true);
add(form);
AjaxFallbackButton ajaxFallbackButton = new AjaxFallbackButton("ajaxFallbackButton", new Model<String>("AjaxFallbackButton"), form) {
private static final long serialVersionUID = 1L;
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
System.out.println("ajaxFallbackButton onSubmit");
}
};
form.add(ajaxFallbackButton);
Button button = new Button("submitBtn", new Model<String>("Button"));
form.add(button);
Why the event of form onsubmit defined int the html can not work when I used Wicket V6.X?
Thanks for svenmeier, I fixed it by override the AjaxFallbackButton.updateAjaxAttributes method.
#Override
protected void updateAjaxAttributes(AjaxRequestAttributes attributes) {
super.updateAjaxAttributes(attributes);
AjaxCallListener ajaxCallListener = new AjaxCallListener();
ajaxCallListener.onPrecondition("alert('form submit')");
attributes.getAjaxCallListeners().add(ajaxCallListener);
}

Wicket 6 uses JavaScript event handlers, so your inline JavaScript is not executed.
Read here for further explanation and solutions:
Wicket AjaxButton and javascript condition

Related

unable to render index or mobile page with spring mobile

I was able to get my project to run just fine until I brought in spring-mobile. Now only Strings will return in the browser that the spring-mobile method is returning:
#Controller
public class DeviceDetection {
private Logger logger = LoggerFactory.getLogger(DeviceDetection.class);
#RequestMapping(value="/index")
public String detectDevice(Device device) {
if (device.isNormal()) {
System.out.println("Inside isNormal()");
return "index";
} else if (device.isMobile()) {
System.out.println("Inside isMobile()");
return "mobilePage";
} else if (device.isTablet()) {
return "mobilePage";
}
return "index";
}
}
So I decided I needed internalResourceViewResolver but that just gives me the following error:
Error creating bean with name 'viewResolver' defined in class path resource
[org/springframework/boot/autoconfigure/web/WebMvcAutoConfiguration$WebMvcAutoCon
figurationAdapter.class]: Initialization of bean failed; nested exception is
org.springframework.beans.factory.BeanCreationException: Error creating bean
with name 'org.springframework.boot.autoconfigure.mobile
and
DeviceDelegatingViewResolverAutoConfiguration$DeviceDelegatingViewResolverConfigur
ation$InternalResourceViewResolverDelegateConfiguration.viewResolver; nested
exception is
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No
qualifying bean of type
[org.springframework.web.servlet.view.InternalResourceViewResolver] is
defined: expected single matching bean but found 2:
getViewResolver,defaultViewResolver
Resolver class
#Configuration
public class MvcConfiguration extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver getViewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/");
resolver.setSuffix(".html");
return resolver;
}
#Override
public void configureDefaultServletHandling(
DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
Main Class
#SpringBootApplication
public class StidhamFinancialApplication extends SpringBootServletInitializer {
public static void main(String[] args) throws UnknownHostException {
SpringApplication app = new SpringApplication(StidhamFinancialApplication.class);
Environment env = app.run(args).getEnvironment();
System.out.println(String.format("Access URLs:\n----------------------------------------------------------\n\t" +
"Local: \t\thttp://127.0.0.1:%1s\n\t" +
"External: \thttp://%2s:%3s\n----------------------------------------------------------",
env.getProperty("server.port"),
InetAddress.getLocalHost().getHostAddress(),
env.getProperty("server.port")));
}
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(StidhamFinancialApplication.class);
}
}
project structure:
I am running it on Tomcat 8...
--------Update 1---------------
I deleted my configuration package and add #ResponeBody to my detectDevice method like so:
//If I remove #ResponseBody it complains of a Circular view path [index] with
#ResponseBody it just renders a String and not a view
#RequestMapping(value="/index")
public #ResponseBody String detectDevice(Device device) {
if (device.isNormal()) {
System.out.println("Inside isNormal()");
return "index";
} else if (device.isMobile()) {
System.out.println("Inside isMobile()");
return "mobilePage";
} else if (device.isTablet()) {
return "mobilePage";
}
return "index";
}
Only the String index or mobilePage will render in the browser. If I delete the #ResponseBody then the project gives an error saying the following:
2016-05-29 08:55:03.682 ERROR 8230 --- [nio-8080-exec-1]
o.s.boot.context.web.ErrorPageFilter : Forwarding to error page
from request [/index.html] due to exception [Circular view path
[index]: would dispatch back to the current handler URL [/index] again.
Check your ViewResolver setup! (Hint: This may be the result of an
unspecified view, due to default view name generation.)]
If I remove the /index then the method is ignored completely and spring-boot just maps my index file and works again but I need the mobile site to work.
-------------------UPDATE 2-------------------
Ok I added thymeleaf and my index page is rendering again. Even better the DeviceDetection is working but once my project looks for my mobilePage I get the following error:
Error resolving template "mobile/index", template might not exist or might not be accessible by any of the configured Template Resolvers
I had to add the follow ing #Configuration to get the project to work. The spring-boots defaultViewResolver was causing me to many issues. Here is the project structure now:
I updated the project on GIT too.
First I needed to use something like Thymeleaf and create a viewResolver. Spring-boots defaultViewResolver was causing me issues:
#Configuration
public class WebConfiguration extends WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter {
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
#Bean
public DispatcherServlet dispatcherServlet(){
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
return dispatcherServlet;
}
#Bean
public ServletContextTemplateResolver templateResolver() {
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setPrefix("/WEB-INF/");
resolver.setSuffix(".html");
resolver.setTemplateMode("HTML5");
resolver.setOrder(1);
return resolver;
}
}
Since I was using Thymeleaf I needed bindingResult and a few other things. This is also where I implemented springs mobile Device interface to detect the device.
#Controller
public class FormController {
#RequestMapping(value="/", method= RequestMethod.GET)
public String contactForm(#Valid #ModelAttribute("person") Contact contact, BindingResult bindingResult,
HttpServletRequest request, Model model, Device device) throws IOException {
if(bindingResult.hasErrors()){
System.out.println("There was a error "+bindingResult);
return "error";
}
model.addAttribute("contact", new Contact());
if(device.isNormal()){
System.out.println("I am normal");
return "index";
} else if(device.isMobile()){
System.out.println("I am a mobile");
return "mobilePage";
} else if (device.isTablet()){
System.out.println("I am a tablet");
return "mobilePage";
}
return "index";
}
Now I just needed to put the different views in the correct directories like so:
That was it and it is working perfectly.
Here is the form I created to use implement Thymeleaf:
<!-- Contact form -->
<form action="#" th:action="#{/contact}" th:object="${contact}" method="POST" class="contact-form clear-fix">
<div class="clear-fix">
<ul class="list-0 clear-fix">
<!-- Name -->
<li>
<div class="block field-box">
<label for="contact-form-name" class="text-color-black">Your Name</label>
<input type="text" th:field="*{name}" class="contact noMarr col-md-6 text-color-black" id="contact-form-name" />
</div>
</li>
<!-- /Name -->
<!-- E-mail address -->
<li>
<div class="block field-box">
<label for="contact-form-mail" class="text-color-black">Your E-mail</label>
<input type="text" th:field="*{email}" class="contact noMarr col-md-6 text-color-black" id="contact-form-mail" />
</div>
</li>
<!-- /E-mail address -->
<!-- Website URL -->
<li>
<div class="block field-box">
<label for="contact-form-subject" class="text-color-black">Subject</label>
<input type="text" th:field="*{subject}" class="contact noMarr col-md-6 text-color-black" id="contact-form-subject" />
</div>
</li>
<!-- /Website URL -->
<!-- Message -->
<li>
<div class="block field-box ">
<label for="contact-form-message" class="text-color-black">Your message</label>
<textarea name="comment" class="contact col-md-12 text-color-black" th:field="*{message}" id="contact-form-message"></textarea>
</div>
</li>
<!-- /Message -->
<!-- Submit button -->
<li>
<div class="block field-box field-box-button text-color-black">
<input type="submit" id="contact-form-submit" name="contact-form-submit" class="button text-color-black" value="Submit"/>
</div>
</li>
<!-- /Submit button -->
</ul>
</div>
</form>

Why #ViewScoped Managed Bean's #PostConstruct init() method never called?

I am newbie to JSF/Java ee7 and tried to test some features to understand how things work.
I used templating for testing purposes. Here is the relevant JSF template client:
<ui:define name="content">
<h:panelGroup class="keret" id="tartalom">
<h:form id="email" rendered="#{emailManagedBean.urlap}">
<h:messages/>
<h:outputLabel for="emailbox" value="Add meg az email címed, és juss hozzá bestsellerünkhöz 5Ft-ért"/>
<h:inputText value="#{emailManagedBean.newEmail.email}"/>
<h:commandButton actionListener="#{emailManagedBean.saveEmail()}" value="Mentés">
<f:ajax immediate="true" execute="#form" render=":tartalom"/>
</h:commandButton>
</h:form>
<h:panelGroup id="szoveg" rendered="#{not emailManagedBean.urlap}">
<h3>Köszönjük!</h3>
<p>
Hamarosan emailt fogsz kapni tőlünk. Kérlek, ellenőrizd a levélszemét, illetve spam mappákban is a tőlünk kapott levelet. A levlében lévő linkre kattintva hozzájuthatsz az ajándékodhoz.
</p>
</h:panelGroup>
</h:panelGroup>
</ui:define>
My managed bean:
#Named(value = "emailManagedBean")
#ViewScoped
public class emailManagedBean implements Serializable {
#EJB
EmailsFacadeLocal emailFacade;
private Emailcamp newEmail;
private boolean urlap;
public Emailcamp getNewEmail() {
return newEmail;
}
public void setNewEmail(Emailcamp newEmail) {
this.newEmail = newEmail;
}
#PostConstruct
public void init() {
newEmail = new Emailcamp();
urlap=true;
}
public boolean isUrlap() {
return urlap;
}
public void setUrlap(boolean urlap) {
this.urlap = urlap;
}
public void validateEmail(FacesContext context, UIComponent comp,
Object value) {
String input = (String) value;
EmailValidator validator = EmailValidator.getInstance();
if (!validator.isValid(input)) {
((UIInput) comp).setValid(false);
FacesMessage message = new FacesMessage(
"Helytelen email cím formátum");
context.addMessage(null, message);
} else {
if(!emailFacade.exist(newEmail.getEmail())){
((UIInput) comp).setValid(true);
}
else {
context.addMessage(null, new FacesMessage("Már megadtad korábban az email címed"));
}
}
}
public void saveEmail() {
/*Get current date and time*/
Calendar c = Calendar.getInstance();
newEmail.setRecemaildate(c.getTime());
/*Get Referral*/
HttpServletRequest hr = (HttpServletRequest) FacesContext.getCurrentInstance().getExternalContext().getRequest();
newEmail.setReferer((hr.getHeader("referer")==null) ? "not available" : hr.getHeader("referer") );
newEmail.setStatus(Status.NOT_SENT.toString());
emailFacade.create(newEmail);
urlap=false;
}
}
The expected behavior is that the init function set urlap boolean variable to true so the h:form should be shown as there is conditional rendering on the view side.
Later the customer clicks on Save ("Mentés") and the new email address and some data will be stored in a database. After the ajax call I urlap boolean varaible is set to false not to render the form again but render a thank you message.
Unfortunately the init function is never called so the boolean variable never set to true. As a consequences my thanks message is shown without collecting the email.
The second strange thing which is the consequence of the ajax component (I guess) that I got an error message: outputScript with no library, no name, and no body content. I did not declare any outputScript in my xhtml.

Why update method isn't calling?

I have a problem. When I click to update image on form, update method isn't calling. I see that in my output logs window. Only may update image in first row in my table.
When I updating first row:
Hibernate: update shoe_goods set description=?, image=?, in_stock=?,
name=?, price=?, size=?, web_store_id=? where id=?
When other rows - nothing.
I save my images in db as byte.
This is my method to display images:
Controller:
#RequestMapping(value = "/imageController/{id}")
#ResponseBody
public byte[] getImages(#PathVariable int id) {
return shoeGoodsService.getById(id).getImage();
}
JSP:
<img src="/imageController/${goods.id}" alt="Image" />
Code in DAO class:
#Override
public void update(ShoeGoods shoeGoods) {
sessionFactory.getCurrentSession().merge(shoeGoods);
sessionFactory.getCurrentSession().getTransaction().commit();
}
Code in Service class:
#Override
public void update(ShoeGoods shoeGoods) {
shoeGoodsDAO.update(shoeGoods);
}
Code in controller, when update method is call only for first row in table:
#RequestMapping(value="/upload_shoe_image", method = RequestMethod.POST)
public #ResponseBody ModelAndView handleFileUpload1(#RequestParam("file") MultipartFile file){
...
try {
ShoeGoods shoeGoods = shoeGoodsService.getById(goodsId);
shoeGoods.setImage(file.getBytes());
//not call always
shoeGoodsService.update(shoeGoods);
} catch (Exception e) {
e.printStackTrace();
}
...
}
In JSP:
<form id="send-image-form" method="POST" enctype="multipart/form-data"
action="/upload_shoe_image">
Edit: <input type="file" name="file">
<br />
Save: <input type="submit" id="save-image" value="Upload">
</form>

Inserting custom tag in JSF Ajax response XML

Consoder the following code:
<h:commandButton value="do" action="#{testBacking.do}">
<f:ajax execute="#all" render="#all" listener="#{testBacking.listener}"/>
</h:commandButton>
I want to have a custom tag (with value based on server logic), in the Ajax response XML, something like the following:
<isValidationFailed> true </isValidationFailed>
I can use this data to re-enable the button (which was disabled when Ajax begin, to avoid double clicks) if validation is failed.
How can I achieve this (preferably without using any JSF 3rd party libraries)?
EDIT:
The example code, more precisely, should be like this:
<h:commandButton id="myButton" value="do" action="#{testBacking.do}">
<f:ajax execute="id1" render="id2 myButton" listener="#{testBacking.listener}"/>
</h:commandButton>
This is only possible with a custom PartialViewContext which you load into your JSF application using a PartialViewContextFactory. The custom PartialViewContext should in turn return a custom PartialResponseWriter on PartialViewContext#getResponseWriter(). In this custom PartialResponseWriter, you should be able to add extensions to the XML response by calling startExtension() and endExtension() in endDocument(). Something like:
#Override
public void endDocument() throws IOException {
Map<String, String> attributes = new HashMap<String, String>();
attributes.put("name1", "value1");
attributes.put("name2", "value2");
startExtension(attributes);
write("lorem ipsum");
endExtension();
super.endDocument();
}
This will then end up in the XML response as
<extension name1="value1" name2="value2">lorem ipsum</extension>
This is available and traversable by data.responseXML in jsf.ajax.addOnEvent() function.
Here's a full kickoff example how you could utilize it in your particular case:
MyPartialViewContextFactory which provides the custom partial view context:
public class MyPartialViewContextFactory extends PartialViewContextFactory {
private PartialViewContextFactory wrapped;
public MyPartialViewContextFactory(PartialViewContextFactory wrapped) {
this.wrapped = wrapped;
}
#Override
public PartialViewContext getPartialViewContext(FacesContext context) {
return new MyPartialViewContext(wrapped.getPartialViewContext(context));
}
}
MyPartialViewContext which provides the custom partial response writer:
public class MyPartialViewContext extends PartialViewContextWrapper {
private PartialViewContext wrapped;
private PartialResponseWriter writer;
public MyPartialViewContext(PartialViewContext wrapped) {
this.wrapped = wrapped;
this.writer = new MyPartialResponseWriter(wrapped.getPartialResponseWriter());
}
#Override
public PartialResponseWriter getPartialResponseWriter() {
return writer;
}
#Override
public void setPartialRequest(boolean isPartialRequest) {
wrapped.setPartialRequest(isPartialRequest);
}
#Override
public PartialViewContext getWrapped() {
return wrapped;
}
}
MyPartialResponseWriter which writes <extension id="myextension"> with the body as JSON):
public class MyPartialResponseWriter extends PartialResponseWriter {
public MyPartialResponseWriter(ResponseWriter wrapped) {
super(wrapped);
}
#Override
public void endDocument() throws IOException {
startExtension(Collections.singletonMap("id", "myextension"));
write("{\"validationFailed\": " + FacesContext.getCurrentInstance().isValidationFailed() + "}"); // Consider a JSON serializer, like Google Gson.
endExtension();
super.endDocument();
}
}
To get it to run, register the factory as follows in faces-config.xml:
<factory>
<partial-view-context-factory>com.example.MyPartialViewContextFactory</partial-view-context-factory>
</factory>
Here's how you can access, parse and use the <extension id="myextension"> in your jsf.ajax.addOnEvent():
jsf.ajax.addOnEvent(function(data) {
if (data.status == "success") {
var args = JSON.parse(data.responseXML.getElementById("myextension").firstChild.nodeValue);
if (args.validationFailed) {
// ...
}
else {
// ...
}
}
});
However, your particular functional requirement can be achieved in a different, likely simpler, manner. Just let the ajax request update the button itself and let the button's disabled attribute evaluate true when there's means of a successful postback.
<h:commandButton id="myButton" value="do" action="#{testBacking.do}"
disabled="#{facesContext.postback and not facesContext.validationFailed}">
<f:ajax execute="id1" render="#this id2" listener="#{testBacking.listener}"/>
</h:commandButton>

Error in onSubmit, feedback not rendering

I have some logic in onSubmit of the button (which is in Form), which may fail, so I would like to show a message using error(myMessage). But it doesn't work, and it seems that it is normal:
Feedback panel added but does not show all messages
Is there any possibility to render feedback panels with errors reported in onSubmit?
There is no ajax used on the page. I am using wicket 1.5.6
EDIT:
MyPage.java
public class MyPage extends WebPage {
private static final Logger logger = Logger.getLogger(MyPage.class);
private static final long serialVersionUID = -8874964120018036584L;
public MyPage(PageParameters parameters) {
super(parameters);
logger.debug("Creating new login page");
add(new MyLoginForm("loginForm"));
}
}
MyLoginForm.java
public class MyLoginForm extends StatelessForm<Void> {
private static final Logger logger = Logger.getLogger(MyLoginForm.class);
private static final long serialVersionUID = -8694389090804630170L;
private MyUser user = new MyUser();
public MyLoginForm(String id) {
super(id);
setOutputMarkupId(true);
logger.debug("Creating new stateless login form");
add(new RequiredTextField<String>("login", new PropertyModel<String>(user, "login")));
add(new PasswordTextField("password", new PropertyModel<String>(user, "password")));
add(new Button("submit"));
add(new FeedbackPanel("feedback"));
}
#Override
public void onSubmit() {
info("test info");
}
}
MyPage.html
<body>
<form wicket:id="loginForm">
<fieldset>
<legend><wicket:message key="form.login.legend"/></legend>
<input type="text" wicket:id="login" />
<input type="password" wicket:id="password" />
<input type="submit" wicket:id="submit" />
<span wicket:id="feedback"></span>
</fieldset>
</form>
</body>
catalina.out
16 May 2012 15:24:20:860 WARN [http-8080-2] [WebSession:135] Component-targetted feedback message was left unrendered. This could be because you are missing a FeedbackPanel on the page. Message: [FeedbackMessage message = "test info", reporter = loginForm, level = INFO]
The same happens when I try to overwrite the onSubmit method in Button instead of the one in MyLoginForm...
You need to add a FeedbackPanel to your Page. Feedback messages 'bubble' up in the component hierarchie. The easiest way is to have one feedbackpanel on your page.
But, you can also display errors close to the FormComponent that reports the error. See this pdf for inspiration or for a possible implementation.
Edit: I just build a very simple test, using the wicket quickstart. Changed the HomePage as below and it worked (I saw all error / info messages)
html:
<form wicket:id="form">
<div wicket:id="feedback"></div>
<input wicket:id="submit" type="button" value="submit">
</form>
Java:
Form<Void> form = new Form<Void>("form") {
#Override
protected void onSubmit() {
super.onSubmit();
error("test error from form");
error("test info from form");
}
};
add(form);
form.add(new FeedbackPanel("feedback"));
form.add(new SubmitLink("submit") {
#Override
public void onSubmit() {
super.onSubmit();
error("an error occurred in button submit");
info("test info from the button");
}
});
Edit 2: It turns out, that a StatelessForm is used (I overlooked that detail). Switching back to (normal) Form, the messages should be displayed correctly.
I have checked twice, Wicket 1.5.6 FeedbackPanel (and SignInForm where I have problem) works worse than 1.5.4
I have no idea, what is backgroud of this behaviour.
EDIT: version 1.5.5 work good.
EDIT2: https://issues.apache.org/jira/browse/WICKET-4536
I found another way for anyone else who stumbles into this issue..
I have a base class that I inherit from and then I have the feedback messages print out as part of the base class.. I too ran into this issue, and just had a method inside my base class return an instance of itself (return this), and then I just access the info method through this method... so, getMethod().info("some message").. and it worked for me. My feedbackPanel is also set in the base class..
So I'd imagine you can do the same thing.. Just get access to an instance of the page you want to stamp the feedback message to.

Resources