GWT: deferred loading of external JS resources - gwt

I have a widget depending on some external JS files, and I'd like to lazy load all these external resources. I've already used code splitting to lazy load the GWT code that concerns the widget, but the JS files defined in the gwt.xml, using the script tag, are loaded anyway, which is not desirable.
Is there a standard GWT way of loading these external resources on demand? I can do it myself using raw JS, but I'd rather not spend time on this too.

I think you'll want to take a look at the com.google.gwt.core.client.ScriptInjector class. From the javadocs:
Dynamically create a script tag and attach it to the DOM.
...
Usage with script loaded as URL:
ScriptInjector.fromUrl("http://example.com/foo.js").setCallback(
new Callback<Void, Exception>() {
public void onFailure(Exception reason) {
Window.alert("Script load failed.");
}
public void onSuccess(Void result) {
Window.alert("Script load success.");
}
}).inject();
This code can of course be invoked from within your split points, or indeed anywhere in your code.

ScriptInjector is quite portable. It doesn't have any external dependencies, so you should be able to backport it into your 2.3 application without much problem.

Related

how to call a GWT module entry point class?

l split my GWT code in different different modules like
PrintPermit.gwt.xml
EmployeeResponse.gwt.xml
Rejected.gwt.xml
and every module has its own entry point class
in my HTML host page I am calling script like
ae.init.EmployeeResponse.nocache.js
I have a menu like
Print Application
Reject Application
New application
whenever user will click on new application default new application will open
as I declare EmployeeResponse.nocache.js statically in my HTML host page.
now I want to call other modules on click button print and reject button
how can i call nocache js for print and reject modules. is there any way to dynamic call.
please help me guys.
Here's how I've done it in the past:
First of all, in the module you want to export, you need to make sure that the code you're going to export doesn't end up obfuscated. This can be accomplished with the liberal use of #JsType; this is the new way of exporting JS, available in GWT 2.8 (as opposed to JSNI).
Your module's entry point onModuleLoad can be empty; it doesn't need to do anything.
Include your JS in the HTML you want to use (maybe the same page as your "main" module)
Check JSInterop documentation (perhaps the one available here) on how you can use native JS in your GWT app (because now, your GWT module became native JS). Import the classes via JSInterop from your library, and use them.
Please be aware of the async nature of the GWT JS loading; your library will be loading in an async manner, just like any JS application (and therefore, it won't be available immediately when your page loads). To overcome this, I've placed a call to a native JS function in my library's onModuleLoad function (i.e. to make sure you notify any potential listeners that the code has loaded; because when onModuleLoad runs, the code surely loaded).
There is a example of an InterAppEventBus:
https://github.com/sambathl/interapp-eventbus
which shows the communication between two GWT applications.
I have adopted it and replaced JSNI with Elemental2 and WebStorage:
https://github.com/FrankHossfeld/InterAppEventBus
Hope that helps.
You can achieve this through separate Html file for each module.
So first of all create separate html for each application e.g. PrintPermit.html and specify corresponding nocache.js in each html.
then on your buttons in menu, add click handlers and in each on click load a corresponding html through Window.open()
e.g. for PrintPermit,
printPermitButton.addClickHandler(new ClickHandler{
#Override
public void onClick(ClickEvent arg0) {
String s = GWT.getHostPageBaseURL() + "PrintPermit.html";
Window.open(s, "PrintPermit", "");
}
});
Please note the window.open will open in new tab in browser, you can also use gwt iframe to open html in same browser page.
Each module will have corresponding nocache.js and will be loaded through html using Window.open()

Strategies for implementing a user specific - custom content EntryPoint in GWT

We have developed a dashboard in GWT that contains some custom widgets for displaying customer's data in various graphical forms. We now want to move to a more custom / user specific approach where each customer who logs into the dashboard can see a different perspective of the dashboard. Some widgets will be available for some users, for some others not and with different initialization parameters.
We are trying to find an efficient strategy to do this. A potential solution would be to have the client side request all this information during EntryPoint loading and then use that incoming configuration to build itself and make further requests for the data. A more efficient solution would also allow downloading to the browser only those widgets relevant to the user.
Does GWT have any design pattern for this scenario? If not, what would a good high level solution be for this case?
Thank you.
Yes there is atleast one mechanism that can be useful to achieve your requirements
Code Splitting :- The idea is to split the particular section of code and download it in independent async call. Using this approach you can reduce the size of primary javascript file making initial page load faster. This approach allow GWT to skip inclusion of all those widgets which have references only inside GWT.runAsync() method in primary js file. Such widgets and code gets downloaded when application runs that code in independent call. you can use this approach and avoid download of additional dashboard charts based on some conditions like user type and so. Here is sample code from GWT reference website
public class Hello implements EntryPoint {
public void onModuleLoad() {
Button b = new Button("Click me", new ClickHandler() {
public void onClick(ClickEvent event) {
GWT.runAsync(new RunAsyncCallback() {
public void onFailure(Throwable caught) {
Window.alert("Code download failed");
}
public void onSuccess() {
Window.alert("Hello, AJAX");
}
});
}
});
RootPanel.get().add(b);
}
}

Calling functions from .js file from GWT

I have a page slider type widget from a third party that is pretty standard in that it runs some jquery code (inside an init function provided by the widget) on the HTML in your document looking for the classes of interest.
My issue is that I am using GWT so my HTML is being generated and then inserted in to the page by the GWT javascript file. The HTML being inserted has all the proper tags for the slider widget to work. So after it is added (to the DOM) I need to run the init js code so that the slider works as expected. I created a JSNI to make the call:
private native void initSlider() /*-{
Index.initLayerSlider();
}-*/;
I load Index in the head of the HTML file before the nocache.js GWT file, but I get the javascript console error when I load the site: Uncaught ReferenceError: Index is not defined so I tried
private native void initSlider() /*-{
jQuery(document).ready(function() {
Index.initLayerSlider();
});
}-*/;
and jQuery is also loaded in the head of my HTML file but I get the similar error: Uncaught ReferenceError: jQuery is not defined
How can I call this function from an external .js file from GWT? I guess I need to do something to make sure the browser has loaded and ran the .js file before GWT runs, or have GWT pause when it gets there until the .js file has been loaded. Or maybe I should approach this problem differently?
I would prefer to not have to make any modifications to the third party widget, but would be willing to make small ones if needed/possible. It has been obfuscated so it is very difficult to read.
call javascript from GWT need start with $wnd
see detail http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJSNI.html
private native void initSlider() /*-{
$wnd.Index.initLayerSlider();
}-*/;

How to include 3rd party JavaScript libraries in a reusable gwt library/widget?

I'm trying to get my feet wet with GWT to see if migrating will work out. I usually try the more difficult parts first to make sure I can finish the project. The most difficult part of my project(s) is referencing 3rd party JS libs. In this example I'm trying to use PubNub as much of our platform uses it.
What I'd like to do is create a reusable object that can be used in other GWT projects in need of PubNub. I've got a simple little test running successfully (ie, I've got the basics of JNSI working), but my question is -> where do I put the reference to the 3rd party script in order to create the library/module properly?
Right now I just put the reference to the external scripts in the HTML page in the project, but I'm pretty sure this is incorrect from a reusability perspective, as this lib would be used in other projects, each of which would have their own base HTML page.
I tried putting the reference in the gwt.xml file, but this seems to lose the references (ie my test project no longer works as it did when the scripts were in the HTML page)
Do you have any tips on how to include 3rd party libraries in a reusable GWT library/widget?
Here you have an example using client bundles and script injector, you can use either synchronous loading or asynchronous.
When using sync the external js content will be embedded in the application, otherwise it will be include in a different fragment which will be got with an ajax request.
You can put your api in any server and load it with the ScriptInjector.
public class Example {
public static interface MyApiJs extends ClientBundle {
MyApiJs INSTANCE = GWT.create(MyApiJs.class);
#Source("my_api.js")
TextResource sync();
#Source("my_api.js") // Should be in the same domain or configure CORS
ExternalTextResource async();
}
public void loadSync() {
String js = MyApiJs.INSTANCE.sync().getText();
ScriptInjector.fromString(js).inject();
}
public void loadAsync() throws ResourceException {
MyApiJs.INSTANCE.async().getText(new ResourceCallback<TextResource>() {
public void onSuccess(TextResource r) {
String js = r.getText();
ScriptInjector.fromString(js).inject();
}
public void onError(ResourceException e) {
}
});
}
public void loadFromExternalUrl() {
ScriptInjector.fromUrl("http://.../my_api.js").inject();
}
}
[EDITED]
A better approach is to use a new feature in gwtquery 1.4.0 named JsniBundle. We introduced this feature during the GWT.create conferences at San Francisco and Frankfurt.
With this approach you can insert any external javascript (placed in your source tree or hosted in an external host) as a JSNI block. It has many benefits:
Take advantage of GWT jsni validators, obfuscators and optimizers.
Get rid of any jsni java method when the application does not use it.
The syntax is actually easy:
public interface JQueryBundle extends JsniBundle {
#LibrarySource("http://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js")
public void initJQuery();
}
JQueryBundle jQuery = GWT.create(JQueryBundle.class);
jQuery.initJQuery();

GWT inject script element into the html file

On my gwt project. i have a script that call the dictionary:
<script type="text/javascript" src=conf/iw_dictionary.js></script>
instead of writing this script element in the html file. i want to inject it into the html from the entry point, on the module load.
how can i do it?
Use com.google.gwt.core.client.ScriptInjector, since it was created specifically for stuff like this
ScriptInjector.fromUrl("conf/iw_dictionary.js").setCallback(
new Callback<Void, Exception>() {
public void onFailure(Exception reason) {
Window.alert("Script load failed.");
}
public void onSuccess(Void result) {
Window.alert("Script load success.");
}
}).inject();
Basically you inject the script element in your onModuleLoad():
Element head = Document.get().getElementsByTagName("head").getItem(0);
ScriptElement sce = Document.get().createScriptElement();
sce.setType("text/javascript");
sce.setSrc("conf/iw_dictionary.js");
head.appendChild(sce);
The browser will automatically load it as soon as it's injected.
You could simply add a <script> element in your *.gwt.xml file.
<script src='conf/iw_dictionary.js' />
onModuleLoad will only be called once the script is loaded (as if you had it in your html page).
The answers form jusio, Dom and Thomas Broyer are all valid here. In my particular case, I was looking to inject a series of polyfill scripts into GWT for some IE8 support we needed when running native JS code. The polyfill scripts needed to be available to the GWT iframe's window context - NOT the host page. To do that, using ScriptInjector was the correct approach as it attaches the script at that level. You can make ScriptInjector install the scripts to a host window by using setWindow(TOP_WINDOW). Adding scripts with the <script> tag in my *.gwt.xml file seemed to be attaching to the host window as did using #Dom's approach.