Is it possible/how to display a message on the FeedbackPanel more than one time - wicket

I have a org.apache.wicket.markup.html.panel.FeedbackPanel in my panelA class. The feedback panel is instantiated in a panelA constructor with one message to display -> feedbackPanel.info("displayFirstTime"). I am navigating to a new page and later to the previous panelA with a command
target.getPage().get(BasePage.CONTENT_PANEL_ID).replaceWith(panelA);
but the message "displayFirstTime" won't be displayed on the feedback panel again.
I have made it with overriding a panel onBeforeRender method
#Override
public void onBeforeRender() {
super.onBeforeRender();
if (again_displayCondition) {
this.info("displayFirstTime");
}
}
but it's not a clean solution.
Is it possible or how to make it, that when moving to a panelA page for the 2nd time the feedback message will be also displayed ?

Wicket uses application.getApplicationSettings().getFeedbackMessageCleanupFilter() to delete the feedback messages at the end of the request cycle.
By default it will delete all already rendered messages.
You can setup a custom cleanup filter that may leave some of the messages, e.g. if they implement some interface. For example:
component.info(new DoNotDeleteMe("The actual message here."));
and your filter will have to check:
#Override
public boolean accept(FeedbackMessage message)
{
if (message.getMessage() instanceOf DoNotDeleteMe) {
return false;
}
return message.isRendered();
}
Make sure you implement DoNotDeleteMe#toString() so that it renders properly. Or you will have to use a custom FeedbackPanel too.
DoNotDeleteMe must implement java.io.Serializable!

Related

Wicket 6 - Feedback message not showing until request after the request in which it happened

I have an AjaxButton. The event fires, and I'm testing a scenario where I need to add a feedback and return without committing the change. I add the feedback at the page level (our feedback only shows page level messages...others are showing on component level feedback panels). I add the WebMarkupContainer that contains the feedback panel to the target. This exact thing works on every other button on the page.
But for this button, which happens to be the only one where defaultformprocessing is not false, the feedback doesn't show. To the user's view, nothing happens except our processing veil appears and then disappears. If I hit submit again, THEN the message and feedback are shown. I stuck a timestamp on it to see if it was showing the one from the 2nd request or the 1st. It's from the 1st.
What's more, a breakpoint in the feedback's filter shows that the filter was never called in the 1st request, but is called BEFORE the event processing on the 2nd request. It accepts the message as intended.
I set defaultformprocessing to FALSE on this button as a test, and in fact, messages suddenly work. But of course, that also means the form doesn't get processed. Can someone help me square this circle?
AjaxButton:
add(new AjaxButton("btnCreateRequest", getForm()) {
#Override
public void onSubmit(AjaxRequestTarget target, Form<?> form) {
//stuff happens
target.add(getFeedbackPanelForAjax());
String date = new Date().toGMTString();
System.out.println("ADDING MESSAGE - " + date);
getPage().error("This is a message! " + date);
return;
}
#Override
public void onError(AjaxRequestTarget target, Form<?> form) {
getPage().error("There was an error processing your request");
target.add(getFeedbackPanelForAjax());
target.add(form);
}
}.setVisible(enabled));
UPDATE:
getFeedbackPanelForAjax returns the web markup container that the feedback resides in. I've also tried adding the feedback directly to the target.
public Component getFeedbackPanelForAjax() {
return (Component) getForm().get("feedbackWmc");
}
Where the feedback is added:
feedback = new FRFeedbackPanel("feedback") {
#Override
public boolean isVisible() {
if(anyMessage()){
return true;
} else {
return false;
}
}
};
// feedback container
WebMarkupContainer feedbackWmc = new WebMarkupContainer("feedbackWmc");
getForm().add(feedbackWmc.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true));
feedbackWmc.add(feedback.setOutputMarkupId(true).setOutputMarkupPlaceholderTag(true));
I can say that through debugging, I put a breakpoint in anyMessage() and it returns false in this case at the same time that getPage().getFeedbackMessages() returns the message correctly. I commented out this override of isVisible() and indeed, the message shows. The problem is, that it means the artifacts of the feedback panel show when there are no messages as well, which is not what we want.
This anyMessage() solution works perfectly when I'm in an event that is defaultformprocessing=false. I suppose I could do an anyMessage() || getPage().getFeedbackMessages(), but my understanding was that anyMessage was supposed to find if there was ANY message in the hierarchy for this panel. Is that not so?
I assume you cannot replicate the problem in a small quickstart?
One idea: I've seen similar problems when the FeedbackPanel collects its messages too early, i.e. before you add the error to the page.
FeedbackMessagesModel keeps the messages to render until the end of the request - maybe some of your code triggers this by accessing the messages model.

