The case is simple: I have several ajax components and I want to update them when some ajax action is
happened. It is easy when all of these components are in the same place and they can be reached one by another.
But if the page has a huge hierarchy this can be not so trivial and to solve this problem I would like to send some global event (which will contain an IPartialPageRequestHandler) and all these components should catch it and update himself.
How can I do this in wicket?
Actualy I see onEvent method in the component class and I can access IPartialPageRequestHandler inside of it:
public void onEvent(IEvent<?> event){
Object payload = event.getPayload();
if (payload instanceof IPartialPageRequestHandler) {
...
}
}
but how can I create the global event that should be catched by this method?
Create a custom event, for example:
public class CounterUpdate
{
private final AjaxRequestTarget target;
/**
* Constructor
*
* #param target
*/
public CounterUpdate(AjaxRequestTarget target)
{
this.target = target;
}
/** #return ajax request target */
public AjaxRequestTarget getTarget()
{
return target;
}
}
In your Ajax callback method broadcast it:
send(getPage(), Broadcast.BREADTH, new CounterUpdate(target));
In any Component/Behavior that is interested for this event do:
#Override
public void onEvent(IEvent<?> event)
{
super.onEvent(event);
// check if this is a counter update event and if so repaint self
if (event.getPayload() instanceof CounterUpdate)
{
CounterUpdate update = (CounterUpdate)event.getPayload();
update.getTarget().add(this);
}
}
you could notify the entire page or application 'page.send(...)' or 'application.send(...)'. Wicket already does it for every AJAX event to notify the entire page hierarchy. See the end of this paragraph from user guide:
https://ci.apache.org/projects/wicket/guide/8.x/single.html#_how_to_use_ajax_components_and_behaviors
Related
I have this behavior added to a component(MarkupContainer)
AjaxSelfUpdatingTimerBehavior updateBehavior = new AjaxSelfUpdatingTimerBehavior(Duration.seconds(3))
{
#Override
public void onEvent(Component component, IEvent<?> event) {
// some business logic
}
};
Somewhere , on the same page I have an AjaxLink which redirects to another page(in whom constructor I pass the actual page as a parameter) and on that page I have a "Back" AjaxLink which redirects me back , calling setResponsePage(myFirstPage) .
The problem is that even though , when rendering the page the behavior updates once , it stops updating once at 3 seconds , as was constructed for.No problem faced with the behavior until leaving the page.
Probably not the best solution , but I managed to fix it by removing the behavior onBeforeRender() of the page and adding again . I declared a field on the page private int autoUpdateBehaviorId = -1;
public void addUpdateBehavior(Component c)
{
if(autoUpdateBehaviorId >= 0)
c.remove(c.getBehaviorById(autoUpdateBehaviorId));
AjaxSelfUpdatingTimerBehavior updateBehavior = new AjaxSelfUpdatingTimerBehavior(Duration.seconds(3))
{
#Override
public void onEvent(Component component, IEvent<?> event) {
// bussines logic
}
};
c.add(updateBehavior);
autoUpdateBehaviorId = c.getBehaviorId(updateBehavior);
}
#Override
protected void onBeforeRender() {
super.onBeforeRender();
addUpdateBehavior(myContainer);
}
Not necessarily the solution to your problem; but I have implemented the behavior by overriding onConfigure method of the AjaxSelfUpdatingTimerBehavior as below.
In my case, I had to update label with a count of current records in queue every 10 seconds.
Following is code snippet:
labelToBeUpdated.add(new AjaxSelfUpdatingTimerBehavior(Duration.seconds(configurableDelay)) {
#Override
public void onConfigure(Component component) {
String inProgressOutOfTotal = "10/100"; //Business logic to get total count and inprogress count
labelToBeUpdated.setDefaultModel(Model.of(inProgressOutOfTotal));
//Set visibility of the component if needed
}
}
labelToBeUpdated.setOutputMarkupId(true);
Just curious; is it that onEvent is waiting on an event on the component in order to refresh? Since onConfigure is called before the rendering cycle has begun, it is working for me.
But as Sven Meier has mentioned, you might still want to work on his advise to get your code with onEvent.
I am building a simple app and I am implementing it in a simple MVC pattern where the controller adds event handlers to the view. Here's a sample controller code attaching a handler to the UI.
Basically, the code adds an event handler when the UI's save button is clicked. The UI contains the name and id number entry. What I wanted to happen is to pass the name and id number into the actionPerformed function.
ui.onAddStudent(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
System.out.print("test");
}
});
And the receiving function in the UI (in another file) is the following.
public void onAddStudent(ActionListener handler){
//something missing here
addStudent.addActionListener(handler);
}
I am not really into Java because it's not my forte. I actually do JavaScript. Now, a similar handler In JavaScript, one can use the call() or apply() method to call the handler and pass in additional parameters. If the above code was in JS, it would be like
//in the controller
ui.onAddStudent(function(event,id,name){
//I can use id and name
});
//in the UI
ui.onAddStudent = function(handler){
//store into a cache
//add handler to the button
}
//when student is added (button clicked)
handler.call(this,event,id,name);
How do I do the same thing in Java?
You have two choices:
let it as it is, and have the controller get the ID and name from the GUI (and that is the easiest and simplest solution, IMHO)
use your own Event and Listener types, containing this information. For example:
public class StudentAddedEvent {
private long ID;
private String name;
...
}
public interface StudentAddedListener {
void studentAdded(StudentAddedEvent event);
}
The UI would register an ActionListener on the button, and this action listener would do:
#Override
public void actionPerformed(ActionEvent e) {
long id = getIdInGui();
String name = getNameInGui();
StudentAddedEvent event = new StudentAddedEvent(id, name);
for (StudentAddedListener listener : studentAddedListeners) {
listener.studentAdded(event);
}
}
You can define your own Actions too, and set those to the buttons (constructor argument or setAction) and other components.
Extend AbstractAction for that.
I am using Eclipse 4.2 Juno, Java 1.6. I have two parts in my application. One part is registering the SelectionChangedListener:
#Inject
private ESelectionService selectionService;
#PostConstruct
public void init() {
TreeViewer bsTreeViewer = new TreeViewer(tabFolder, SWT.BORDER);
/* some other stuff */
// Event declaration
bsTreeViewer.addSelectionChangedListener(new SelectionChangedListener() {
#Override
public void selectionChanged(SelectionChangedEvent event) {
if( selectionService != null ) {
selectionService.setSelection(((IStructuredSelection)event.getSelection()).getFirstElement());
}
}
});
}
This Listener is called correctly. The first selected Element is of the right type, too.
I another part I am setting up the receiving end:
#Inject
public void setBS(#Named(IServiceConstants.ACTIVE_SELECTION) #Optional BS bs) {
if (bs == null) {
/* implementation not shown */
} else {
/* implementation not shown */
}
}
However, nothing is received on this end of the pipe. What am I doing wrong or how could I debug this?
The code above looks fine, but try to check the following issues:
check if the receiver object is created - if not, it won't receive an event
check if the receiver object is created by eclipse framework (for example if it is element of application model such as part, handler it is for sure created by the framework) - if not, the framework (selection service) does not know about the receiver object and cannot notify it
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 would like to listen for the mouse over event in GWT 1.6. Since GWT 1.6 has introduced handlers and deprecated listeners I'm unsure as to how I can accomplish this with what little information exists.
Note: I have an Element object. That's what I need to add the mouse handler to. I apologize for my lack of clarity.
Thanks!
I was hoping we'd see an answer before I needed to tackle this myself. There are some errors in the example code he posted, but the post by Mark Renouf in this thread has most of what we need.
Let's say you want to listen for mouse over and mouse out events on a custom widget. In your widget, add two methods:
public HandlerRegistration addMouseOverHandler(MouseOverHandler handler) {
return addDomHandler(handler, MouseOverEvent.getType());
}
public HandlerRegistration addMouseOutHandler(MouseOutHandler handler) {
return addDomHandler(handler, MouseOutEvent.getType());
}
Then create a handler class:
public class MyMouseEventHandler implements MouseOverHandler, MouseOutHandler {
public void onMouseOver(final MouseOverEvent moe) {
Widget widget = (Widget) moe.getSource();
widget.addStyleName("my-mouse-over");
}
public void onMouseOut(final MouseOutEvent moe) {
Widget widget = (Widget) moe.getSource();
widget.removeStyleName("my-mouse-over");
}
}
Finally, add the handler to the widget:
myWidget.addMouseOverHandler(new MyMouseEventHandler());
myWidget.addMouseOutHandler(new MyMouseEventHandler());
If you are only listening to the mouse over event, you can skip the mouse out handling. And if you aren't making a custom widget, the widget my already have a method to add the handler.
Finally, per the warning from the thread, remember to addDomHandler for the mouse events, not addHandler.
You'd want to implement these interfaces in your class:
HasMouseOverHandlers
HasMouseOutHandlers
MouseOverHandler
MouseOutHandler
MouseOverEvent is fired when the mouse enters the element, and MouseOutEvent is fired when it's no longer over.
HasMouseOverHandler is implemented like this:
public HandlerRegistration addMouseOverHandler(MouseOverHandler handler) {
return addDomHandler(handler, MouseOverEvent.getType());
}
HasMouseOutHandler is implemented like this:
public HandlerRegistration addMouseOutHandler(MouseOutHandler handler) {
return addDomHandler(handler, MouseOutEvent.getType());
}
After that you just handle the events with a MouseOverHandler and MouseOutHandler, should be pretty straightforward after that.
If you want to add an EventHandler to an Element that already exists in the HTML the only idea I've come up with is creating a wrapper class. This is completely untested.
class ElementWrapper extends UIObject implements HasMouseOverHandlers,
HasMouseOutHandlers
{
public ElementWrapper(Element theElement)
{
setElement(theElement);
}
public HandlerRegistration addMouseOutHandler(MouseOutHandler handler) {
return addDomHandler(handler, MouseOutEvent.getType());
}
public HandlerRegistration addMouseOverHandler(MouseOverHandler handler) {
return addDomHandler(handler, MouseOverEvent.getType());
}
}
Then you could get an existing Element from the HTML and initialize like this:
onModuleLoad()
{
Element theElement = RootPanel().get("elementID");
ElementWrapper wrapper = new ElementWrapper(theElement);
wrapper.addMouseOverHandler(new myHandler());
}
Hope this helps.
If you know the element's type, you can wrap the Element and get the appropriate Widget back. In the case of, say, an Image:
Element el = DOM.getElementById("someImageOnThePage");
Image i = Image.wrap(el);
i.addMouseOverHandler(...);
The only problem with this I've encountered is you'll get an AssertionError in HostedMode if the element is already attached to another parent widget. It'll work fine in production however. There's probably a good reason for that assertion, so be careful.
If you know the type of the object some widgets include a static wrap function. From one of them I was able to derive the following class.
public class Widget extends com.google.gwt.user.client.ui.Widget
{
public Widget(Element element, boolean detatchFromDom)
{
super();
if (detatchFromDom)
element.removeFromParent();
setElement(element);
if (!detatchFromDom)
{
onAttach();
RootPanel.detachOnWindowClose(this);
}
}
public <H extends EventHandler> HandlerRegistration addDomHandlerPub(final H handler, DomEvent.Type<H> type)
{
return addDomHandler(handler, type);
}
}