Handle DIV add child node event with GWT - java

Is there such a thing in GWT to handle event when elements are added to a specific DIV?
From this answer there is jQuery solution:
Fire jQuery event on div change
$('body').on('DOMNodeInserted', '#common-parent', function(e) {
if ($(e.target).attr('class') === 'myClass') {
console.log('hit');
}
});

You can integrate jquery method by using jsni.
At first you need to create corresponding gwt like event:
package com.test.gwt;
import com.google.gwt.event.shared.GwtEvent;
public class DOMNodeInsertedEvent extends GwtEvent<DOMNodeInsertedEventHandler> {
public static Type<DOMNodeInsertedEventHandler> TYPE = new Type<DOMNodeInsertedEventHandler>();
public Type<DOMNodeInsertedEventHandler> getAssociatedType() {
return TYPE;
}
protected void dispatch(DOMNodeInsertedEventHandler handler) {
handler.onDOMNodeInserted(this);
}
}
and gwt like handler:
package com.test.gwt;
import com.google.gwt.event.shared.EventHandler;
public interface DOMNodeInsertedEventHandler extends EventHandler {
void onDOMNodeInserted(DOMNodeInsertedEvent event);
}
then implement jsni wrapping function
public native void addDOMNodeInsertedEventHandler(Widget widget, Element element)/*-{
$wnd.$(element).bind('DOMNodeInserted', function (event) {
var gwtEvent = #com.test.gwt.DOMNodeInsertedEvent::new()();
widget.#com.google.gwt.user.client.ui.Widget::fireEvent(Lcom/google/gwt/event/shared/GwtEvent;)(gwtEvent);
});
}-*/;
and at last add your handler to corresponding panel
FlowPanel flowPanel = new FlowPanel();
flowPanel.addHandler(new DOMNodeInsertedEventHandler() {
#Override
public void onDOMNodeInserted(DOMNodeInsertedEvent event) {
Window.alert("Inserted");
}
}, DOMNodeInsertedEvent.TYPE);
addDOMNodeInsertedEventHandler(flowPanel, flowPanel.getElement());

Related

Is there a "best" way to invoke a class method from a static method?