how to stop firing unrelated event of event bus

My problem is with how to stop firing unrelated event of event bus. as I got this solution for Dialog box.
but it does not work in case of where one instance already initialize and try to create new instance of same class.
Just example: A below scroll panel has handler initialized. it used for document preview.
class TestScroll extends ScrollPanel
{
public TestScroll(){
}
implemented onload()
{
// eventBus.addHandler code here.
//here some preview related code
}
unload() method
{
//eventBus remove handler code
}
}
This preview has some data which contains some links that open different preview but with same class and different data structure,
Now The problem is like onUnload ( which contains code of remove handler) event does not load , because other panel opened. that does not mean previous panel unload.
So in that case, twice event handler registered. when one event fired then other event also fired.
Due to that, Preview 1 data shows properly, but after that Preview2 opened and when I close it, I find Preview1=Preview2.
so how can I handle such situation?
As per no of instance created each event fired. but I have to check some unique document id with if condition in event itself.
is there any other ways to stop unrelated event firing?
Edit:
public class Gwteventbus implements EntryPoint {
int i=0;
#Override
public void onModuleLoad() {
TestApp panel=new TestApp();
Button button=new Button("Test Event");
button.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
TestApp panel=new TestApp();
int j=i;
new AppUtils().EVENT_BUS.fireEventFromSource(new AuthenticationEvent(),""+(j));
i++;
}
});
panel.add(button);
RootPanel.get().add(panel);
}
}
public class AppUtils {
public static EventBus EVENT_BUS = GWT.create(SimpleEventBus.class);
}
public class TestApp extends VerticalPanel{
String testString="";
public TestApp( ) {
AppUtils.EVENT_BUS.addHandler(AuthenticationEvent.TYPE, new AuthenticationEventHandler() {
#Override
public void onAuthenticationChanged(AuthenticationEvent authenticationEvent) {
System.out.println("helloworld"+authenticationEvent.getSource());
}
});
}
}
These are wild guesses as it's difficult to really answer it without code and a clear description.
I'm guessing you have one eventbus for all the panels. So when you register a handler it is registered with that one eventbus. In case you fire an event from one of the panels to the eventbus all panels will receive the event.
To fix this you can either create a new eventbus per panel or check who fired the event with event.getSource().
If this doesn't make sense you probably are reusing a variable or use a static variable which actually should be a new instance or none static variable.
You can use the GwtEventService-Library to fire specific events over a unique domain and every receiver that is registered at this domain receives that events then. You can handle as many different events/domains as you want.
In order to remove a handler attached to the EventBus, you must first store a reference to the HandlerRegistration returned by the addHandler method:
HandlerRegistration hr = eventBus.addHandler(new ClickHandler(){...});
Then you can remove the handler with the removeHandler method:
hr.removeHandler();
A final note worth mentioning is that when using singleton views, like is typical with MVP and GWT Activities and Places, it is best practice to make use of a ResettableEventBus. The eventBus passed to an activity's start() is just such a bus. When the ActivityManager stops the activity, it automatically removes all handlers attached to the ResettableEventBus.
I would strongly recommend reading the GWT Project's documentation on:
Activities and Places
Large scale application development and MVP

wicket download link clear feedback panel

