Set focus on a component with Apache Wicket? - java

How do you set focus on a component with Apache Wicket? Searching leads to very little information, mostly on setting the default field. I do not want to set a default field, rather I am looking to set focus when, for example, a specific radio button is selected.

I suggest using the native org.apache.wicket.ajax.AjaxRequestTarget#focusComponent(). For example:
/**
* Sets the focus in the browser to the given component. The markup id must be set. If
* the component is null the focus will not be set to any component.
*
* #param component
* The component to get the focus or null.
*/
org.apache.wicket.ajax.AjaxRequestTarget#focusComponent(Component component)

Once you create your behavior to set the focus, you should be able to add it to the component on any event, just make sure that component is part of the AjaxRequestTarget. I don't see why this wouldn't work...
myRadioButton.add(new AjaxEventBehavior("onchange") {
#Override
protected void onEvent(AjaxRequestTarget target) {
myOtherComponent.add(new DefaultFocusBehavior());
target.addComponent(myForm);
}
});
Here's a link that shows how to create the default focus behavior if you do not have one already:
http://javathoughts.capesugarbird.com/2009/01/wicket-and-default-focus-behavior.html

If you only want to setFocus through javascript and don't want to reload a form or a component, you can use the following code:
import org.apache.wicket.Component;
public class JavascriptUtils {
private JavascriptUtils() {
}
public static String getFocusScript(Component component) {
return "document.getElementById('" + component.getMarkupId() + "').focus();";
}
}
And then in any Ajax Method you can use:
target.appendJavascript(JavascriptUtils.getFocusScript(componentToFocus));

For a pop-up like modalWindow my workaround solution was to use the attribute "autofocus" on the first input tag.
An easy solution is to add it to the html directly.
<input ..... autofocus>
Another solution is to add it to the modalWindow itself:
#Override
public void show(AjaxRequestTarget target) {
super.show(target);
setUpFocus();
}
protected void setUpFocus() {
DeepChildFirstVisitor visitor = new DeepChildFirstVisitor() {
#Override
public void component(Component component, IVisit<Void> iVisit) {
if (isAutofocusable(component)) {
component.add(new AttributeAppender("autofocus", ""));
iVisit.stop();
}
}
#Override
public boolean preCheck(Component component) {
return false;
}
};
this.visitChildren(FormComponent.class, visitor);
}
protected boolean isAutofocusable(Component component) {
if (component instanceof TextArea ||
component instanceof DropDownChoice ||
// component instanceof RadioChoice ||
component instanceof AjaxCheckBox ||
component instanceof AjaxButton ||
component instanceof TextField) {
return true;
}
return false;
}
RadioChoice is commented out because this solution is not working on that. For RadioChoice i would recommend to implement a FocusedRadioChoice:
public class FocusedRadioChoice<T> extends RadioChoice<T> {
//constructors...
#Override
protected IValueMap getAdditionalAttributes(int index, T choice) {
super.getAdditionalAttributes(0, choice);
AttributeMap am = new AttributeMap();
am.put("autofocus", "");
return am;
}
}

Is there a way to achieve the same without JavaScript?
(I am implementing a form with a feedback-Panel that only comes up when Javascript is turned off, so it would not make sense to depend on JavaScript there...,-)
I could only find answers which use JS .focs()... maybe Wicket 1.5 will provide a method Component.setFocus()...

If you happen to be using an Ajax button, you can simply call target.focusComponent(myComponent); in the button's onSubmit method.

#martin-g 's solution was the only solution that got it working for my scenario - a modal/pop up.
Note:
I think autofocus embedded explicitly in HTML only works on page load, not modal load so any efforts to skillfully set the autofocus attribute in the HTML of a modal just fail miserably - always.
Here I lay out the steps for setting the focus on an input field called 'myInput' using the full power of Wicket (no JS!):
In onInitialize:
// Make sure the field has an ID in markup
myInput.setOutoutMarkupId(true);
Provide an overridden show method where you call the focusComponent method:
public void show(AjaxRequestTarget target)
{
// Make sure you call the super method first!
super.show(target);
target.focusComponent(myInput);
}
This does require that your component is an attribute of your modal content class so that you can access it in the show method. To avoid creating a class attribute for your input component you could blend this solution with the solution from BlondCode by replacing that solution's
component.add(new AttributeAppender("autofocus", ""));
with
target.focusComponent(component);
This also works!

