The following code attaches a new element with every subsequent click on the 2nd div. With each adding, the focus is set to the added element, so that it is ready for blur methods (I am setting the tabIndex attribute just for this purpose).
However, on clicking outside the new element, nothing happens, even though there should be a pop-up. Can anyone tell me what's wrong with this code?
public void onModuleLoad() {
VerticalPanel vert = new VerticalPanel();
String foo = "<div id ='foo'>Foo</div>";
$("#bodywrapper").append(foo);
$("#bodywrapper").append("<div id ='boo'>Boo</div>");
$("#boo").click(new Function() {
public boolean f(Event e) {
// Window.alert("foo");
$("<div id ='goo' tabIndex = '1'>Boo</div>").appendTo("#bodywrapper").focus();
return true;
}
});
$("#goo").live("blur", new Function() {
public boolean f(Event e) {
Window.alert("Foo");
return true;
}
});
RootPanel.get().add(vert);
}
}
The blur and focus events don't work with event delegation (live or delegate methods) because they're not bubbling events. JQuery introduce the special events focusout and focusin for this purpose. But GwtQuery doesn't support them yet. Please open an issue there and they will be implemented
Related
I'm using Wicket 8.10.
I have a Wicket Component that I want to dynamically hide or show depending on some external condition. For that I have the following code:
var mccc = new MyCoolCustomComponent("component"); // Custom component I wrote
mccc.setOutputMarkupId(true);
mccc.setOutputMarkupPlaceholderTag(true);
mccc.setVisible(false); //Should be hidden initially
var container = new WebMarkupContainer("container");
container.setOutputMarkupId(true);
container.setOutputMarkupPlaceholderTag(true);
container.add(mccc);
add(container);
var updateTimer = new AbstractAjaxTimerBehavior(Duration.seconds(1)) {
#Override
protected void onTimer(AjaxRequestTarget target) {
if(FooSingleton.instance().isBar()) {
mccc.setVisible(true);
} else {
mccc.setVisible(false);
}
target.add(mccc);
}
};
container.add(updateTimer);
The corresponding HTML looks like this:
<div wicket:id="container" >
<div wicket:id="component"/>
</div>
What I would expect to happen: The component is hidden initially. When isBar() returns true the component is shown and once it returns false again it is hidden.
What is actually happening: The component is hidden initially. It is shown once isBar() becomes true but does not become invisible once isBar() returns false.
I also thought about using an AttributeModifier to use the CSS display property, but I can't find how to change the value of the modifier.
I solved it with CSS:
mccc.add(new AttributeModifier("style",
() -> {
if (FooSingleton.instance().isBar()) {
return "";
}
return "display: none;";
}));
I'm sure the solution is not great, but it works for now.
I have to add a class to a component. I can't add the component via ajax because this is a problem with the input. My code is:
private ListView<Opzioni> setListOpzioni(boolean b) {
return new ListView<Opzioni>("list_opzioni", opzioniDao.findAll()) {
#Override
protected void populateItem(ListItem<Opzioni> item) {
erroriAssociatiAlTextField = new HashMap<>();
List<Opzioni> opzioniCron = opzioniDao.getOpzioniFormatore();
final Opzioni o = item.getModelObject();
final WebMarkupContainer errorContainer = new WebMarkupContainer("errorContainer");
errorContainer.setOutputMarkupId(true);
errorContainer.setOutputMarkupPlaceholderTag(true);
Boolean isSelected = false;
Boolean isAzienda = o.getAzienda() != null ? o.getAzienda().equals(getAziendaLogged()) : false;
if (isAdminFormatore(getUserLogged())) {
isSelected = o.getControlFormatore() || isAzienda;
} else {
isSelected = isAzienda;
}
Boolean visibile = isSa || isSelected;
Label name_op = new Label("name_op", o.getName());
item.add(name_op.setVisible(visibile));
TextField val_op = new TextField("val_op", new PropertyModel(o, "val"));
val_op.add(new OnChangeAjaxBehavior() {
#Override
protected void onUpdate(AjaxRequestTarget art) {
if (opzioniCron.contains(o)) {
controllaStringa(o);
}
if (valoriScorretti == true) {
contatore++;
} else {
contatore = 0;
}
if (contatore > 0) {
ciSonoErrori = true;
String error = "Valori inseriti nel box " + o.getName() + " non corretti";
if (!erroriAssociatiAlTextField.containsKey(o)) {
erroriAssociatiAlTextField.put(o, error);
}
for (Map.Entry<Opzioni, String> map : erroriAssociatiAlTextField.entrySet()) {
val_op.error(map.getValue());
}
art.add(errorContainer.setVisible(true));
refreshFp(art);
art.add(save_btn.setVisible(false));
} else {
ciSonoErrori = false;
if (!erroriAssociatiAlTextField.isEmpty()) {
art.add(save_btn.setVisible(false));
if (erroriAssociatiAlTextField.containsKey(o)) {
erroriAssociatiAlTextField.remove(o);
}
for (Map.Entry<Opzioni, String> map : erroriAssociatiAlTextField.entrySet()) {
val_op.error(map.getValue());
}
}
if (erroriAssociatiAlTextField.isEmpty()) {
art.add(save_btn.setVisible(true));
}
art.add(errorContainer.setVisible(false));
refreshFp(art);
}
}
});
item.add(val_op.setEnabled(b).setVisible(visibile));
item.add(errorContainer.setVisible(false));
if (visibile) {
o.setModificato(true);
} else {
o.setModificato(false);
}
}
};
}
With this code every time a user insert a letter inside the field the cursor go to the first position and it's impossible to use it. Is there an alternative mode to add the class dynamically?
With this code every time a user insert a letter inside the field the
cursor go to the first position and it's impossible to use it.
That is because of the OnChangeAjaxBehavior you are using.
This behavior checks after every user input if the FormComponent validates correct and if it does it will call the onUpdate method.
For a TextField without an IValidator added that means onUpdate is called after every input. If you then reprint the TextField via AjaxRequestTarget you get the behaviour of an input field where you type "backwards" as you currently do.
how can i modify attributes without adding the component in Wicket?
If you want you're changes to be visible in the browser then you need to update the component with ajax at some point. There is no way around it.
You probably have to rethink you're aproach because what you are currently doing doesn't make much sense.
At the moment you have a TextField and when the user enters something that is valid you add the css class "field-error" to the html input.
Shouldn't it be the other way around and the " field-error" should get added when the users enters something that is invalid?
Do you really want to validate and do an ajax update while the user enters something? Why not validate the input when the form/textfield actually gets submitted, or when the user is done typing into the field?
Edit
Instead of updating the input with the AjaxRequestTarget you could use the AjaxRequestTarget to send the jQuery command to add the css class to the input:
val_op.setOutputMarkupId(true);
val_op.add(new OnChangeAjaxBehavior() {
#Override
protected void onUpdate(AjaxRequestTarget art) {
art.appendJavaScript("$('#"+val_op.getMarkupId()+"').addClass('field-error');");
}
}
Instead of updating the whole input via ajax, this will just send a jQuery Javascript to be executed in the AjaxResponse. You can then just do the Javascript call in the page you linked and the adding of the css class will be done on client side.
The only thing you need is the id of your input so that jquery can find it. So setOutputMarkupId must be set to true and you can then get the id that wicket created by calling getMarkupId() and insert it into the javascript command.
As I already said it seems strange to me that you add the error-class in the onUpdate method. The correct way would seem to me to add the error class in the onError method (called when input is invalid) and remove it in the onUpdate (when input is valid).
val_op.setOutputMarkupId(true);
val_op.add(new OnChangeAjaxBehavior() {
#Override
protected void onUpdate(AjaxRequestTarget art) {
art.appendJavaScript("$('#"+val_op.getMarkupId()+"').removeClass('field-error');");
}
#Override
protected void onError(AjaxRequestTarget art, RuntimeException e) {
art.appendJavaScript("$('#"+val_op.getMarkupId()+"').addClass('field-error');");
}
}
By default a JFace TableViewer moves the selection up and down when the user presses the up or the down arrow key. This makes sense most of the time, but in my application the business logic will be different.
How can I override this behavior? I tried adding a KeyListener and it gets called, but the default action still happens: selection changes.
I have figured out I have to use a TableViewerFocusCellManager along with a CellNavigationStrategy. I expected the FocusCellManager to be a property of the TableViewer, but it is not. Apparently the FocusCellManager does the wiring behind the scenes.
CellNavigationStrategy navigationStrategy = new CellNavigationStrategy() {
#Override
public boolean shouldCancelEvent(ColumnViewer viewer, Event event) {
return true;
}
#Override
public boolean isNavigationEvent(ColumnViewer viewer, Event event) {
return false;
}
};
TableViewerFocusCellManager focusCellManager = new TableViewerFocusCellManager(tableViewer, new FocusCellOwnerDrawHighlighter(tableViewer), navigationStrategy);
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!
I am attempting to create a Google Web Toolkit (GWT) application that also uses Google Gears, but every time I try to remove the panel, I get an exception and the panel stays there.
Here is an excerpt from the exception I get (I've only included the relevant bits of the call stack, the rest just descends into the included function below):
java.lang.AssertionError: A widget that has an existing parent widget may not be added to the detach list
at com.google.gwt.user.client.ui.RootPanel.detachOnWindowClose(RootPanel.java:122)
at com.google.gwt.user.client.ui.RootPanel.get(RootPanel.java:197)
I'm not sure what the problem is, but I really don't like leaving the button there after they approve the use of Gears.
What am I doing wrong? Or any suggestions on a different way I could do this to make it work?
if(!gearsFactory.hasPermission()) {
HorizontalPanel rightPanel = new HorizontalPanel();
rightPanel.getElement().setId("gearsPrompt");
rightPanel.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
rightPanel.setSpacing(0);
rightPanel.setHeight("28px");
InlineLabel enableGearsText = new InlineLabel("Enable Gears for off-line access");
enableGearsText.getElement().setId("gearsText");
enableGearsText.addStyleName("titleElement");
rightPanel.add(enableGearsText);
final Button gearsButton = new Button("Use Gears");
gearsButton.getElement().setId("gearsButton");
gearsButton.addStyleName("titleElement");
gearsButton.setHeight("24px");
gearsButton.addClickHandler( new ClickHandler() {
public void onClick(ClickEvent event) {
Factory gearsFactory = Factory.getInstance();
if(gearsFactory != null) {
if(gearsFactory.getPermission()) {
RootPanel gearsPrompt = RootPanel.get("gearsPrompt");
gearsPrompt.removeFromParent();
}
}
}
});
rightPanel.add(gearsButton);
RootPanel titleBarRight = RootPanel.get("titleBarRight");
titleBarRight.add(rightPanel);
}
One solution I've found is to loop through all of the widgets under the "titleBarRight" panel and remove all widgets it contains:
if(gearsFactory.getPermission()) {
RootPanel titleBarRight = RootPanel.get("titleBarRight");
java.util.Iterator<Widget> itr = titleBarRight.iterator();
while(itr.hasNext()) {
itr.next();
itr.remove();
}
}
But somehow this still seems hacky and not quite the "right way to do it."
I know this is old, but how about...
gearsButton.addClickHandler( new ClickHandler() {
public void onClick(ClickEvent event) {
Factory gearsFactory = Factory.getInstance();
if(gearsFactory != null) {
if(gearsFactory.getPermission()) {
Button btn=(Button) event.getSource();
btn.removeFromParent();
}
}
}
});
Is there any reason for using RootPanel.get("gearsPrompt").removeFromParent(); instead of your own rightPanel.removeFromParent();? The reference is already there.
You can do :
theParentWidget.remove(index);
and the first child corresponds to 0;