I'm getting acquainted with GWTP. I tried to output a table, that would contain the JSON values, taken with a help of Piriti mappers. It's not a real project's code, it's just an attempt to understand GWTP, so this may be not the most beautiful solution (in fact, it's not one for sure). Here are the two presenters that are involved in this procedure:
The FirstPresenter (that uses ProductListPresenter, that is a widget, I'm not sure that widget should be used here, but, according to this conversation, widget may do the trick):
public class FirstPresenter extends
Presenter<FirstPresenter.MyView, FirstPresenter.MyProxy> {
public static final Object SLOT_RATE = new Object();
public static final Object SLOT_PRODUCT = new Object();
private IndirectProvider<ProductListPresenter> productListFactory;
public interface MyView extends View {
public Panel getListProductPanel();
}
#Inject ProductListPresenter productListPresenter;
#ProxyCodeSplit
#NameToken(NameTokens.first)
public interface MyProxy extends ProxyPlace<FirstPresenter> {
}
#Inject
public FirstPresenter(final EventBus eventBus, final MyView view,
final MyProxy proxy, Provider<ProductListPresenter> productListFactory) {
super(eventBus, view, proxy);
this.productListFactory = new StandardProvider<ProductListPresenter>(productListFactory);
}
#Override
protected void revealInParent() {
}
#Override
protected void onBind() {
super.onBind();
}
#Inject
PlaceManager placeManager;
#Override
protected void onReset() {
super.onReset();
setInSlot(SLOT_PRODUCT, null);
for (int i = 0; i < 2; i++) { //TODO: change hardcoded value
productListFactory.get(new AsyncCallback<ProductListPresenter>() {
#Override
public void onSuccess(ProductListPresenter result) {
addToSlot(SLOT_PRODUCT, result);
}
#Override
public void onFailure(Throwable caught) {
}
});
}
}
}
The ProductListPresenter:
public class ProductListPresenter extends
PresenterWidget<ProductListPresenter.MyView> {
#Inject ProductListPiritiJsonReader reader;
public interface MyView extends View {
public Label getNameLabel();
public Label getCompanyLabel();
public Label getSerialLabel();
public Label getPricesLabel();
}
#Inject
public ProductListPresenter(final EventBus eventBus, final MyView view) {
super(eventBus, view);
}
#Override
protected void onBind() {
super.onBind();
}
#Override
protected void onReset() {
super.onReset();
try {
RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, "/jsongwtproject/products.json");
rb.setCallback(new RequestCallback() {
#Override
public void onResponseReceived(Request request, Response response) {
ProductList productList = reader.read(response.getText());
for (Product product : productList.getProductList()) {
fetchDataFromServer();
}
}
#Override
public void onError(Request request, Throwable exception) {
Window.alert("Error occurred" + exception.getMessage());
}
});
rb.send();
}
catch (RequestException e) {
Window.alert("Error occurred" + e.getMessage());
}
}
//Takes the JSON string and uses showProductListData(String jsonString) method
public void fetchDataFromServer() {
try {
RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, "/jsongwtpproject/products.json");
rb.setCallback(new RequestCallback() {
#Override
public void onResponseReceived(Request request, Response response) {
showProductListData(response.getText());
}
#Override
public void onError(Request request, Throwable exception) {
Window.alert("Error occurred" + exception.getMessage());
}
});
rb.send();
}
catch (RequestException e) {
Window.alert("Error occurred" + e.getMessage());
}
}
//Uses Piriti mappers to take JSON values
private void showProductListData(String jsonString) {
ProductList productList = reader.read(jsonString);
for (Product product : productList.getProductList()) {
StringBuffer priceSb = new StringBuffer();
for (Double price : product.getPrices()) {
priceSb.append(price + ", ");
}
getView().getNameLabel().setText(product.getName());
getView().getCompanyLabel().setText(product.getCompany());
getView().getSerialLabel().setText(product.getSerialNumber());
getView().getPricesLabel().setText(priceSb.toString());
//break;
}
}
}
And the ProductListView.ui.xml:
<g:HTMLPanel>
<table border="1">
<tr>
<td><g:Label ui:field="nameLabel" /> </td>
<td><g:Label ui:field="companyLabel" /> </td>
<td><g:Label ui:field="serialLabel" /> </td>
<td><g:Label ui:field="pricesLabel" /> </td>
</tr>
</table>
</g:HTMLPanel>
Currrently there are two products in the JSON.
Here is what happens with this code: the first row with Product1 appears, then it changes to the first row that contains Product2's values, then again it contains Product1's values, then again Product2's, after that the second row with Product1 appears, then it changes to the second row that contains Product2's values, then again it contains Product1's values, then again Product2's.
So, there are two products and two rows, and in this code the values are changed twice, but in the end the table contains only Product2's values. If the break; is uncommented, Product1's values output twice in the first row, then in the second row, then the table contain only these Product1's values.
I do understand why that happens. But I haven't yet figured out how to make the correct output. It'd be great if someone could tell me how to do the correct output, or, well, provide an example (or would tell me which part, e.g. the widget usage, is terribly wrong).
The problem with your code is that you really don't have a real table in your ProductListView.ui.xml.
Of course if there were two records retrieved from the server, this part of the code is called twice:
getView().getNameLabel().setText(product.getName());
getView().getCompanyLabel().setText(product.getCompany());
getView().getSerialLabel().setText(product.getSerialNumber());
getView().getPricesLabel().setText(priceSb.toString());
the second call overwriting the value from the first call.
Points to improve your code:
You may want to read about CellTable for creating a real table view.
Do not use the PresenterWidget itself as data holder, instead create
a DTO that will be pass to the database and use this to retrieve the
data.
Related
Inside the onConsentFormLoaded method it says for “variable ‘form’ is accessed from within inner class must be declared final” but then when I declare it final it then shows a error “variable ‘form’ might not have been initialized” I tried declaring 'private static ConsentForm form" at the top of my class but then it gives a error saying about placing android context classes inside a static field will result in a memory leak so im not sure where to go from here ?
public static void settings()
{
final AppActivity activity = ((AppActivity) Cocos2dxHelper.getActivity());
activity.runOnUiThread(new Runnable() {
#Override
public void run() {
URL privacyUrl = null;
try {
// TODO: Replace with your app’s privacy policy URL.
privacyUrl = new URL(“https://privacy-policy”);
} catch (MalformedURLException e) {
e.printStackTrace();
}
ConsentForm form = new ConsentForm.Builder(getContext(), privacyUrl).withListener(new ConsentFormListener() {
#Override
public void onConsentFormLoaded() {
// Consent form loaded successfully.
Log.i(“consent”, “consent loaded”);
form.show();
}
#Override
public void onConsentFormOpened() {
// Consent form was displayed.
}
#Override
public void onConsentFormClosed(ConsentStatus consentStatus, Boolean userPrefersAdFree) {
}
#Override
public void onConsentFormError(String errorDescription) {
// Consent form error.
Log.i(“consent”, errorDescription);
}
})
.withPersonalizedAdsOption().withNonPersonalizedAdsOption().build();
form.load();
}
});
}
I am looking at a code that I have to work on. And basically I have to add a validation to a listener of a button.
The code has already multiple validations. They are kind of set in a cascade.
The listener of the buttons calls an asyncCallBack method that if everything is ok, on the onsuccess part of the method calls for the next one, an that one on the next one, until it reaches the end and goes to the next page. I am not a fan of this approach because it is kind of messy. What would the best way to do that using best practices.
An example of the code:
Button btnOK = new Button("Aceptar");
btnOK.addListener(Events.Select, new Listener<ButtonEvent>() {
public void handleEvent(ButtonEvent e) {
myService.getInfo1(1, txt, "N",
new AsyncCallback<List<InfoService>>() {
public void onFailure(Throwable caught) {
// goes back
return
}
public void onSuccess(
List<Object> result) {
// do some validation with the result
validation2();
}
}
}
}
public void validation2(){
myService.getDireccionCanalesElectronicos(id, new AsyncCallback<MyResult>() {
public void onSuccess(MyResult result) {
// do some validation with the result
validation3();
}
...
}
}
public void validation3(){
myService.getDireccionCanalesElectronicos(id, new AsyncCallback<MyResult>() {
public void onSuccess(MyResult result) {
// do some validation with the result
validation4();
}
...
}
}
Is there a better way of doing this, it seems messy and hard to follow. Adding another validation is complicated. It doesnt seem like a good practice.
Create 1 method in the servlet that calls all the validation methods and do just one call in the client ?
public void validation()
{
boolean ok = validation1();
if (ok) ok = validation2();
return validation;
}
Using mirco services is sometimes hard to deal with. As #Knarf mentioned, this is a way to go. But sometime you may want to handle the calls on the client side. Another one will be using this tiny framework: sema4g. It will help you to solve your problem.
A solution might look like that:
First create the sem4g commands:
private SeMa4gCommand createGetInfoCommand() {
return new AsyncCommand() {
// create callback
MethodCallbackProxy<List<InfoService>> proxy = new MethodCallbackProxy<List<InfoService>>(this) {
#Override
protected void onProxyFailure(Method method,
Throwable caught) {
// Enter here the code, that will
// be executed in case of failure
}
#Override
protected void onProxySuccess(Method method,
List<InfoService> response) {
// Enter here the code, that will
// be executed in case of success
}
};
#Override
public void execute() {
// That's the place for the server call ...
myService.getInfo1(1, txt, "N", proxy);
}
};
}
do that for all your calls;
private SeMa4gCommand createCommandGetDireccionCanalesElectronicos() {
return new AsyncCommand() {
// create callback
MethodCallbackProxy<MyResult> proxy = new MethodCallbackProxy<MyResult>(this) {
#Override
protected void onProxyFailure(Method method,
Throwable caught) {
// Enter here the code, that will
// be executed in case of failure
}
#Override
protected void onProxySuccess(Method method,
List<MyResult> response) {
// Enter here the code, that will
// be executed in case of success
}
};
#Override
public void execute() {
// That's the place for the server call ...
myService. getDireccionCanalesElectronicos(id, proxy);
}
};
}
Once you have done this for all your calls, create a sema4g context and run it:
try {
SeMa4g.builder()
.addInitCommand(new InitCommand() {
#Override
public void onStart() {
// Enter here your code, that
// should be executed when
// the context is started
})
.addFinalCommand(new FinalCommand() {
#Override
public void onSuccess() {
// Enter here the code, that will
// be executed in case the context
// ended without error
}
#Override
public void onFailure() {
// Enter here the code, that will
// be executed in case the context
// ended with an error
})
.add(createGetInfoCommand())
.add(createCommandGetDireccionCanalesElectronicos())
.build()
.run();
} catch (SeMa4gException e) {
// Ups, something wrong with the context ...
}
For more informations, read the documentation. If you have questions, feel free to ask: SeMa4g Gitter room.
Hope that helps.
What can I use instead of addAll() method in my adapter, I'm using realm version 2.0.1 and that method is deprecated, I'm trying to get all the data from the API, save it to my database and pass it to my adapter, I'm using like this:
public void getData(int page) {
if (GlobalModel.existeConexao()) {
Call<PedidosResponse> call = ((NavigationMain) getActivity()).apiService.getPedidos(GlobalModel.getToken(), GlobalModel.geEmpresaId(), page);
call.enqueue(new Callback<PedidosResponse>() {
#Override
public void onResponse(Call<PedidosResponse> call, Response<PedidosResponse> response) {
if (response.isSuccessful()) {
for (int i = 0; i < response.body().getPedidos().size(); i++) {
Pedidos mPedido = response.body().getPedidos().get(i);
int myInt = (mPedido.isProjecao()) ? 1 : 0;
if (!mRepositorio.checkIfExists(mPedido.getId())) {
mRepositorio.addPedido(mPedido.getId(), mPedido.getCliente_id(), mPedido.getData_hora(), mPedido.getData_pedido_cliente(), mPedido.getPrevisao_entrega(), mPedido.getFrete_tipo(), myInt, mPedido.getObservacao(), mPedido.getAliquota_projecao(), mPedido.getStatus(), mPedido.getPedido_cliente());
}
}
arraypedidos = mRepositorio.findAllPedidos();
if (mPedidosAdapter == null) {
mPedidosAdapter = new PedidosAdapter(getActivity(), arraypedidos);
listpedidos.setAdapter(mPedidosAdapter);
} else {
mPedidosAdapter.setData(arraypedidos);
}
}
}
#Override
public void onFailure(Call<PedidosResponse> call, Throwable t) {
if (t.getMessage() != null) {
Log.v("pedidos", t.getMessage());
}
}
});
} else {
Toast.makeText(getActivity(), "Verifique sua conexão", Toast.LENGTH_SHORT).show();
}
}
But when I run the app I get this message:
java.lang.UnsupportedOperationException: This method is not supported by RealmResults.
That's because RealmResults is just a set of pointers that satisfy the condition defined in the query. You can't manipulate it, nor should you if you just intend to show every element in your adapter.
In fact, Realm was explicitly designed to simplify the workflow of "downloading data on a background thread and saving the data in a database", and "showing the data downloaded on a background thread automatically on the UI thread".
This is what RealmChangeListener is for.
Simply put, all of this code is unnecessary:
arraypedidos = mRepositorio.findAllPedidos();
if (mPedidosAdapter == null) {
mPedidosAdapter = new PedidosAdapter(getActivity(), arraypedidos);
listpedidos.setAdapter(mPedidosAdapter);
} else {
mPedidosAdapter.setData(arraypedidos);
}
And could be replaced with this:
public class SomeActivity extends AppCompatActivity {
PedidosAdapter pedidosAdapter;
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.some_view);
pedidosAdapter = new PedidosAdapter(context, mRepositorio.findAllPedidos());
// set adapter, ...
}
}
And
public class PedidosAdapter extends RealmRecyclerViewAdapter<Pedidos, PedidosViewHolder> {
public PedidosAdapter(Context context, RealmResults<Pedidos> results) {
super(context, results, true);
}
// onBindViewHolder
// onCreateViewHolder
}
For this, use RealmRecyclerViewAdapter, unless you intend to handle the RealmChangeListener manually.
My problem is annoying. My server side is generating 12 random numbers (double here).
My Client side received the correct data but nothing is displayed in my Chart. That worked fine with hardcoded data in the store but not with a REST call.
The transfer between my server and my client is that :
[{"key":"key0","value":0.47222548599297787},{"key":"key1","value":0.6009173797369691},{"key":"key2","value":0.13880104282435624},{"key":"key3","value":0.01804674319345545},{"key":"key4","value":0.5547733564202956},{"key":"key5","value":0.8229999661308851},{"key":"key6","value":0.8959346004391032},{"key":"key7","value":0.6848052288628435},{"key":"key8","value":0.10222856671111813},{"key":"key9","value":0.6931371931409103},{"key":"key10","value":0.2994297934549003},{"key":"key11","value":0.47566752196381334}]
Here my simple class used for my test. I am a newbie with GXT 3
public void onModuleLoad() {
final ListStore<JSOModel> store;
final ContentPanel panel = new FramedPanel();
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, "/ws/DocumentService/v1/test");
builder.setHeader("Accept", "application/json");
HttpProxy proxy = new HttpProxy(builder);
final Loader<ListLoadConfig, ListLoadResult<JSOModel>> loader = new ListLoader<ListLoadConfig, ListLoadResult<JSOModel>>(proxy, new DataReader<ListLoadResult<JSOModel>, String>() {
#Override
public ListLoadResult<JSOModel> read(Object loadConfig, String data) {
List<JSOModel> jsoModels = new ArrayList<JSOModel>();
JsArray<JSOModel> jsoModelJsArray = JSOModel.arrayFromJson(data);
if(jsoModelJsArray != null) {
for(int i = 0; i < jsoModelJsArray.length(); i++) {
jsoModels.add(jsoModelJsArray.get(i));
}
}
return new ListLoadResultBean<JSOModel>(jsoModels);
}
});
store = new ListStore<JSOModel>(new ModelKeyProvider<JSOModel>() {
#Override
public String getKey(JSOModel item) {
return item.get("key");
}
});
loader.addLoadHandler(new LoadResultListStoreBinding<ListLoadConfig, JSOModel, ListLoadResult<JSOModel>>(store) {
#Override
public void onLoad(LoadEvent<ListLoadConfig, ListLoadResult<JSOModel>> event) {
ListLoadResult<JSOModel> loaded = event.getLoadResult();
if(loaded.getData() == null) {
store.replaceAll(new ArrayList<JSOModel>());
} else {
store.replaceAll(loaded.getData());
}
}
});
Chart<JSOModel> chart = new Chart<JSOModel>();
chart.setStore(store);
chart.setShadowChart(true);
NumericAxis<JSOModel> axis = new NumericAxis<JSOModel>();
axis.setPosition(Chart.Position.LEFT);
axis.addField(new ValueProvider<JSOModel, Number>() {
#Override
public Number getValue(JSOModel JSOModel) {
return JSOModel.getNumber("value");
}
#Override
public void setValue(JSOModel JSOModel, Number number) {
}
#Override
public String getPath() {
return "key";
}
});
axis.setTitleConfig(new TextSprite("Number of hits"));
axis.setWidth(50);
axis.setMinimum(0);
axis.setMaximum(100);
chart.addAxis(axis);
PathSprite odd = new PathSprite();
odd.setOpacity(1);
odd.setFill(new Color("#dff"));
odd.setStroke(new Color("#aaa"));
odd.setStrokeWidth(0.5);
axis.setGridOddConfig(odd);
CategoryAxis<JSOModel, String> horizontalAxis = new CategoryAxis<JSOModel, String>();
horizontalAxis.setPosition(Chart.Position.BOTTOM);
horizontalAxis.setField(new ValueProvider<JSOModel, String>() {
#Override
public String getValue(JSOModel JSOModel) {
return JSOModel.get("key");
}
#Override
public void setValue(JSOModel JSOModel, String s) {
}
#Override
public String getPath() {
return "key";
}
});
horizontalAxis.setTitleConfig(new TextSprite("month of year"));
chart.addAxis(horizontalAxis);
LineSeries<JSOModel> column = new LineSeries<JSOModel>();
column.setYAxisPosition(Chart.Position.LEFT);
column.setStroke(new RGB(148,174,10));
column.setHighlighting(true);
chart.addSeries(column);
axis.addField(column.getYField());
chart.addSeries(column);
chart.setHeight(100);
chart.setWidth(100);
Button b = new Button("ha");
b.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent clickEvent) {
loader.load();
}
});
RootPanel.get().add(b);
panel.setCollapsible(true);
panel.setHeadingText("Column Chart");
panel.setPixelSize(620, 500);
panel.setBodyBorder(true);
VerticalLayoutContainer layout = new VerticalLayoutContainer();
panel.add(layout);
chart.setLayoutData(new VerticalLayoutContainer.VerticalLayoutData(1,1));
layout.add(chart);
chart.setBackground(new Color("#dff"));
RootPanel.get().add(panel);
There are two ways to wire the chart into a store. One is to simply specify that the chart is using a store via setStore, as you have done:
chart.setStore(store);
When you do this, you must also inform the chart when it must redraw everything - you must call:
chart.redrawChart();
This call must be made shortly after the load is completed - consider doing it at the end of onLoad.
Why is this required? In some cases, developers want to make many changes to the store, one at a time, and if the chart automatically updated after each change, that would spawn many slow changes to the data model, and could end up looking strange. In a case like this, you would only call redrawChart() after all changes were complete.
There is another option however - instead of calling setStore, you can call bindStore, and ask the Chart to automatically update whenever any change occurs to the chart:
chart.bindStore(store);
In your case, this is likely the correct answer.
I am writing a GWT app that involves interacting with an external document in an iframe. As a proof of concept, I am trying to attach a click handler to a button.
The following works in javascript
var iframe = document.getElementById("rawJSIFrame");
var doc = iframe.contentDocument;
var body = doc.body;
var button = doc.getElementsByTagName("input").namedItem("submit");
button.onclick = function() {
alert("Clicked!");
};
Trying to do the equivalent in GWT, I did the following:
public void addClickHandlerToSubmitButton(String buttonElementName, ClickHandler clickHandler) {
IFrameElement iframe = IFrameElement.as(frame.getElement());
Document frameDocument = getIFrameDocument(iframe);
if (frameDocument != null) {
Element buttonElement = finder(frameDocument).tag("input").name(buttonElementName).findOne();
ElementWrapper wrapper = new ElementWrapper(buttonElement);
HandlerRegistration handlerRegistration = wrapper.addClickHandler(clickHandler);
}
}
private native Document getIFrameDocument(IFrameElement iframe)/*-{
return iframe.contentDocument;
}-*/;
The following is the ElementWrapper class:
public class ElementWrapper extends Widget implements HasClickHandlers {
public ElementWrapper(Element theElement) {
setElement(theElement);
}
public HandlerRegistration addClickHandler(ClickHandler handler) {
return addDomHandler(handler, ClickEvent.getType());
}
}
The code to find the button works fine but the actual click event handler is not getting invoked. Has anybody had a similar issue before, and how did you resolve it?
Thanks in advance,
Tin
Hilbrand is right about the problem being that the GWT method onAttach() was not called.
I implemented your original solution, adding the following method to ElementWrapper:
public void onAttach() {
super.onAttach();
}
And called added wrapper.onAttach() after the ElementWrapper is created. Works like a charm!
I expect the problem is that the GWT method onAttach() is not called when you use the wrapping as in your first example. You can try to use the static wrap method on the Button widget. Although to use this the input must be of type button. Or have a look at the implementation of the wrap method. Here is the modified code when using the wrap method:
Element buttonElement = finder(frameDocument).tag("input").name(buttonElementName).findOne();
Button button = Button.wrap(buttonElement);
HandlerRegistration handlerRegistration = button.addClickHandler(clickHandler);
After researching this further, I found that the iframe is irrelevant. The same behaviour doesn't work on a normal button on the host page.
I basically fixed it by using JSNI to replicate part of GWT's event handling mechanism. The following works:
Element buttonElement = DOM.getElementById("externalButton");
new CustomElementWrapper(buttonElement).addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
Window.alert("GWT hooked into button");
}
});
Where CustomElementWrapper is:
public class CustomElementWrapper extends Widget implements HasClickHandlers {
private ClickEventManager clickEventManager;
public CustomElementWrapper(Element theElement) {
setElement(theElement);
clickEventManager = new ClickEventManager(theElement);
}
public HandlerRegistration addClickHandler(ClickHandler handler) {
//The 'right' way of doing this would be the code below. However, this doesn't work
// A bug in GWT?
//
// return addDomHandler(handler, ClickEvent.getType());
return clickEventManager.registerClickHandler(handler);
}
void invokeClickHandler() {
clickEventManager.invokeClickHandler();
}
public boolean isClickHandlerRegistered() {
return clickEventManager.isClickHandlerRegistered();
}
}
Finally, the ClickEventManager, where the actual work happens is:
public class ClickEventManager {
private boolean clickHandlerRegistered = false;
private ClickHandler clickHandler;
private Element element;
public ClickEventManager(Element element) {
this.element = element;
}
public void invokeClickHandler() {
//This shouldn't really be null but we are bypassing GWT's native event mechanism
//so we can't create an event
clickHandler.onClick(null);
}
public boolean isClickHandlerRegistered() {
return clickHandlerRegistered;
}
HandlerRegistration registerClickHandler(ClickHandler handler) {
clickHandler = handler;
if (!clickHandlerRegistered) {
registerClickHandlerInJS(element);
clickHandlerRegistered = true;
}
return new HandlerRegistration() {
public void removeHandler() {
//For now, we don't support the removal of handlers
throw new UnsupportedOperationException();
}
};
}
private native void registerClickHandlerInJS(Element element)/*-{
element.__clickManager = this;
element.onclick
= function() {
var cm = this.__clickManager;
cm.#com.talktactics.agent2.client.widgets.ClickEventManager::invokeClickHandler()();
}
}-*/;
}
Personally, I hate this solution because I appear to be duplicating GWT's event handling and quite possibly introducing nasty javascript memory leaks. Any ideas on why my first post doesn't work (remembering that the iframe aspect is a red herring), would be appreciated.
Thanks,
Tin
You may find this helpful:
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.HasClickHandlers;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.ui.AbsolutePanel;
public class DirectPanel extends AbsolutePanel implements HasClickHandlers {
public DirectPanel(Element elem) {
super(elem.<com.google.gwt.user.client.Element> cast());
onAttach();
}
#Override
public HandlerRegistration addClickHandler(ClickHandler handler) {
return addDomHandler(handler, ClickEvent.getType());
}
}
You will then be able to make arbitrary containers into widget containers:
Element root = Document.get().getElementById("target");
DirectPanel p = new DirectPanel(root);
Button register = new Button("Register");
register.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
// ...
}
});
p.add(register);
And bind events to arbitrary elements:
Element root = Document.get().getElementById("target");
DirectPanel p = new DirectPanel(root);
p.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
// ...
}
});
Specifically in your case, try this:
IFrameElement frm = Document.get().createIFrameElement();
Document d = frm.getContentDocument();
NodeList<Element> inputs = d.getElementsByTagName("input");
InputElement target = null;
for(int i = 0; i < inputs.getLength(); ++i) {
Element e = inputs.getItem(0);
if (e.getNodeName().equals("submit")) {
target = InputElement.as(e);
break;
}
}
if (target != null) {
DirectPanel p = new DirectPanel(target);
p.addClickHandler(new ClickHandler() {
#Override
public void onClick(ClickEvent event) {
// TODO Auto-generated method stub
}
});
}
It's always mystified me that GWT makes doing this so difficult and poorly documented.
Instead of using iframes i suggest you simply make a http request from GWT via com.google.gwt.http.client.RequestBuilder. Like so:
private void getHtml(String url) {
RequestBuilder rb = new RequestBuilder(RequestBuilder.GET, url);
rb.setCallback(new RequestCallback() {
#Override
public void onResponseReceived(Request request, Response response) {
HTMLPanel html = new HTMLPanel(response.getText());
// Now you have a widget with the requested page
// thus you may do whatever you want with it.
}
#Override
public void onError(Request request, Throwable exception) {
Log.error("error " + exception);
}
});
try {
rb.send();
} catch (RequestException e) {
Log.error("error " + e);
}
}
You could use JSNI to reuse your JavaScript piece of code. Your javascript code would call a gwt method on an object that would throw it on behalf of the button in the iframe.
As to why GWT code does not work -- I guess that is because they use some layer on top of regular browser events that probably cannot span more than 1 frame. That's just a guess though. You could file this as a feature/bug request agains GWT team. If I am right your code looks just fine.
Please see my previous answer. A slight modification to your original solution will make it work.