javafx webview not supporting Ajax web features - java

I am trying to open webpage in webview using JavaFx . Its opening the web page properly but its not supporting the Ajax based web features like partial refreshing and new window popup handling
I am using the following code
final Group group= new Group();
Scene scene= new Scene(group);
fxpanel.setScene(scene);
WebView webview = new WebView ();
group.getChildren().add(webview);
eng= webview.getEngine();
eng.setJavaScriptEnabled(true);
try{
String url="http://www.abc.com";
eng.load(url);
eng.setCreatePopupHandler(
new Callback<PopupFeatures, WebEngine>() {
#Override
public WebEngine call(PopupFeatures config) {
smallView = new WebView();
smallView.setFontScale(0.8);
ChatPopup frm = new ChatPopup(smallView);
frm.setBounds(0,0,400,250);
frm.setVisible(true);
return smallView.getEngine();
}
});
}
catch(Exception ex){}
}

WebView does support Ajax.
Run the following app.
Click on the "Load data from server into div" button.
Page will be refreshed with data fetched from the server.
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.scene.Scene;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
public class WebViewAjax extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) {
WebView webView = new WebView();
webView.getEngine().load("http://www.jquerysample.com/#BasicAJAX");
final Scene scene = new Scene(webView);
stage.setScene(scene);
stage.show();
}
}
Aside on alerts
Note, in the sample page linked above there there are numerous examples for handling the json data. Some of the examples, e.g. the jquery $.get() example, output the result of the ajax call using a JavaScript alert().
If you want to see the alert data, you need to add an alert handler to the WebView engine. A basic alert handler such as below will just output the alert data to the output console:
webView.getEngine().setOnAlert(
stringWebEvent -> System.out.println(stringWebEvent.getData())
);
This is not really related to ajax calls, but without an alert handler, you may be confused you if you are trying to use an alert to debug or show data returned from a WebView ajax call.

If you need to make AJAX calls to cross-site services in the WebView, you can get around the security restrictions by making your AJAX calls through an up-call to Java. For example you could write or find a class with a ".request()" method that takes a JSObject as a parameter (the same JSObject format that jQuery's $.ajax() method takes, preferably), and inject a Java object that will expose that method:
WebView myWebView; //assuming it's initialized and points to an actual WebView
WebEngine engine = myWebView.getEngine();
JSObject window = null;
try{
window = (JSObject) engine.executeScript("window");
}catch (JSException e){
e.printStackTrace();
}
if (window != null){
window.setMember("myAjax", new AJAXProxyClass());
}
You can also directly override jQuery's ajax method with your own upcalling method, so that the difference is completely transparent to the javascript code, i.e.:
engine.executeScript("$.ajax = new function (o) { myAjax.request(o); };");
engine.executeScript("_$ = window.$");
This will replace jQuery's "$.ajax" call with the one from your Java object seamlessly. (I set the "_$" variable because jQuery will overwrite $ with it sometimes if it detects conflicts, returning jQuery to its original version.) This means that in most cases, any javascript code will not have to care whether it is running in your WebView or not.
A warning, though - jQuery's ajax call is rather complex, and this may break some of the jQuery ajax extensions if you're not careful about how you handle it. It should work just fine for the most common kinds of GET and POST calls, though.

Related

How to Listen to Vaadin component events in a JavaFX WebView?

I have created a form with Vaadin Flow (Vaadin version 21) with a save button. This form is then shown in a JavaFX WebView (Version 17.0.1) and now I want to listen to events in the form like save button from JavaFX, so I can update JavaFX parts of the application. I have seen examples like:
How to retrieve components from HTML webview in JavaFX
But they don't work since doc.getElementById("xxx") does not work at all and returns null. If I use the xpath method described there, I do get the button element, but then the adding the listener by calling the
((EventTarget) button).addEventListener("click", e -> doSomeAction(), false);
does not help. When I click the button the Vaadin listener works but the "javafx" listener does not get the event.
So my question is if there is a known solution for this Use Case?
You can invoke Java methods from JavaScript with upcalls. It works by assigning a JavaScript object to a Java one:
JSObject jso = (JSObject)webEngine.executeScript("window");
jso.setMember("java", new JavaObj());
Full example:
public class JavaObj {
public void myMethod() {
...
}
}
webEngine.setJavaScriptEnabled(true); // Obviously...
webEngine.getLoadWorker().stateProperty().addListener((observable, oldState, newState) -> {
if(newState == Worker.State.SUCCEEDED) {
JSObject jso = (JSObject)webEngine.executeScript("window");
jso.setMember("java", new JavaObj());
}
});
((EventTarget) button).addEventListener("click", e -> UI.getCurrent().getPage().executeJs("java.myMethod()"), false);
More about upcalls here.

GWT Form upload without refresh or forward

I'm wondering if what should be done for GWT com.google.gwt.user.client.ui.FileUpload
to upload to server without refresh or forwarding.
I have implemented the Form upload from this Java Doc, however it forwards the browser page to the target server URL.
How can I implement a form upload without forwarding?
Update:
final FormPanel form = new FormPanel();
form.setAction("/upload");
form.setEncoding(FormPanel.ENCODING_MULTIPART);
form.setMethod(FormPanel.METHOD_POST);
VerticalPanel panel = new VerticalPanel();
form.setWidget(panel);
final TextBox tb = new TextBox();
tb.setName("textBoxFormElement");
panel.add(tb);
// Create a FileUpload widget.
FileUpload upload = new FileUpload();
upload.setName("uploadFormElement");
panel.add(upload);
DOM.getElementById("form_panel").appendChild(panel.getElement());
Button submit = new Button("Submit");
panel.add(submit);
DOM.sinkEvents(submit.getElement(), Event.ONCLICK);
DOM.setEventListener(submit.getElement(), new EventListener(){
#Override
public void onBrowserEvent(Event event) {
if (event.getTypeInt() == Event.ONCLICK) {
form.submit();
return;
}
}});
form.addSubmitHandler(new FormPanel.SubmitHandler() {
public void onSubmit(SubmitEvent event) {
if (tb.getText().length() == 0) {
Window.alert("The text box must not be empty");
event.cancel();
}
}
});
form.addSubmitCompleteHandler(new FormPanel.SubmitCompleteHandler() {
public void onSubmitComplete(SubmitCompleteEvent event) {
Window.alert(event.getResults());
}
});
IMO you are making things more complex than they are.
I don't understand why, if you are using widgets, you are trying to manage the DOM by hand.
1.- Attach your form panel to the root panel using GWT way, otherwise you are going to break widget hierarchy.
RootPanel.get("form_panel").add(panel);
2.- Try not to use sink-events by hand, and use methods already present in widgets:
submit.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
form.submit();
}
});
FormPanel sets its target to a hidden <iframe> so you will never be redirected to another page.
BTW, I'd rather use gwtupload library which simplifies so much uploading code in GWT, and adds a lot of additional features.
I used before gwt-upload library.
You dont need to rediscover America.
Thanks for moxie group
gwt-upload-project page

