GWT: How to distinguish ClickEvent on anchor in HTML component? - gwt

I have com.google.gwt.user.client.ui.HTML component which contains some anchors.
When the component is clicked I need distinguish the clicks on anchors from the clicks on rest of the content of the component.
Example:
htmlText = new HTML();
htmlText.setHTML("foo <a href=http://stackoverflow.com target=_blank>stackoverflow</a> bar");
htmlText.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
if (!anchorClicked(event)) doSomethingElse();
}
});
When the "stackoverflow" hyperlink is clicked, I want default behaviour - go to stackoverflow.com. When "foo" or "bar" is clicked, I want "doSomethingElse()" to be called.
Is there anyway to achieve that? What should be in the anchorClicked(e) method?

You ought to check if your EventTarget is the hyperlink element (or a child of hyperlink).
Lets say the id of your hyperlink is "corvus-link"
Element link = Document.get().getElementById("corvus-link");
Element trgt = Element.as(e.getNativeEvent().getEventTarget());
Then what you need to check in your case is:
link.isOrHasChild(trgt);
EDIT:
the method you've asked for would look something like this:
boolean anchorClicked(e)
{
Element link = Document.get().getElementById("corvus-link");
Element trgt = Element.as(e.getNativeEvent().getEventTarget());
return link.isOrHasChild(trgt);
}

This is solution I used. It's based on idea provided by #Amey. I had to modify his solution, because my HTML component may contain multiple anchors.
htmlText = new HTML();
htmlText.getElement().setId("HtmlTextArea");
...
private boolean anchorClicked(ClickEvent event) {
NodeList<Element> links = Document.get().getElementById("HtmlTextArea").getElementsByTagName("a");
Element target = Element.as(event.getNativeEvent().getEventTarget());
for (int i = 0; i < links.getLength(); i++) {
if (links.getItem(i).isOrHasChild(target)) {
return true;
}
}
return false;
}

Related

Swap the type of link depending on model object