I have couple of drop downdowns and a download link button. Based on the user selection, i get the file to be downloaded. if the user did not make a selection I show an error on the feedback panel. if the user then makes a selection and clicks on download link it works fine, but the previous feedback message is still visible. How do I clear it.
onclick of the download link, i tried the following, but no use
FeedbackMessages me = Session.get().getFeedbackMessages();
me.clear();
Probably it is
Session.get().cleanupFeedbackMessages()
even it has been changed in Wicket 6.x
I've found this post and I think it is time to share the way for Wicket 6.x and for Wicket 7.x, because Session.get().cleanupFeedbackMessages() was deprecated already.
To do it for Wicket 6.x you have to implement additional filter for the feedback panel. Where to do it, it is your decision to decide.
Create a new FeedbackPanel implementation by extending from the existing FeedBackPanel class
private class MessagesFeedbackPanel extends FeedbackPanel{
private MessageFilter filter = new MessageFilter();
public MessagesFeedbackPanel(String id){
super(id);
setFilter(filter);
}
#Override
protected void onBeforeRender(){
super.onBeforeRender();
// clear old messages
filter.clearMessages();
}
}
Provide a new Filter implementation, by implementing the existing IFeedbackMessageFilter interface
public class MessageFilter implements IFeedbackMessageFilter{
List<FeedbackMessage> messages = new ArrayList<FeedbackMessage>();
public void clearMessages(){
messages.clear();
}
#Override
public boolean accept(FeedbackMessage currentMessage){
for(FeedbackMessage message: messages){
if(message.getMessage().toString().equals(currentMessage.getMessage().toString()))
return false;
}
messages.add(currentMessage);
return true;
}
}
Following code works for me in Wicket 6:
public class MyComponent extends Panel {
...
FeedbackMessages feedback = getFeedbackMessages();
feedback.clear();

GWT 2.4 customized ListBox doesn't fire Change event

I have added some extra functionality to the standard GWT ListBox by extending it like so:
public class FeatureListBox extends ListBox
{
public FeatureListBox()
{
}
public FeatureListBox(boolean isMultipleSelect)
{
super(isMultipleSelect);
}
public FeatureListBox(Element element)
{
super(element);
}
}
Nothing fancy here. However, the Change event is not firing now, or at least the handler (attached per below) is not getting invoked.
FeatureListBox listBox = new FeatureListBox();
listBox.addChangeHandler(new ChangeHandler()
{
public void onChange(ChangeEvent event)
{
// Do something here...
}
});
Any ideas why?
Either remove the no-argument constructor from FeatureListBox or call super() inside it, otherwise the initialization in the superclasses won't happen, which would probably result in what you're seeing.
The problem was in the way I was using my custom list box. In my application I wrap GWT Widgets around existing DOM elements on the page using the static wrap() methods of their widget classes in which the widgets get marked as attached, making them fire events. I didn't do that with my custom list box class originally, so I ended up implementing a static wrap() method similar to the one of the regular ListBox widget and using it in my code. Everything works like a charm now.

How to group gwt composite widgets change events as one change event

If i have a GWT composite widget with three text boxes like for SSN, and i need to fire change event only when focus is lost from the widget as a whole, not from individual text boxes how to go about doing that?
If you want just the event when your whole widget loses focus (not the text boxes), then make the top level of your widget be a FocusPanel, and expose the events that it gives you.
You need to implement the Observer Pattern on your composite, and trigger a new notification everytime:
the focus is lost on a specific text box AND
the focus was not transferred to any of the other text boxes.
Couldn't you use a timer? On lost focus from a text box, start a 5ms (or something small) timer that when it hits, will check focus on all 3 TextBox instances. If none have focus, then you manually notify your observers. If one has focus, do nothing.
Put this in your Composite class:
private Map<Widget, Boolean> m_hasFocus = new HashMap<Widget, Boolean>();
And then add this to each one of your TextBox instances:
new FocusListener() {
public void onFocus(Widget sender) {
m_hasFocus.put(sender, Boolean.TRUE);
}
public void onLostFocus(Widget sender) {
m_hasFocus.put(sender, Boolean.FALSE);
new Timer() {
public void run() {
for (Boolean bool : m_hasFocus.values()) {
if (bool) { return; }
}
notifyObservers();
}
};
}
};