Related

Set a Page for Component Testing with Apache Wicket

I am testing some components in my wicket 7 application.
My Component is nothing special but it inherits from
public PageAwarePanel extends Panel {
#Override
protected void onInitialize() {
super.onInitialize();
//refuse if used on page without PageConfig
if (getPageConfigurationModel() == null){
throw new RuntimeException("this component is only allowed inside pages having PageConfigurationModel");
}
}
protected IModel<PageConfiguration> getPageConfigurationModel(){
if (getPage() instanceof TemplatePage){
return ((TemplatePage)getPage()).getPageConfigurationModel();
}
return null;
}
}
With this I can access some configurations from a certain panel.
Now when I try in a test:
PositionsPanel p = new PositionsPanel("123", asmNumber, Model.of());
tester.startPage(MyPage.class);
tester.startComponentInPage(p);
where MyPage is a TemplatePage.
I get the defined RuntimeException. My Question is:
How can I test this component with defining on which page it should be rendered?
Thanks for all the help in advanced.
tester.startPage(MyPage.class);
tester.startComponentInPage(p);
those two are not related. It is like navigating two different pages in the browser.
The best way is to create a page for the tests that fulfills the requirements, add the panel to this page and do tester.startPage(TestPage.class).
Another way is to override org.apache.wicket.util.tester.BaseWicketTester#createPage() and return an instance of MyPage. This way you can still use startComponentInPage() but otherwise it is basically the same as the first approach.

GXT - send forms on enter, project-wide

I have an application which uses GXT and contains ±30 forms. I would like to make these forms so that when the user hits enter in a text field, the form gets submitted, like a regular browser form would.
I know I can add a key press listener to every text field, which would invoke the submit after enter is pressed, but since I want to apply this to every field in every form I am not sure if this is ideal.
Is there a simpler way to implement this in the entire application?
If not, which pattern should I use to add this functionality to every field? I can extend the TextField class, add the functionality in the child class and use the child class in the application. Or I can create a factory for the text field class which would also add the listener to the field. Or is there some other way, Decorator perhaps? I was wondering which of these approaches, if any, is generally preferred.
I would try something like this:
Event.addNativePreviewHandler(new NativePreviewHandler() {
#Override
public void onPreviewNativeEvent(NativePreviewEvent event) {
if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) {
if (event.getNativeEvent().getEventTarget() != null) {
Element as = Element.as(event.getNativeEvent().getEventTarget());
if (as.getTagName().toLowerCase().equals("input") ||
as.getTagName().toLowerCase().equals("textarea")) {
// TODO submit data;
}
}
}
}
});
Every time someone hits the Enter Key and the cursor is placed on a input- or textarea-tag, you will get the control and can submit your data.
I don't think there is a way to do what you're asking directly in the GXT library. I do want to stress that extending the TextField class just to add an event handler to it is not the correct way to handle this. Event handlers are based on the composition of a class. It would be like extending a class with a List field just to add another element into the list.
A singleton factory class that created and initialises the Textfield for you would be the cleanest solution, in my opinion. It would allow you to effectively change defaults and add other handlers as required at a later time in a single place if requirements change.
You can try it with GWT JSNI also.
Steps to follow:
define a function in JavaScript that is called on Enter key press
call GWT JSNI from above JavaScript function that is exported at the time of onModuleLoad using GWT JSNI
get the Element from where this event is triggered and finally submit the form based on its tag name or Id
Sample code:
HTML/JSP:
<script>
window.onkeydown = keydown;
function keydown(event) {
if (event.which == 13) {
formSubmit(event.target);
}
}
</script>
JAVA(Entry Point):
import com.google.gwt.dom.client.Element;
public void onModuleLoad() {
exportFormSubmit();
...
}
public static void formSubmit(Element element) {
Window.alert("element tag name:" + element.getTagName() + "form ID:"
+ element.getParentElement().getId());
}
public static native void exportFormSubmit() /*-{
$wnd.formSubmit = $entry(#com.x.y.z.client.GWTTestProject::formSubmit(Lcom/google/gwt/dom/client/Element;));
}-*/;

