My form has 1 dropdown use AjaxLazyLoadPanel and 1 ajax button submit.
I click button submit that works only when the dropdown is finished loading.
Index.java
form.add(new AjaxLazyLoadPanel("lazy") {
private static final long serialVersionUID = 1L;
#Override
public Component getLazyLoadComponent(String markupId) {
Fragment fr = new Fragment(markupId, "lazyContentFragment", Index.this);
fr.add(dropdown());
return fr;
}
});
form.setOutputMarkupId(true);
form.add(new AjaxButton("search") {
#Override
protected void onError(AjaxRequestTarget target) {
target.add(feedback);
}
#Override
protected void onSubmit(AjaxRequestTarget target) {
//do something...
}
});
Index.html
<form wicket:id="form">
<input wicket:id="textbox"/>
<div wicket:id="lazy"></div>
<button wicket:id="search"></button>
</form>
<wicket:fragment wicket:id="lazyContentFragment">
<select wicket:id="dropdown"></select>
</wicket:fragment>
Is there any way to submit the form without waiting for the dropdown finish loading.
By default wicket-ajax.js serializes the Ajax calls to the server. This is being done so that the second Ajax call is not called if its HTML element is being removed/painted by the first Ajax call.
In addition only one thread can manipulate a Page instance at a time at the server side!
To be able to make two Ajax calls at the same time you need to use different AjaxChannels for them. For example:
... = new AjaxButton("someId") {
#Override protected void updateAjaxAttributes(AjaxRequestAttributes attributes)
{
attributes.setChannel(new AjaxChannel("customName"));
}
}
This way both Ajax requests will be fired to the server, but the AjaxButton's one will be processed only after the AjaxLazyLoadingPanel's one releases the lock on the page instance at the server side. There is no way to execute two threads on the same page instance (i.e. having the same pageId).
If you're dropdown's choices are taking so long to load, you might want to make your AjaxLazyLoadPanel asynchronous by overriding #isContentReady():
private String token;
private List<T> choices;
protected boolean isContentReady()
{
if (token == null) {
token = startLoadingChoices();
} else {
choices = checkChoicesLoaded(token);
}
return choices != null;
}
Since Wicket manages and serializes all pages, your page must not be held as a reference in a thread or a listener in central registry. Keep a token instead and ask for the loading result; #isContentReady() will be polled repeatedly until it returns true.
Related
I have a gwt app with a login page and a main page. After login app goes to main page. What i want is if i refresh the page to stay in main page and not going to login page. I have read many things and i tried History Mechanish but no result. Here is my code:
#Override
public void onSuccess(Login result) {
if (result.getLoginCount() == 1) {
final VerticalPanel userPanel = new VerticalPanel();
Anchor logout = new Anchor("logout");
logout.addStyleName("user");
logout.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
loginPanel.setVisible(true);
tablePanel.setVisible(false);
addPanel.setVisible(false);
userPanel.setVisible(false);
}
});
Label user = new Label("Hi " + usernameBox.getText());
userPanel.add(user);
user.addStyleName("user");
userPanel.add(logout);
userPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_RIGHT);
userPanel.setVisible(true);
usernameBox.setText("");
passwordBox.setText("");
RootPanel.get("user").add(userPanel);
loginPanel.setVisible(false);
tablePanel.setVisible(true);
addPanel.setVisible(true);
History.newItem("main");
History.addValueChangeHandler(new ValueChangeHandler<String>() {
#Override
public void onValueChange(ValueChangeEvent<String> event) {
if(History.getToken().equals("main")){
loginPanel.setVisible(false);
tablePanel.setVisible(true);
addPanel.setVisible(true);
}
}
});
}
i also tried:
String historyToken = event.getValue();
if(historyToken.substring(0 , 4).equals("main")){
loginPanel.setVisible(false);
tablePanel.setVisible(true);
addPanel.setVisible(true);
} else {
loginPanel.setVisible(true);
tablePanel.setVisible(false);
addPanel.setVisible(false);
}
Is this the right way to handle page refresh with History.addValueChangeHandler? I would appreciate any help.
GWT application is a single page application. It means that if your reload page, the state of your application will be lost. What you can do, is to use local storage to store same state data, but that is not a good idea for an authentication data.
I recommend you to refactor your code in a way that the authentication is done against the back end and your GWT client will recover it's state from back end data when user refreshes the page.
I'm trying to start an interval after button click.
button = form.add(new AjaxButton("button") {
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
AjaxSelfUpdatingTimerBehavior ajaxSelfUpdatingTimerBehavior = new AjaxSelfUpdatingTimerBehavior(Duration.seconds(5)) {
private static final long serialVersionUID = 1L;
#Override
protected void onPostProcessTarget(AjaxRequestTarget target) {
logger.debug("onPostProcessTarget");
}
};
this.add(ajaxSelfUpdatingTimerBehavior);
}
});
});
The previous AjaxSelfUpdatingTimerBehavior does not start after button click. How can I make it start?
The wierd thing is if I add a new one like this after the previous block
button.add(new AjaxSelfUpdatingTimerBehavior(Duration.seconds(5)) {
private static final long serialVersionUID = 1L;
#Override
protected void onPostProcessTarget(AjaxRequestTarget target) {
logger.debug("onPostProcessTarget2");
}
});
both of them start. But the last one also updates the dom which I don't want.
So all in all how can I start an interval after button click? (similarly to first code snippet)
Unlike regular form submission, ajax form submission (via an ajax button like you do here) is not going to re-render anything that you don't explicitly state you want re-rendered by performing target.add(...) on it.
What this means is that any changes you make to the component hierarchy or components themselves (like adding a new behavior) are going to stay in the back-end until you either update the individual components, or re-render the whole page.
In order to propogate any state changes in an ajax request call target.add(this) in your form submission. This will make wicket update the component and hence add the new behaviors of it to the front-end.
So given your example the code should look like the following:
button = form.add(new AjaxButton("button") {
#Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
AjaxSelfUpdatingTimerBehavior ajaxSelfUpdatingTimerBehavior = new AjaxSelfUpdatingTimerBehavior(Duration.seconds(5)) {
private static final long serialVersionUID = 1L;
#Override
protected void onPostProcessTarget(AjaxRequestTarget target) {
logger.debug("onPostProcessTarget");
}
};
this.add(ajaxSelfUpdatingTimerBehavior);
target.add(this);
}
});
});
We are using Wicket and our generated pages are quiet long (a lot of vertical scrolling). Some links or form's onSubmit methods invoke just perform some actions on the database and show the same page again:
public class MyPage extends WebPage {
public MyPage(PageParameters parameters) {
....
final Form<Void> form = new StatelessForm<Void>("formId") {
protected void onSubmit() {
// some database stuff
...
setResponsePage(getClass(), getPageParameters());
}
};
...
}
}
How can I make the setResponsePage invocation cause the browser scroll to the form, so the page is not just showing the top? Maybe some JavaScript-injection?
I think a nice Wicket-y solution combines stuff that is already in Michael's answer, with a Behavior, so you can just add this to your form.
form.add( new ScrollToTopBehavior());
The behaviour itself would like something like this:
public class ScrollToTopBehavior extends Behavior
{
#Override
public void renderHead( Component component, IHeaderResponse response )
{
super.renderHead( component, response );
response.render( JavaScriptHeaderItem.forReference( Application.get().getJavaScriptLibrarySettings().getJQueryReference() ) );
component.setOutputMarkupId( true );
String script = String.format("doSomeJavaScriptStuff('%s')", component.getMarkupId());
response.render( OnDomReadyHeaderItem.forScript( script ) );
}
}
UPDATE:
For scrolling to a specific ID / ANCHOR only once, you can follow this answer:
https://stackoverflow.com/a/3163635/461499
JS of course.
This would be something like (with JQuery usage):
var scrollPosition = $('#scrollToMarkupId').offset().top;
$('html, body').animate({ scrollTop: " + scrollPosition + " }, 'slow');
where scrollToMarkupId is wicket component's markup id, which could be obtained by calling component.getMarkupId() method.
I'm not pro in JS, so you can try to google better impl may be.
Now, about wicket:
1) As for me, I prefer AJAX invocations for such behavior ( note that if you use such approach your page won't be stateless ):
// do not override your form's `onSubmit()` method
final Form<Void> form = new Form<Void>("formId");
// adding ajax behavior with `onSubmit()` method overriding.
form.add ( new AjaxFormSubmitBehavior ("submit")
{
protected void onSubmit ( AjaxRequestTarget target )
{
// your submit logic
// then insert js, descriped above:
target.appendJavaScript ("..." + componentToScroll.getMarkupId() + "..");
}
});
This approach won't reload your page at all but also post your data.
/----------------------------------------------------------------------------------------------------------------------------------/
2) You also could execute JS after page loading, by overriding renderHead method:
public class YourPage extends WebPage
{
...
#Override
public void renderHead ( final IHeaderResponse response )
{
//replace `...` by your script.
response.render ( OnDomReadyHeaderItem.forScript ( "..." );
}
...
}
Such script will be invoked after page is renedered (and setResponsePage method will render your page). You can use this approach for any components and panels too.
I've now use following JavaScript injecting code:
add(new Behavior() {
#Override
public void renderHead(Component component, IHeaderResponse response) {
super.renderHead(component, response);
response.render(new HeaderItem() {
#Override
public Iterable<?> getRenderTokens() {
return Collections.singletonList("javascript-anchor");
}
#Override
public void render(Response response) {
response.write("<script type=\"text/javascript\">\n");
response.write("window.location.href='#rules';\n");
response.write("</script>\n");
}
});
}
});
Feel free to comment (I'm a complete JS-noob with only very limited experience in Wicket).
I'm developing a wizard by using GWT. In the Wizard first page i have a form component to upload the file. In the wizard panel i have the next button when i press the next button the validation method will be triggered if the validation is passed then i'm calling the form.submit(); but before form.submit() handler starts the functionality the validation methods completes it. After it completes the validation method only the form submit it's really taking part. How can i controll this event behavior, when i submit the form using form.submit() the remaining actions has to wait till this form returns to it's handler.
Advance Thanks.
Assuming you have a FormPanel:
FormPanel form = new FormPanel();
You can add a handler:
form.addSubmitCompleteHandler(new FormPanel.SubmitCompleteHandler() {
public void onSubmitComplete(SubmitCompleteEvent event) {
// TODO: Do the next step in the wizard
// Use event.getResults() to get the text of the response
}
});
Write a boolean which you'll set to true once the validation is complete. Then start a timer that waits on that boolean. But consider using events and callbacks for repeated use. Using timer will make your code messy if you overuse them.
#UiHandler("submit")
protected void onSubmit() {
validated=false;
validate();
Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
public boolean execute() {
if (validated) {
if (validationSucceeded()) {
submit();
}
return false;
}
return true;
}
}, 250);
}
private boolean validated = false;
private void validate() {
// do validation
validated=true;
}
I am trying to sign into my application. First I throw the RestartResponseAtInterceptPageException (this is in a WicketPanel on my BasePage):
add(new Link<String>("signin") {
#Override
public void onClick() {
throw new RestartResponseAtInterceptPageException(SignIn.class);
}
});
The SignIn Page class contains a form (an inner private class) for the sign in, with the following submit button:
add(new Button("signinButton") {
#Override
public void onSubmit() {
final User user = model.getObject();
final boolean result = MySession.get().authenticate(user);
if (result) {
if (!continueToOriginalDestination()) {
setResponsePage(MySession.get().getApplication().getHomePage());
}
} else {
error("Authentication failed");
}
}
});
When this button is clicked and the user is successfully authenticated, I am not redirected to the page where I clicked on the signIn link but instead I stay on the SignIn page? I've tried debugging this, but haven't been able to find out where things go wrong.
I am glad for any hints that lead to my finding the error of my ways.
This is wicket 1.5.1 by the way.
Small Update because I got the hint I needed from the answer, there is still a bit of explaining to do. The solution looks like this:
add(new Link<String>("signin") {
#Override
public void onClick() {
setResponsePage(new SignIn(getPage()));
}
});
The SignIn class gets a constructor that takes a page obviously and I simply set that page as with setResponsePage to return to where I started without all the continueToOriginalDestination and exception throwing.
RestartResponseAtInterceptPageException is meant to be used to redirect to an interception page while rendering a page. For example, in the constructor of a Page class ProtectedPage, if there is no user signed in, you throw new RestartResponseAtInterceptPageException(SignIn.class). When the SignIn page calls continueToOriginalDestination(), the user is taken back to the original ProtectedPage destination.
Your use is not a typical use of RestartResponseAtInterceptPageException since you throw it in a link handler. Why don't you do a setResponsePage(SignIn.class) directly instead? If you really want to return to the exact page you were on when the "signin" link is clicked, you could also try changing it to:
add(new Link<String>("signin") {
#Override
public void onClick() {
setResponsePage(getPage());
throw new RestartResponseAtInterceptPageException(SignIn.class);
}
});