How to show GWT Dialog Box at the end of UI initialization if there're async calls to server?

I initialize UI, adding all the elements to a VerticalPanel and at the end of initialization I check whether user password is expiring or not. If it is expiring I show a DialogBox with warning.
One of the element of this page is a TabPanel with a DataGrid and all the data is taken from server asynchronously. So the problem is that when Password Expiration dialog box appears it is behind DataGrid rows. When I inspect elements in Chrome I see that there're PopupGlassPanel -> DialogBox -> Table (with content). But the picture should be: Table (with content) -> PopupGlassPanel -> DialogBox.
But I cannot do anything with async calls to the server for retrieving data. Is it possible to control such things in order all data is retrieved from server and only after it my Dialo Box is shown?
Initialization method looks like:
public void initUI() {
final VerticalPanel mainPanel = new VerticalPanel();
Panel outer = new VerticalPanel();
DataHolder dataHolder = new DataHolder(); //this class contains DataGrid and makes async calls to server for data
outer.add(dataHolder.getContent());
tabPanel.add(outer, "Important Data", true);
mainPanel.add(tabPanel);
checkPasswordExpiration(getPwdExpiring());
RootPanel.get().add(mainPanel);
}
//...........
private void checkPasswordExpiration(int days) {
//...
//Show dialog box
//...
}
You need to call checkPasswordExpiration() after the async call returns. To do this you should get the async callback (onSuccess()) to fire an event for which you have a handler that will call checkPasswordExpiration(getPwdExpiring()); You can use EventBus to fire the event in and to add a handler.

Getting all windows using UISpec4J