Showing disabled SectionItems expanded in SmartGWT

When you disable a DynamicForm in SmartGWT, all the items in the form become disabled and thus unresponsive. This is expected and correct behaviour.
The problem is that if there are collapsed SectionItems in the form, they cannot be expanded before the form is re-enabled. Is there an easy way to make the sections interactive when a form is disabled?
SectionItem are FormItem whose drawing is handled by the containig DynamicForm.
Check http://forums.smartclient.com/showthread.php?t=15008.
Following options exist in achieving the required behavior.
These options do not disable the entire canvas of the form.
Option 1:
Use following method in place of form.setDisabled(true|false) as setFormDisabled(form, true|false);.
private void setFormDisabled(DynamicForm form, boolean isDisabled) {
FormItem[] fields = form.getFields();
for (FormItem field : fields) {
if (!(field instanceof SectionItem)) {
field.setDisabled(isDisabled);
}
}
}
Option 2:
Override form.*Disabled() methods and use as form.setDisabled(true|false).
DynamicForm form = new DynamicForm() {
private boolean isDisabled;
#Override
public boolean getDisabled() {
return isDisabled;
}
#Override
public void setDisabled(boolean disabled) {
this.isDisabled = disabled;
setFormDisabled(this, disabled);
}
};
This can be enhanced with Generics if multiple types of items need to be kept enabled in different forms.

Apache wicket Button setMarkupId not working

I have:
ListView
Button
WebMarkupContainer (Popup content container)
So, when I populate the ListView, I add an AjaxEventBehavior to the buttons. I also override getAjaxCallDecorator(), as I need to call a javascript function from each button. So, in the decorateScript function, I override the WebMarkupContainer markup id by using setMarkupId(), it works. I do the same for the Button, and it works, at least when I call getMarkupId(). But when I go to the generated HTML, it's not there! Why is this happening?
The code is the following (it is inside of the populateItem from ListView):
infoBtn.add(new AjaxEventBehavior("onclick") {
#Override
protected void onEvent(AjaxRequestTarget target) {
}
#Override
protected IAjaxCallDecorator getAjaxCallDecorator() {
return new IAjaxCallDecorator() {
#Override
public CharSequence decorateScript(Component component, CharSequence script) {
StringBuilder jsScript = new StringBuilder();
infoPopoverContent.setMarkupId(infoPopoverContent.getMarkupId(true) + String.valueOf(pos));
infoBtn.setOutputMarkupPlaceholderTag(true);
infoBtn.setMarkupId(infoBtn.getMarkupId() + String.valueOf(pos));
jsScript.append("$('#" + infoBtn.getMarkupId() + "').popover({");
jsScript.append("html:true,");
jsScript.append("placement:'bottom',");
jsScript.append("content:function() {");
jsScript.append("return $('#");
jsScript.append(infoPopoverContent.getMarkupId());
jsScript.append("').html();");
jsScript.append("}");
jsScript.append("});");
logger.debug(jsScript.toString());
pos++;
return jsScript;
}
#Override
public CharSequence decorateOnSuccessScript(Component component,
CharSequence script) {
// TODO Auto-generated method stub
return null;
}
#Override
public CharSequence decorateOnFailureScript(Component component,
CharSequence script) {
// TODO Auto-generated method stub
return null;
}
};
}
});
Make sure you're calling infoBtn.setOutputMarkupId(true), so that Wicket knows it should be outputting the id attribute.
Notice setOutputMarkupPlaceholderTag(true) is also calling setOutputMarkupId(true) behind the scenes. Without knowing much about your code, it looks like you don't really need it. setOutputMarkupPlaceholderTag() will output an emtpy container (<span id="xxx"> in case the component is not visible, just to have a reference to the place the component belgons to, and allowing Wicket for manipulation via DOM in the AJAX response (for example, to make the component visible again).
As a side note, if you don't really need the id attributes to have a specific value, you can simplify your code by not using setMarkupId() and letting Wicket generate ids for you.
Also, it might be simpler to drop the IAjaxCallDecorator and just append the script in onEvent by means of AjaxRequestTarget#appendJavascript() or AjaxRequestTarget#prependJavascript(), depending on your needs.

