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);
}
}
Related
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
I would like to know
Am I doing things (the following) too complicated?
Is there a better way to update the main content of an activity that allows me to bookmark the event calendar of a store via URL like #MainPlace:eventCalendar?storeId=<id>?
I'm having this ActivityMapper here
public class AppActivityMapper implements ActivityMapper {
private ClientFactory clientFactory;
private MainActivity mainActivity;
// ..
#Override
public Activity getActivity(Place place) {
if (place instanceof LoginPlace) {
return new LoginActivity((LoginPlace) place, clientFactory);
} else if (place instanceof MainPlace) {
if(this.mainActivity == null) {
this.mainActivity = new MainActivity((MainPlace) place, clientFactory);
} else {
this.mainActivity.updateMainContent(((MainPlace) place).getMainContentToken());
}
return this.mainActivity;
}
return null;
}
}
and a MainActivity that controls my MainView that is just a menu ond the left side and the main content on the right side.
I want to decouple my views like in Best Practices for Architecting GWT App which is why I'm trying to control the main content by using events that get fired as something gets clicked in my MenuView.
Therefore I am initializing some event handlers in my MainActivity that react to clicks on the buttons in my menu to delegate the update to the MainView.
public class MainActivity extends AbstractActivity implements MainView.MainPresenter {
#Override
public void start(AcceptsOneWidget panel, EventBus eventBus) {
this.mainView = this.clientFactory.getMainView();
this.mainView.setPresenter(this);
this.mainView.initialize();
this.eventBus = eventBus;
this.eventBus.addHandler(HomeClickedEvent.TYPE, new HomeClickedHandler() {
#Override
public void onHomeClicked(HomeClickedEvent event) {
goTo(new MainPlace("home"));
}
});
this.eventBus.addHandler(EventCalendarClickedEvent.TYPE, new EventCalendarClickedHandler() {
#Override
public void onEventCalendarClicked(EventCalendarClickedEvent eventCalendarClickedEvent) {
goTo(new MainPlace("eventCalendar?storeId=" + eventCalendarClickedEvent.getStoreId()));
}
});
panel.setWidget(this.mainView.asWidget());
}
#Override
public void goTo(Place place) {
this.clientFactory.getPlaceController().goTo(place);
}
#Override
public void updateMainContent(String currentMainContentToken) {
this.mainView.updateMainContent(currentMainContentToken);
}
}
this event gets fired by MenuPresenter.clickedEventCalendar() that reacts to a click on the corresponding menu entry of the MenuView:
public class MenuPresenter implements MenuView.MenuPresenter {
// ..
#Override
public void clickedEventCalendar(Long storeId) {
this.eventBus.fireEvent(new EventCalendarClickedEvent(storeId));
}
}
One of the things I really don't like is this where I append parameters to the token e.g. to display the event calendar of a store given by storeId:
#Override
public void onEventCalendarClicked(EventCalendarClickedEvent eventCalendarClickedEvent) {
goTo(new MainPlace("eventCalendar?storeId=" + eventCalendarClickedEvent.getStoreId()));
}
is there a cleaner solution for a problem like this in GWT? I don't like the fact that I'd have to parse that string in my actual event calendar. Am I using the ActivityMapper wrong or is there simply no other way to do this?
This question should really be split into several separate ones, but that's maybe something to keep in mind for the future. If you're asking one thing then it's easier to answer thoroughly and others can find the answer easier too.
Anyway, I can see a few improvements:
use EventBinder to get rid a bit of the cruft when handling and creating new events.
if you just want to let the presenter know that a button was pressed on in the view (associated with that presenter) sending a custom event over the event bus is a bit of an overkill. Depending on your needs you can expose the button in your view's interface:
public interface Display {
HasClickHandlers getButton();
}
And then just register the ClickHandler in your presenter.
Or, if you need to do something view- and presenter- related on the click, register the ClickHandler in your view and call the presenter:
// In MainView:
#UiHandler("button")
void handleClick(ClickEvent event) {
// Do some stuff with view,
// like hide a panel or change colour
panel.setVisible(false);
// Let the presenter know that a click event has been fired
presenter.onEventCalendarClicked();
}
you're right - creating MainPlace like you are proposing is wrong. You are creating the token too soon - that's what the tokenizer associated with the place is for. You should create MainPlace by passing just the storeId to the constructor - why should MainPresenter (or any other class using this place) should know how to create the token? MainPlace should look more like this:
public class MainPlace extends Place {
private final Long storeId;
public MainPlace(Long storeId) {
this.storeId = storeId;
}
public Long getStoreId() {
return storeId;
}
public static class Tokenizer implements PlaceTokenizer<MainPlace> {
#Override
public MainPlace getPlace(String token) {
return new MainPlace(Long.valueOf(token));
}
#Override
public String getToken(MainPlace place) {
return "eventCalendar?storeId=" + place.getStoreId();
}
}
}
Now, it's the Tokenizer's responisibily to create and parse the token. Just remember to register it on your PlaceHistoryMapper.
Is there someway i can have several different wicket components have the same implementation of isVisible()
for instance i have Labels, TextFields, DropdownChoices and etc that have the same isVisible method but i dont wont to implement custom classes for all of them since is hard to maintain changes to the code.
btw i can't put them in a webmarkupcontainer due to the design of the page.
I want them all to inherit something like this.
public class DepositoryFormComponent extends Component
{
public DepositoryFormComponent(String id) {
super(id);
}
public DepositoryFormComponent(String id, IModel model) {
super(id, model);
}
public boolean isVisible() {
return isFormDepositoryType();
}
protected boolean isFormDepositoryType() {
return getCurrentSelections().getSelectedOwnedAccount().getAssetType() == AssetType.DEPOSITORY;
}
protected CurrentSelections getCurrentSelections() {
return (CurrentSelections) getSession().getAttribute(CurrentSelections.ATTRIBUTE_NAME);
}
public void onRender(){};
}
You have several options:
If you've got control over the markup, and can group in a single tag all the components you want to control visibility of, you could use a <wicket:enclosure> tag to make a Component control visibility of an entire piece of markup. Notice this won't affect page design, and would achieve a similar effect as to adding a WebMarkupContainer
You could add to those components an IBehavior that will calculate visibility and call setVisible() on the Component. You can also invoke Component#setVisibilityAllowed() if you don't want future calls to setVisible() to alter the Component's visibilty. Maybe not exactly as overriding isVisible, but I think it'll be unlikely to achieve an override if you don't create custom components.
public class VisiblityControlBehavior extends AbstractBehavior {
private boolean isComponentVisible() {
return isFormDepositoryType();
}
protected boolean isFormDepositoryType() {
return getCurrentSelections().getSelectedOwnedAccount().getAssetType() == AssetType.DEPOSITORY;
}
protected CurrentSelections getCurrentSelections() {
return (CurrentSelections) getSession().getAttribute(CurrentSelections.ATTRIBUTE_NAME);
}
#Override
public void bind(Component component) {
boolean visible = isComponentVisible();
component.setVisible(visible);
component.setVisibilityAllowed(visible);
}
}
I wonder how to use the EventBus or whether there are some better solutions to send an Event through the project.
Widget1 has a Button. Widget2 has a Label, that should change when I press the button. These widgets are in a DockLayout:
RootLayoutPanel rootLayoutPanel = RootLayoutPanel.get();
DockLayoutPanel dock = new DockLayoutPanel(Unit.EM);
dock.addWest(new Widget1(), 10);
dock.add(new Widget2());
rootLayoutPanel.add(dock);
I have declared an handleClickAlert in Widget1:
#UiHandler("button")
void handleClickAlert(ClickEvent e) {
//fireEvent(e);
}
When you divide the project into logical parts (for example with MVP), then the different parts sometimes need to communicate. Typical this communication is done by sending status changes, e.g.:
user logged-in / logged-out.
user navigated directly via URL to a page, so the menu needs to be updated.
Using the event bus is quite logical in those cases.
To use it you instantiate one EventBus per app which is then used by all other classes. To achieve this use a static field, factory or dependency injection (GIN in case of GWT).
Example with your own event types:
public class AppUtils{
public static EventBus EVENT_BUS = GWT.create(SimpleEventBus.class);
}
Normally you'd also create your own event types and handlers:
public class AuthenticationEvent extends GwtEvent<AuthenticationEventHandler> {
public static Type<AuthenticationEventHandler> TYPE = new Type<AuthenticationEventHandler>();
#Override
public Type<AuthenticationEventHandler> getAssociatedType() {
return TYPE;
}
#Override
protected void dispatch(AuthenticationEventHandler handler) {
handler.onAuthenticationChanged(this);
}
}
and the handler:
public interface AuthenticationEventHandler extends EventHandler {
void onAuthenticationChanged(AuthenticationEvent authenticationEvent);
}
Then you use it like this:
AppUtils.EVENT_BUS.addHandler(AuthenticationEvent.TYPE, new AuthenticationEventHandler() {
#Override
public void onAuthenticationChanged(AuthenticationEvent authenticationEvent) {
// authentication changed - do something
}
});
and fire the event:
AppUtils.EVENT_BUS.fireEvent(new AuthenticationEvent());
How do you add an event listener or handler to widgets in GWT 1.7?
I know there are some questions alreayd about this on SO but it seems they are outdated.
For example (ignoring the fact that there is a :hover in CSS) how do I add a Hover listener to a FlexTable for example?
If you want to add a MouseOverHandler to a FlexTable try this:
public class MyFlexTable extends FlexTable implements MouseOverHandler, HasMouseOverHandler {
public MyFlexTable() {
this.addMouseOverHandler(this);
}
public void onMouseOver(MouseOverEvent event) {
//do something
}
public HandlerRegistration addMouseOverHandler(MouseOverHandler handler) {
return addDomHandler(handler, MouseOverEvent.getType());
}
}
Starting in GWT 1.6 you use Handlers instead of Listeners. So for example, for hovering you would add a MouseOverHandler and MouseOutHandler. The FlexTable itself doesn't implement these interfaces so you'll probably want to implement it on the widgets contained in the FlexTable. For example,
myWidget.addMouseOverHandler(new MouseOverHandler(){
void onMouseOver(MouseOverEvent event){
doHovering();
}
});
Similarly for adding a MouseOutHandler.