I have multiple controllers, each associated to a different FXML file. There is an event in one node that requires synchronization across other nodes, so I decided to do this with another event, and event handlers in the various controller files.
To register the event handlers requires the event handler method to be static (i.e., addEventHandler(SomeEvent, ClassName::MethodName).
So, the controller looks something like...
public class MyController {
private static MyController selfRef = null;
public MyController() {
selfRef = this;
}
public static void someEventHandler(Event event) {
if (selfRef != null) {
selfRef.doSomethingUseful();
}
}
private void doSomethingUseful() { /* synch the nodes */ }
}
This works, but seems a bit of a hack. Is there a preferred mechanism to achieve the same end result?
You might have more flexibility with this if you get rid of all the static stuff and make the event handler a member of your controller class as demonstrated below.
Sample implementation without static members
import javafx.event.*;
import javafx.fxml.*;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.*;
import java.io.IOException;
class CustomerDialogController {
#FXML
private Label customerName;
private EventHandler<Event> customEventHandler = event -> {
// handle the event...
};
void initData(Customer customer) {
customerName.setText(customer.getName());
}
public EventHandler<Event> getCustomEventHandler() {
return customEventHandler;
}
}
public class EventHandling {
public Stage showCustomerDialog(Customer customer) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource("customerDialog.fxml"));
Stage stage = new Stage(StageStyle.DECORATED);
stage.setScene(new Scene(loader.load()));
CustomerDialogController controller = loader.getController();
controller.initData(customer);
stage.addEventHandler(Event.ANY, controller.getCustomEventHandler());
stage.show();
return stage;
}
}
class Customer {
private String name;
Customer(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
Notes on implementation options
In the example the event handler has been added to the stage, but it could equally have been added to any scene or node or anything that has the ability to handle events.
If desired, you could also add a setter for the event handler to allow changing of the event handling logic externally.
In addition to the setup above you might wish to have the controller self-register the event handler in it's initialize method. Whether you do so or not just depends on whether you want the ability to register event handlers exposed outside the controller or if you want to use encapsulation to hide all of the event handling logic local to the controller.
Notes on (perhaps superior) alternatives
As an alternate approach, rather than using the event handling system within JavaFX for your custom approach, you could make use of a third party system such as the Google Guava Event Bus.
You should also consider why you need to add custom event handling to your application. JavaFX supports very flexible binding and observer patterns. By exposing properties of your model objects as observable, it is often not necessary to have custom events. Often, your view controllers can observe any changes to associated model objects and modify the internal state of model objects based upon UI interactions. This is especially the case if you introduce a dependency injection based system for injecting models into your controllers, such as Guice, Spring, afterburner.fx or Gluon Ignite.
Maybe you could use some kind of registry, which takes care of the synchronisation. Here is a quick and dirty example:
public class Synchronizer {
private ObservableList<Node> nodes;
private boolean isSyncing;
public Synchronizer() {
nodes = FXCollections.observableArrayList();
}
public void addNode(Node node) {
nodes.add(node);
}
public void sync(Node sourceNode, Event event) {
if (isSyncing) {
return;
}
isSyncing = true;
for (Node node : nodes) {
if (node != sourceNode) {
node.fireEvent(event);
}
}
isSyncing = false;
}
}
In your Controller you can add the node, whose event you like to get synchronized, to the synchronizer, and call sync() in the eventListener.
public class Controller {
private StackPane root;
private Button button;
public Controller(Synchronizer synchronizer) {
button = new Button();
button.setOnAction(evt -> {
synchronizer.sync(button, evt);
//action
});
synchronizer.addNode(button);
root = new StackPane(button);
}
}
EDIT:
This should make for a cleaner version:
public class Starter extends Application {
#Override
public void start(Stage primaryStage) {
ViewController controller1 = new ViewController();
ViewController controller2 = new ViewController();
Synchronizer synchronizer = new Synchronizer();
synchronizer.add(controller1);
synchronizer.add(controller2);
VBox box = new VBox(controller1.root, controller2.root);
primaryStage.setScene(new Scene(box));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
public interface SyncTarget {
Node getSyncNode();
void triggerAction();
}
public class Synchronizer {
private ObservableList<SyncTarget> syncTargets;
private EventHandler<Event> eventHandler;
public Synchronizer() {
syncTargets = FXCollections.observableArrayList();
eventHandler = e -> sync();
}
public void add(SyncTarget target) {
syncTargets.add(target);
target.getSyncNode().addEventHandler(ActionEvent.ANY, eventHandler);
}
public void remove(SyncTarget target) {
syncTargets.remove(target);
target.getSyncNode().removeEventHandler(ActionEvent.ANY, eventHandler);
}
public void sync() {
for (SyncTarget target : syncTargets) {
target.triggerAction();
}
}
}
public class ViewController implements SyncTarget {
private StackPane root;
private Button button;
public ViewController() {
button = new Button();
root = new StackPane(button);
}
#Override
public Node getSyncNode() {
return button;
}
#Override
public void triggerAction() {
//action
}
}
}

Why page cannot get bookmarked with History mechanism in GWT/MVP4G.?

I am trying to implement history mechanism with a GWT app but has problem with page bookmark i.e. in my case, I have created 3 pages where one get invoked from another. Now, the problem is if page3 is bookmarked then while invoking that bookmark it should open page3 instead now it opens Home page.
Why is it so.? What can be the issue.?
I have implemented HistoryConverter as,
#History(type=HistoryConverterType.SIMPLE)
public class MyHistoryConverter implements HistoryConverter<HistoryManagerEventBus> {
public MyHistoryConverter() {
}
#Override
public void convertFromToken(String historyName, String param,HistoryManagerEventBus eventBus) {
eventBus.dispatch(historyName);
}
public String convertToToken(String eventType){
return eventType;
}
public String convertToToken(String eventType,HistoryPageTwoView view){
return view.getClass().getName();
}
public String convertToToken(String eventType,HistoryPageThreeView view){
return view.getClass().getName();
}
#Override
public boolean isCrawlable() {
return false;
}
}
and eventBus as,
#Events(startPresenter = HistoryPageOnePresenter.class,historyOnStart=true)
public interface HistoryManagerEventBus extends EventBusWithLookup {
/**
* Start event will be fired internally
*/
#Start
#Event(handlers = HistoryPageOnePresenter.class,historyConverter=MyHistoryConverter.class)
void start();
#InitHistory
#Event(handlers = HistoryPageOnePresenter.class)
void init();
#Event(handlers = HistoryPageTwoPresenter.class,historyConverter=MyHistoryConverter.class)
void getHistoryPageTwo();
#Event(handlers=HistoryPageThreePresenter.class,historyConverter=MyHistoryConverter.class)
void getHistoryPageThree();
#Event(handlers=HistoryPageOnePresenter.class,historyConverter=MyHistoryConverter.class)
void getHistoryPageOne();
#Event(handlers=HistoryPageOnePresenter.class)
void setHistoryPageTwo(HistoryPageTwoView view);
#Event(handlers=HistoryPageOnePresenter.class)
void setHistoryPageThree(HistoryPageThreeView view);
}
Assuming that:
#Event(handlers = HistoryPageTwoPresenter.class,historyConverter=MyHistoryConverter.class)
void getHistoryPageTwo();
#Event(handlers=HistoryPageThreePresenter.class,historyConverter=MyHistoryConverter.class)
void getHistoryPageThree();
#Event(handlers=HistoryPageOnePresenter.class,historyConverter=MyHistoryConverter.class)
void getHistoryPageOne();
are your navigation events, there is no need to have the following methods inside the MyHistoryConverter class defined:
public String convertToToken(String eventType,HistoryPageTwoView view){
return view.getClass().getName();
}
public String convertToToken(String eventType,HistoryPageThreeView view){
return view.getClass().getName();
}
as they are not called to create history tokens.
If your history converter works, you should see something like that in your URL:
[myURL]#getHistoryPageOne
or
[myURL]#getHistoryPageTwo
or
[myURL]#getHistoryPageThree
If you entering:
[myURL]#getHistoryPageThree
to start your application, the tokens will be handle in the convertFromToken-method.
You can add the #Debug-annotation to your eventBus to verify that the bookmarked event is fired at the start of your application.
So everything looks good, except the fact, that the Start-event should not have a historyConverter-attribute.

How to add properly UiHandler for BlurEvent in GWT?

Normally if we have some textField in GWT we can add a BlurHandler by the following code:
textField.addBlurHandler(new BlurHandler() {
#Override
public void onBlur(BlurEvent event) {
//what we need
}
});
But if we use UiBinder and our textField is annotated by #UiField and it is mentioned in our ui.xml file we can add BlurHandler by this code as well:
#UiHandler("textField")
protected void createBlurHandler(BlurEvent event) {
}
I guess I am right here because it works like this. So, the question is, can we actually define BlurHandler inside ui.xml file?
For example, it is possible to add inputMaxLength and some other attributes there, does GWT has some possibility like onChange method or are these ways that I described the only possibilities?
I would like to have something like this:
<g:TextBox ui:field="textField" onBlur="methodName" />
Is it possible?
I am pretty sure what you are asking is not possible. The problem is that you wouldn't be able to use reflection to figure out which method you want to call. However you can extends the TextBox class and use that inside your template. The extended class could have it's own properties that can be set in the template. An example is as follows where I set the default test on my own DefaultTextBox.
public class DefaultTextBox extends TextBox {
/**
* The text color used when the box is disabled and empty.
*/
private static final String TEXTBOX_DISABLED_COLOR = "#AAAAAA";
private final String defaultText;
public #UiConstructor
DefaultTextBox(final String defaultText) {
this.defaultText = defaultText;
resetDefaultText();
// Add focus and blur handlers.
addFocusHandler(new FocusHandler() {
#Override
public void onFocus(FocusEvent event) {
getElement().getStyle().clearColor();
getElement().getStyle().clearFontStyle();
if (defaultText.equals(getText())) {
setText("");
}
}
});
addBlurHandler(new BlurHandler() {
#Override
public void onBlur(BlurEvent event) {
if ("".equals(getText())) {
resetDefaultText();
}
}
});
}
public String getDefaultText() {
return defaultText;
}
#Override
public void setText(String text) {
if (text == null) {
super.setText(getDefaultText());
} else {
getElement().getStyle().clearColor();
getElement().getStyle().clearFontStyle();
super.setText(text);
}
}
public String getText() {
return super.getText();
}
/**
* This is override so that the editor framework will not get the default
* value but the actual null value when the default text is in the box.
*/
#Override
public String getValue() {
try {
return getValueOrThrow();
} catch (ParseException e) {
return null;
}
}
#Override
public void setValue(String value) {
setText(value);
}
/**
* This is overridden from the parent class because this is
* how the editor gets the value.
*/
public String getValueOrThrow() throws ParseException {
if (defaultText.equals(super.getValueOrThrow())) {
return null;
}
return super.getValueOrThrow();
}
/**
* Reset the text box to the default text.
*/
public void resetDefaultText() {
setText(defaultText);
getElement().getStyle().setColor(TEXTBOX_DISABLED_COLOR);
getElement().getStyle().setFontStyle(FontStyle.ITALIC);
}
}
Then in the template you can set properties like this.
<w:DefaultTextBox defaultText="name" ui:field="nameTextBox" />
This will also work with setters, you can set properties without having to use the #UiConstructor but in my case I wanted to make sure that there was no empty constructor for this class.

Is it possible to write your own objects that give out ActionEvents?

I've looked at the java tutorials online and they all seem concerned with catching ActionEvents given out by other components that are already written. Is it possible to write your own objects that have there own set of criteria that trigger actionEvents that can then be caught by other classes that have registered as listeners?
So for example: If I wanted an object that was counting sheep to send out an actionEvent when 100 sheep had been counted to all the sleeper objects that had registered as listeners.
Is there a way to do this are there any tutorials online?
Any help is greatly appreciated.
Yes, it's pretty straightforward, once someone shows you how to create your own listeners.
First, you create your own EventObject. Here's an example from one of my projects.
import gov.bop.rabid.datahandler.bean.InmateDataBean;
import java.util.EventObject;
public class InmatePhotoEventObject extends EventObject {
private static final long serialVersionUID = 1L;
protected InmateDataBean inmate;
public InmatePhotoEventObject(Object source) {
super(source);
}
public InmateDataBean getInmate() {
return inmate;
}
public void setInmate(InmateDataBean inmate) {
this.inmate = inmate;
}
}
There's nothing special about this class, other than it extends EventObject. Your constructor is defined by EventObject, but you can create any methods you want.
Second, you define an EventListener interface.
public interface EventListener {
public void handleEvent(InmatePhotoEventObject eo);
}
You would use the EventObject you created. You can use any method name or names that you want. This is the interface for the code that will be written as a response to the listener.
Third, you write a ListenerHandler. Here's mine from the same project.
import gov.bop.rabid.datahandler.bean.InmateDataBean;
import gov.bop.rabid.datahandler.main.EventListener;
import gov.bop.rabid.datahandler.main.InmatePhotoEventListener;
import gov.bop.rabid.datahandler.main.InmatePhotoEventObject;
import java.util.ArrayList;
import java.util.List;
public class InmatePhotoListenerHandler {
protected List<EventListener> listeners;
public InmatePhotoListenerHandler() {
listeners = new ArrayList<EventListener>();
}
public void addListener(EventListener listener) {
listeners.add(listener);
}
public void removeListener(EventListener listener) {
for (int i = listeners.size() - 1; i >= 0; i--) {
EventListener instance = listeners.get(i);
if (instance.equals(listener)) {
listeners.remove(i);
}
}
}
public void fireEvent(final InmatePhotoEventObject eo,
final InmateDataBean inmate) {
for (int i = 0; i < listeners.size(); i++) {
final EventListener instance = listeners.get(i);
Runnable runnable = new Runnable() {
public void run() {
eo.setInmate(inmate);
instance.handleEvent(eo);
}
};
new Thread(runnable).start();
}
}
public static void main(String[] args) {
System.out.println("This line goes in your DataHandlerMain class "
+ "constructor.");
InmatePhotoListenerHandler handler = new InmatePhotoListenerHandler();
System.out.println("I need you to put the commented method in "
+ "DataHandlerMain so I can use the handler instance.");
// public InmatePhotoListenerHandler getInmatePhotoListenerHandler() {
// return handler;
// }
System.out.println("This line goes in the GUI code.");
handler.addListener(new InmatePhotoEventListener());
System.out.println("Later, when you've received the response from "
+ "the web service...");
InmateDataBean inmate = new InmateDataBean();
inmate.setIntKey(23);
handler.fireEvent(new InmatePhotoEventObject(handler), inmate);
}
}
The main method in this class shows you how you use a ListenerHandler. The rest of the methods in the class are standard. You would use your own EventObject and EventListener.
Yes.
I suggest you look at the java API documentation for ActionEvent and EventListenerList.
I also suggest that you read about the Listener (also called Observer) pattern.

Using null assignment to control addition and removal of listeners

I have a case where a JComponent needs to have a listener added or removed depending on the state of other fields of a class. The listener should not be added more than once, and it can, of course, be removed only once. Is it a good practice to use a class field to store the listener and use the null value to control the action of registering/unregistering the listener with the Component.
The code I have in mind is something like this (code modified to make it clear that the JComponent is provided to the class):
public class MyClass {
private ActionListener fListener = null;
private JComponent fComponent;
public MyClass(JComponent component) {
fComponent = component; // for example, component = new JButton("Test");
}
public void setListener() {
if (fListener == null ) {
fListener = new MyListener();
fComponent.addActionListener(fListener);
}
}
public void removeListener() {
if (fListener != null) {
fComponent.removeActionListener(fListener);
fListener = null;
}
}
}
Do not instantiate and dispose listener object every time. Use getActionListeners() method to verify that listener is added or not.
public class MyClass {
private ActionListener fListener = new MyListener();
private JButton fComponent = new JButton("Test");
public MyClass() {
fComponent.addActionListener(fListener);
}
public void setListener() {
if (fComponent.getActionListeners().length == 0) {
fComponent.addActionListener(fListener);
}
}
public void removeListener() {
if (fComponent.getActionListeners().length !=0) {
fComponent.removeActionListener(fListener);
}
}
}
Method ActionListener[] getActionListeners() returns array of all the ActionListeners added to this JButton.
Is it absolutely necessary to continually add and remove the listener from the component? Can you just disable the component, or have a flag that you can use to determine if the action can be run?
Can you wrap the listener in another listener that you define? The enveloping listener can have a boolean switch you can flip to control delegation to the real listener.
If worse comes to worst and you absolutely have to remove and add the listener, you can do it as follows, with a twist on AVD's solution:
public void setListener() {
// java.util.List and java.util.Arrays
List<ActionListeners> listenerList = Arrays.asList(fComponent.getActionListeners());
if (!listenerList.contains(fListener) {
fComponent.addActionListener(fListener);
}
}
public void removeListener() {
List<ActionListeners> listenerList = Arrays.asList(fComponent.getActionListeners());
if (listenerList.contains(fListener) {
fComponent.removeActionListener(fListener);
}
}

Categories

Resources