I'm at complete loss how to proceed further:
I have panel with a DropDownChoice and a submit button next to it. Depending on the selected value of the DropDownChoice (Obtained upon the firing of a OnChangeAjaxBehavior attached to it, the submit button needs to either replace the whole panel with a different one, OR become an ExternalLink.
Currently, the code looks like that:
public class ReportSelectionPanel extends Panel {
protected OptionItem selectedOption ;
public ReportSelectionPanel(String id) {
super(id);
IModel<List<OptionItem>> choices = new AbstractReadOnlyModel() {
// Create a list of options to be displayed in the DropDownChoice
} ;
final IModel<OptionItem> optionModel =
new PropertyModel<OptionItem>(this,"selectedOption") ;
final DropDownChoice<OptionItem> options =
new DropDownChoice("selectChoice",optionModel,choices) ;
// I don't know what the button should be... Plain Button? A Link?
final Component button = ???
options.add( new OnChangeAjaxBehavior() {
protected void onUpdate(AjaxRequestTarget target) {
if ( selectedOption.getChild() == null ) {
// button becomes an ExternalLink.
// A new window will popup once button is clicked
} else {
// button becomes a Something, and upon clicking,
// this ReportSelectionPanel instance gets replaced by
// an new Panel instance, the type of which is
// selectedOption.getChild()
}
} ) ;
I'm really not quite sure what the commented lines should become to achieve the result. Any suggestions?
Thanks!
Eric
IMHO it's nicer to keep just one button and just react differently depending on the selected option:
final Component button = new AjaxButton("button") {
public void onClick(AjaxRequestTarget target) {
if (selectedOption.getChild() == null) {
PopupSettings popup = new PopupSettings();
popup.setTarget("'" + externalUrl + "'");
target.appendJavascript(popup.getPopupJavaScript());
} else {
ReportSelectionPanel.this.replaceWith(new ReportResultPanel("..."));
}
}
};
// not needed if options and button are inside a form
// options.add( new OnChangeAjaxBehavior() { } ) ;

Add ClickHandler to every button

I am trying to implement a click logging system in GWT, so I know where people are going around my app.
I want to be able to do this automatically with out adding the handler to every single Button?
I tried in a Composite class:
this.addDomHandler(new ClickHandler() {...}, ClickEvent.getType());
But the ClickEvent didn't give me any specifics on what had been clicked. The below didn't work as well.
NodeList<Element> elements = Document.get().getElementsByTagName("a");
EventListener el = new EventListener() {
#Override
public void onBrowserEvent(Event event) {
System.out.println(event.toString());
}
};
for (int i = 0; i < elements.getLength(); i++) {
Element e = elements.getItem(i);
com.google.gwt.user.client.Element castedElem = (com.google.gwt.user.client.Element) e;
DOM.sinkEvents(castedElem, Event.ONCLICK);
DOM.setEventListener(castedElem, el);
}
Any tips?
Take a look here:
Notice every click in a gwt application
This will be called on every click in your applilcation.
So, if you have this code:
Event.addNativePreviewHandler(new NativePreviewHandler() {
#Override
public void onPreviewNativeEvent(NativePreviewEvent event) {
if (event.getNativeEvent().getType().equals("click")) {
Element eventTarget = DOM.eventGetTarget(Event.as(event.getNativeEvent()));
// check if eventTarget is an a-tag
}
}
});
Any time the mouse is clicked, you will get an event. Exame the event to see, if an a-tag is clicked.
Hope that helps.

Handling click events from CellTable, how to handle them back to default dispatcher?

I have a CellTable with one custom column where I render it manually and put a FlowPanel with a bunch of HTMLPanel/Anchor/FlowPanel widgets, and among them is DecoratorPanel.
DecoratorPanel renders as a table, of course.
Rendering happens like this:
public class MyExpandableCell extends AbstractCell<String> {
...
#Override
public void render(com.google.gwt.cell.client.Cell.Context context, String value, SafeHtmlBuilder sb) {
if (value != null) {
FlowPanel fp = createDetailsFlowPanel(value);
sb.appendHtmlConstant(fp.getElement().getString());
}
}
Now, I have added a handler for click events to my main CellTable. In my handler I traverse the tree to find a first TR belonging to my CellTable, by checking if it contains "even row" or "odd row" CSS classes.
However, when click happens inside of the DecoratorPanel (which is inside of my cell table's TD), my handler also gets triggered, since the click belongs to the cell table area.
I can detect this my seeing that parent TR does not have CellTable CSS classes.
QUESTION: how can I return processing of such click events to the DecoratorPanel - where it really belongs to? As it is now, my DecoratorPanel does not expand and I think because my click handler intercepts and suppresses all clicks on the CellTable level.
table = setupMyCellTable(PAGE_SIZE, CLIENT_BUNDLE, myCellTableResources);
mydataprovider.addDataDisplay(table);
table.sinkEvents(Event.ONCLICK);
table.addDomHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent e) {
boolean isClick = "click".equals(e.getNativeEvent().getType());
if (isClick) {
Element originalElement = e.getNativeEvent().getEventTarget().cast();
Element element = originalElement;
String ctTrClassEven = CLIENT_BUNDLE.mainCss().cellTableEvenRow();
String ctTrClassEven = CLIENT_BUNDLE.mainCss().cellTableOddRow();
// Looking for closest parent TR which has one
// of the two row class names (for this cellTable):
while (element != null
&& !"tr".equalsIgnoreCase(element.getTagName())
&& !(element.getClassName().contains(ctTrClassEven) ||
element.getClassName().contains(ctTrClassEven))) {
element = element.getParentElement();
}
if (element != null) {
if (element.getClassName().contains(ctTrClassEven)
|| element.getClassName().contains(ctTrClassEven)) {
// Found cell table's TR. Set new TR height.
} else {
// Found TR from DecoratorPanel inside of the celltable's cell.
// Do nothing. But how do I make sure
// that decorator panel processes this click and expands?
return;
// Did not work: NS_ERROR_ILLEGAL_VALUE javascript exception.
// if (originalElement != null) {
// originalElement.dispatchEvent(e.getNativeEvent());
// }
}
} else {
debugStr.setText("(did not find tr)");
}
}
}
}, ClickEvent.getType());
Looks like a bug in GWT, triggered because decorator panel uses table to render itself:
http://code.google.com/p/google-web-toolkit/issues/detail?id=5714
(another example http://code.google.com/p/google-web-toolkit/issues/detail?id=6750)
Fix is expected to be shipped with GWT 2.5.

GWT - implement ClickHandler event on a row of a FlexTable

So my software is displaying a flextable (the data is grabbed and displayed from a database) with users allowing to click on a checkbox to select a data.
//users is the flextable object.
userCheck = new ClickHandler() {
public void onClick(ClickEvent event) {
CheckBox src = (CheckBox) event.getSource();
for (int i = 1, n = users.getRowCount(); i < n; i++) {
CheckBox box = (CheckBox) users.getWidget(i, 0);
if (!box.equals(src)) {
box.setValue(false, false);
}
}
removeUserButton.setEnabled(src.getValue());
editUserButton.setEnabled(src.getValue());
}
};
The code above works, but now I'm trying to implement an action where instead of the user clicking on the checkbox, I want the user to click on a row (which ever cell of the table) and make the whole row (where the user have selected) to be highlighted. So I implemented this code below but so far it doesn't work (like the mouseclick won't register, I've yet to implement the color stuff yet.. :( ). Any suggestions?
userRowCheck = new ClickHandler() {
public void onClick(ClickEvent event) {
Cell src = users.getCellForEvent(event);
int rowIndex = src.getRowIndex();
System.out.println("Cell Selected: userRowCheck Handler, rowIndex: " + rowIndex);
//This is just to check if this method is even called out. And well, it doesn't.
}
};
Thanks very much!!!!
If you added userRowCheck to the FlexTable : myFlexTable.addClickHandler(userRowCheck); it should work. Just make sure you test src for null, because if you didn't put a widget in a cell and the user clicks on that cell it returns null.

GWT CellList; programmatical scrolling between elements options

I would like to understand the options available to scrolling to a specific element in a CellList? Currently I have a 100 elements in the list and need to "jump" through the elements at the click of a button but can't seems to locate any methods on the celllist (or in the code) that provides this feature.
Any Ideas?
Many Thanks in advance,
Ian.
**EDIT
working code example below;
public class CellListTest implements EntryPoint {
private CellList<String> cellList;
private SingleSelectionModel<String> stringSingleSelectionModel;
/**
* This is the entry point method.
*/
public void onModuleLoad() {
cellList = new CellList<String>(new TextCell());
cellList.setRowData(buildStringList(200));
cellList.setKeyboardSelectionPolicy(HasKeyboardSelectionPolicy.KeyboardSelectionPolicy.BOUND_TO_SELECTION);
Button byTen = new Button("Jump Forward 10");
stringSingleSelectionModel = new SingleSelectionModel<String>();
cellList.setSelectionModel(stringSingleSelectionModel);
byTen.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
jumpForward(10);
}
});
byTen.setHeight("30px");
HTMLPanel htmlPanel = new HTMLPanel("");
VerticalPanel verticalPanel = new VerticalPanel();
cellList.setHeight("600px");
ScrollPanel scrollPanel = new ScrollPanel(cellList);
verticalPanel.add(byTen);
verticalPanel.add(scrollPanel);
htmlPanel.add(verticalPanel);
RootLayoutPanel.get().add(htmlPanel);
}
final Random random = new Random();
private List<String> buildStringList(int numberToCreate) {
final ArrayList<String> randomValues = new ArrayList<String>();
for (int i = 0; i < numberToCreate; i++) {
randomValues.add(String.valueOf(random.nextInt()));
}
return randomValues;
}
private int currentPosition = 0;
private void jumpForward(int toJump) {
Element rowElement = cellList.getRowElement(currentPosition += toJump);
rowElement.scrollIntoView();
}
}
I do not think CellList has a direct method for your purpose.
What you can do is use Element's scrollIntoView method. This method adjusts the scrollLeft and scrollTop properties of each scrollable element to ensure that the specified element is completely in view. In order to use that method you need to get the element containing the cell you want to show. One way to achive this is by using CellList public getRowElement(int indexOnPage).
I have not tryed it, but I believe the following code should work:
//Ensures cell 22 on page is shown
Element element = myCellList.getRowElement(22);
element.scrollIntoView();
Scrolling the element into view is one thing; changing the keyboard focus to a particular element is quite another. After much exploration, I have found that the best way to achieve that is to fire native events to simulate the user pressing keys.
private void hitKeyOnList(int keyCode) {
NativeEvent keyDownEvent = Document.get().createKeyDownEvent(
false, false, false, false, keyCode);
list.getElement().dispatchEvent(keyDownEvent);
}
In the above snippet, the list variable is a reference to a CellList.
So, to move the cursor to the end of the list, do this:
hitKeyOnList(KeyCodes.KEY_END);
to move the cursor to the next item down from the one that currently has keyboard focus:
hitKeyOnList(KeyCodes.KEY_DOWN);
This approach should work for all of these keys, which are handled by AbstractHasData:
KeyCodes.KEY_DOWN
KeyCodes.KEY_UP
KeyCodes.KEY_PAGEDOWN
KeyCodes.KEY_PAGEUP
KeyCodes.KEY_HOME
KeyCodes.KEY_END