I am trying to use UISpec4J in order to automate a Java Swing application. After adapter setup:
setAdapter(new MainClassAdapter(Main.class, new String[0]));
I am trying to obtain the main window:
Window mainWindow = getMainWindow();
Instead of a login dialog, I am getting a splash screen with logo of application. All my attempts to call this dialog manually have failed.
How can I get the list of opened dialogs/windows?
It looks like MainClassAdapter is not designed to handle a sequence of windows. However you can implement your own adapter that ignores the splash screen and returns the subsequent window. Here is a sample taken from UISpec4J forums:
setAdapter(new UISpecAdapter() {
public Window getMainWindow() {
final Window[] result = new Window[1];
WindowInterceptor.init(new MainClassTrigger(Main.class, new String[0]))
.processTransientWindow()
.process(new WindowHandler() {
public Trigger process(Window window) throws Exception {
result[0] = window;
return Trigger.DO_NOTHING;
}
})
.run();
return result[0];
}
});

GWT open page in a new tab

I am developing GWT application and I use
com.google.gwt.user.client.Window.open(pageUrl, "_blank", "");
to open new page. And it opens in a new tab when called, for example, directly after button click.
But I decided to do some validations on server before opening new page and placed the call to the mentioned above method to the
public void onSuccess(Object response) {
}
And it starts to open pages in new window instead of new tab (this is true only for Chrome, other browsers still open it in a new tab).
Can anybody help me?
I built a small example to illustrate the issue:
button.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
Window.open("http://www.google.com/", "_blank", "");
MySampleApplicationServiceAsync serviceAsync = GWT.create(MySampleApplicationService.class);
serviceAsync.getMessage("Hello, Server!", new AsyncCallback() {
public void onFailure(Throwable caught) {
Window.alert("ERROR");
}
public void onSuccess(Object result) {
Window.open("http://www.bing.com/", "_blank", "");
}
}
);
}
});
Firefox(3.6.8) opens both pages in new tabs.
Chrome(6.0) opens "google.com" in new tab and "bing.com" in new window
Opera(10.10) opens in new tabs.
IE(8.0) opens both in new Windows.
I marked igorbel 's answer as the only correct cos I haven't found any proper way to specify the same behaviour in all situations.
I used this code and it works for me in google chrome and mozilla firefox 3.6.8 browsers
If you want to open a page in new window you should write code as
Window.open("www.google.com","_blank","enabled");
If you want to open a page in new tab you should write code as
Window.open("www.google.com","_blank","");
I am not sure you are going to be able to control this the way you want. The problem is that browsers can decide when to open windows and when to open tabs. For example, firefox has the option: "Open new windows in new tabs instead". And don't forget the browsers that don't support tabs (yes, those do still exist).
Since this is such a problematic aspect of the user experience, my recommendation would be to reconsider your design. Is it really that important for you application to differentiate between opening a new tab and opening a new window?
This code works for me:
Before calling the Async method keep a reference to a new window with empty parameters.
At onSuccess() method set the URL of the window.
Button someButton = new Button("test");
SelectionListener<ButtonEvent> listener = new SelectionListener<ButtonEvent>()
{
public void componentSelected(ButtonEvent ce)
{
final JavaScriptObject window = newWindow("", "", "");
someService.doSomething(new AsyncCallback()
{
public void onSuccess(Object o)
{
setWindowTarget(window, "http://www.google.com/");
}
});
}
}
someButton.addSelectionListener(listener);
private static native JavaScriptObject newWindow(String url, String name, String features)/*-{
var window = $wnd.open(url, name, features);
return window;
}-*/;
private static native void setWindowTarget(JavaScriptObject window, String target)/*-{
window.location = target;
}-*/;
Found at:
http://groups.google.com/group/google-web-toolkit/browse_thread/thread/574b3b828271ba17
Interesting thing,
chrome will open page in new tab in case if you put window.open(...) instruction into the body of the click handler implementation.
For example:
Button someButton = new Button("test",
new ClickHandler() {
public void onClick(ClickEvent event) {
Window.open(...);
}
});
And a page will be opened in the separate window in case if I will include any Async. request into the mentioned code:
Button someButton = new Button("test",
new ClickHandler() {
public void onClick(ClickEvent event) {
someService.doSomething(new AsyncCallback() {
void onSuccess(Object o) {
Window.open(...);
}
...
});
}
});
The way Chrome looks at it, calling Window.open() is like trying to open a pop-up window in the user's face. That's frowned upon and will trigger the built-in pop-up blocker. Following a link, according to Chrome, should be the result of a user clicking on a good old anchor tag with an href attribute. But here lies the answer you're looking for: you can show a link to the user and change the link target on the fly. That would qualify as a 'proper' link in Chrome's world.
This code works for me:
public static native String getURL(String url)/*-{
return $wnd.open(url,
'target=_blank')
}-*/;

Categories