GWT SuggestBox: How do I force the SuggestBox to select the first item in the suggestion list?

I have a textbox and one suggestbox. I attach a value change and key up handler to the text box such that whatever the user types (or pastes) into the text box is echo-ed inside the suggestbox. I can get the suggestbox to display the suggestion list by calling showSuggestionList on each value change and key up event.
Now, how do I get the suggestbox to automatically choose the first item in the suggestion list?
One of the methods I tried is to programatically simulate key presses, i.e
suggestBox.setFocus(true);
NativeEvent enterEvent = Document.get().createKeyPressEvent(false, false, false, false, KeyCodes.KEY_ENTER);
DomEvent.fireNativeEvent(enterEvent, suggestBox);
textBox.setFocus(true);
This doesn't work at all. The enter key isn't simulated. Another possible solution is to extend SuggestionBox.SuggestionDisplay, but I'm not too sure how to that. Any pointers appreciated.
Update: I'm still working on this and trying various methods.
Here, I tried to implement my own SuggestionDisplay by subclassing DefaultSuggestionDisplay and overriding getCurrentSelection() to make accessible from my class. This doesn't work either. Null is returned.
private class CustomSuggestionDisplay extends DefaultSuggestionDisplay {
#Override
protected Suggestion getCurrentSelection() {
return super.getCurrentSelection();
}
}
suggestBox.setAutoSelectEnabled(true);
textBox.addKeyUpHandler(new KeyUpHandler() {
public void onKeyUp(KeyUpEvent event) {
suggestBox.setValue(textBox.getText(), true);
suggestBox.showSuggestionList();
if (suggestBox.isSuggestionListShowing()) {
String s = ((CustomSuggestionDisplay) suggestBox.getSuggestionDisplay()).getCurrentSelection().getDisplayString();
Window.alert(s);
}
}
});
Here, I tried to attach a value change handler to the SuggestBox, and casting the event type to SuggestOracle.Suggestion. Again, null is returned.
suggestBox.addValueChangeHandler(new ValueChangeHandler<String>() {
public void onValueChange(ValueChangeEvent<String> event) {
String s = ((SuggestOracle.Suggestion) event).getDisplayString();
Window.alert(s);
}
});
Use suggesBox.setAutoSelectEnabled(true)
Here more info about the SuggestBox of GWT:
You could try using addSelectionHandler in conjunction with setAutoSelectEnabled to receive an event whenever a suggestion is selected. You could also have your Oracle send a message when it suggests something, or your Display send a message when it displays a list:
public class AutomaticallySelectingSuggestionDisplay extends SuggestBox.DefaultSuggestionDisplay {
#Override
protected void showSuggestions(SuggestBox box, Collection<? extends SuggestOracle.Suggestion> suggestions, boolean isDisplayHtml, boolean isAutoSelectEnabled, SuggestBox.SuggestionCallback callback) {
super.showSuggestions(box, suggestions, isDisplayHtml, isAutoSelectEnabled, callback);
fireValueChangeEventWithFirstSuggestion(suggestions);
}
}
This idea feels a little muddled to me, so I hope you can find a solution just using event handlers.

Categories