jQuery include with JavaScript components

This may be more of a general JS import question but I’m specifically asking about jQuery here. I’m really enjoying the new JS component features in Vaadin 7 and I’d like to branch out into integrating some jQuery components but I’m not clear of the best practice for including jQuery.

Most of the examples show jQuery being included with the @JavaScript attribute on the component, such as in
this example
. This is simple and works well but my understanding is that this would cause Vaadin to include the script every time the component is loaded. This not only seems like performance problem, but a possible conflict issue with jQuery initializing multiple times on the same page.

Another option would be to include jQuery in the bootstrap of the application, which is also relatively easy with Vaadin, but then it is harder to package up components for distribution because you have to instruct users of the component to implement a custom bootstrap to include the right version of jQuery.

Anyone have experience with this? What’s the best way to include external JS dependencies, namely jQuery?

-mike

Hi, you could have a look at how e.g. Vaadin Chart is loading the highchart javascript resources by injecting them on the client side. It also checks if there’s already jQuery loaded in the window before injecting jQuery script.

http://dev.vaadin.com/browser/svn/addons/vaadin-charts/addon/src/main/java/com/vaadin/addon/charts/client/HighchartsScriptLoader.java

One downside with this is that the javascript is always included in the widgetset sent to the client, regardless if it’s used or not, but at least this way the distributed package is quite neat and does not require too much effort from the developer using the component. Other than compiling the widgetset of course.

I can share my experience with the Sencha Ext JS integration.

The requirement was to integrate a component based on the Sencha Ext JS 4. The way to use @JavaScript annotation didn’t work for EXT JS component, because it participated in the browser page load mechanism to initialize everything before the page comes out. So I was forced to move everything to the Vaadin bootstrap code.

Additional issue was with the @StyleSheet annotation. If this style sheet contains some image references they cannot be loaded, because vaadin tries to load them below some /VAADIN/* directory which does not exists and which I could not map via web.xml. Here some hints from the JavaDoc would be very helpful.

So I decided to load CSS dynamically (JS Code) once the component is shown.

I figured out, that there are two places you can use for that (see the snippet below). With (1) you can do it for all components on the page, with (2) only in the scope of the component instance.


//>> (1)
org_test_MyComponent = function() {
	
  this.onStateChange = function() {
     //>>(2)
  }
}

Don’t know if that helps in case of JQuery…

The problem with the Vaadin Chart approach is that it requires a GWT component which requires recompiling the widget set. One of the nice things about pure JS components is that they don’t require widget set compilation.

That being said, I suppose the same approach could be used where the server side JavaScriptComponent passes a bunch of ClassPathResources in the state and the client side dynamically generates script tags as needed after doing some checks of the current document state.

I’ll give it a try that way and see if I can avoid jQuery collisions.

I’ve been playing around with this a bit more and I’m still not finding a great solution. I am dynamically generating a script tag in my JS component but of course the browser then loads the script asynchronously which means the script features (in this case jQuery) are not available to my component until it finishes loading.

I noticed that in Vaadin 7.1 there is a the ResourceLoader which seems to do what I want, generate a script tag and then wait for the tag to load before continuing on. There are some JS libraries such as require.js that do the same, but I hate to introduce yet another dependency.

So, I still don’t have a solution for conditionally including external JS files in a JS component. With jQuery, I want to test if jQuery is already initialized by some other component before including my own. Therefore I can’t use the @JavaScript annotation which would blindly include jQuery in the page.

The best I have come up with so far is to generate a script tag when needed and then hook the onreadystatechange with a callback. This seems a bit problematic because Vaadin could start calling my onStateChange or other methods before my component has initialized.

Maybe the client side JavaScriptConnector should expose a loadScript method that internally uses the ResourceLoader to make this process easier? Does anyone else have a suggestion for dynamically including external scripts in a pure JS component?

Hello,

I’m the developer behind most of the JavaScript integration features that were introduced in Vaadin 7. I’m more than happy to hear ideas about how to improve the JavaScript support since I believe it’s very useful to be able to easily integrate more or less any js widget into Vaadin.

@JavaScript only loads every uniquely named script file once. This means that if you have two different components both using @JavaScript(“jqyery.js”) and include multiple instances of each component in a view, then jquery.js would still only be loaded once. This is kind of a hack because the framework assumes that both jquery.js files are identical even though they might be separate files from different parts of the file tree - it just uses the one that it happens to stumble upon first. If you on the other hand have one component with @JavaScript(“jquery-1.7.js”) and another with @JavaScript(“jqery-1.8.js”), then both files would indeed be loaded (although each file is still only loaded once). You can also use an absolute URL, e.g. @JavaScript(“http://code.jquery.com/jquery-1.7.js”). In this case the full absolute URL is used when looking for duplicates, meaning that @JavaScript(“jquery-1.7.js”) would be seen as a separate file that would also be loaded. I have created
#12200
for updating the documentation to be clearer about how this works.

Excellent idea! I’ve created
#12201
for implementing this.

Vaadin 7.1 introduced a new way of bundling and loading custom stylesheets in a way that makes it easy to also reference e.g. images. Please see
https://vaadin.com/wiki/-/wiki/Main/Packaging%20SCSS%20or%20CSS%20in%20an%20add-on
for information about this.

A bit of offtopic :slight_smile:
Would be nice to see ticket
#10946
resolved to make serious integrations possible without widgetset compiling.

Leif, thanks for the reply and opening the tickets.

As far as the ResourceLoader only including a script once, that’s a nice feature but I don’t think it can be relied on for 2 reasons:

  1. As you pointed out, it only does this detection based on the script URL. I can already see this being a problem because different components are very likely to include scripts from two different places (like jQuery from the application itself, the Google cache, or the jQuery page) or use different versions. In fact, the Vaadin Charts plugin (as mentioned above) doesn’t even use a script tag but does an eval on the script content that is bundled in the add-on so ResourceLoader doesn’t even know about it. We really need a way to perform a logical check of the DOM/window before including scripts.

  2. From what I can see, when the ResourceLoader is initialized it looks at the script tags in the head but then it doesn’t track scripts dynamically added by the component. So if a component adds a script tag dynamically, the ResourceLoader won’t prevent conflicts. That brings us to #12201.

It would be nice to hear from some other JS component developers to see if 12201 would meet their needs. One approach would be to do something like this where my components function isn’t called until all the resources are loaded:


if (!window.jQuery) {
  loadResource("http://.../jQuery.js");
}

org_mpilone_mycomponent = function() {
  ...
}

I’m also not sure how/if the ResourceLoader tracks script tags that are added dynamically by included scripts. For example, if I include script A and it generates script tags for B and C, will ResourceLoader see those and block until B and C are loaded as well? I don’t think it does. I’m not sure that this is ever an issue but unfortunately it seems like every JS library out there has a different way of notifying about initialization. Some want the DOM to be constructed and they just modify it on load, others generate a ready callback, and others assume they will be loaded with the initial document and just expect you to use onload. Getting all of these mechanisms to play nicely with Vaadin’s DOM construction and onStateChange is interesting.

-mike

This looks like an interesting discussion! I might have some vague ideas of what could be done, but I’ll try to avoid presenting them right away to keep the discussion open for potentially better approaches. I’ll instead just bombard you with tricky questions!

Yes, @JavaScript should be good enough for your own scripts, but it does not go all the way for loading libraries. Just a simple check before including a script might not be enough since there might still be race conditions: add-on A sees that jQuery is not loaded so it injects a script tag, then add-on B comes along before the script has been fetched and starts loading jQuery from a different URL. Another big problem is the use of different versions. What happens if e.g. jQuery 1.9 is already loaded, but your component needs jQuery 1.10? This might still be possible to solve considering jQuery’s noConflict functionality, but all javascript frameworks are not so well-behaved.

When a component requests e.g. jQuery 1.9, Vaadin does not generally have any way of knowing whether any component requiring jQuery 1.10 might eventually be added to the page. This would imply that the application developer might need some way of overriding the requested jQuery 1.9 to instead load jQuery 1.10.

It gets even worse with a library that is not 100% backwards compatible and doesn’t support having multiple versions loaded at the same time. I guess nothing can be done about such situations, although it would at least be good to present a sensible error message to the application developer. It is of course also possible that even though some framework is generally not backwards compatible, a component might still only use functionality that has not been changed in newer versions.

I can’t see how the framework could know that it should postpone invoking org_mpilone_mycomponent if that structure is used. It could of course be implemented so that Vaadin does not in any way interfere but instead org_mpilone_mycomponent internally defers actions that depend on jQuery. This would however force each javascript component to implement more or less the same logic as every other component.

ResourceLoader does not currently detect B and C, but it could be enhanced to always scan the DOM for script tags before including a new one. The performance impact of this should not be too awful compared to the used for actually fetching and parsing the script, especially since scanning is not needed if info about the requested script has already been cached from a previous scan.

Hello , I am having the same issue of bad performance when loading many javascript files using the @javascript annotation in my AbstractJavaScriptComponent. Did you reach any solution to load the Js files only once for the whole application not whean each UI loads or find another way to load the Js files with better performance?

Hi all,

it’s not the most current thread - but obviously still very much relevant. We discussed it this week at the Hamburg Vaadin meetup - seems like it’s still a challenge for many.

However, we came up with one way that MIGHT work but would require some tweaking in Vaadin: how about @Javascript getting an attribute “noLoadSuperclass” (default false), i.e. disregard all @JavaScript annotations that are in the superclass of the current class and further above in the tree. This way, users of JavaScript extensions (including addons from the directory) would be able to overwrite the Component / Extension class provided - and specify the JQuery (or other lib) they deem the best fit.

In the meantime, forking an addon is a way forward (and so would a servlet filter probably be as a very last resort - to decide which files it lets pass). But I think a nicer way forward would be a big plus anyway.

So: maybe you find this useful - any thoughs, comments, ideas are very welcome

Best
Sebastian

I’m glad there is still interest in this. I ran into the issue again when I had two different components including the same JS file from two different locations (one used a subdirectory for JS resources and one didn’t). Luckly both components were under my control and I could fix it but if they were published add-ons the users would be out of luck.

I reopened ticket https://dev.vaadin.com/ticket/12201 with some more comments. Hopefully we can get some kind of a solution together.

You’re idea sounds good for a single component heirarchy but it doesn’t solve the case of two components importing the same JS file from different locations. Unfortunately I think that is probably a bigger JS issue and there are some module systems being developed (both official and unofficial) that might help. It might also help to have a way for a component to detect what is alread loaded and be able to dynamically define imports. Maybe the @JavaScript tag needs some kind of JS script “check” code that is wrapped around the include.

Interesting and challenging problem but I haven’t had much time to focus on it lately.

-mike

Maybe this could be a solution:

  • Create a maven artifact for each java script eg. com.vaadin:js.jquery which includes an (empty) Interface JQueryComponent with the @JavaScript annotation and the js files
  • The components with neet jQuery implement this interface
  • The add-on as a dependency to com.vaadin:js.jquery
  • Maven or Ivy resolve the different versions for us used in different add-ons

This could of course only work, if someone takes care of the js.*.jars - but it would work without creating a new js loading mechanisim in vaadin.

What do you think about it?

Daniel

That’s a great idea! Unfortunately, webjars alone won’t do it (b/c it contains the version number in the file name). But maybe it can be part of such a solution anyway.

Too bad the idea with the interfaces does not work. Vaadin (com.vaadin.server.communication.UidlWriter:294) simply jjust uses Class.getAnnotation, so there is no traversation in the class hieracy.

So I got a working prototye. Please have a look here
https://github.com/vegetweb/vwebjars
and comment on the approach.

Best wishes

Daniel

Hi Daniel,
this is an amazing solution! Did you consider adding it to the vaadin directoy? (I could help w/ it if you want)
Thanks
Sebastian

Hi,

I had completely missed this thread earlier. The “hack” to use app:// protocol is a nice catch, that didn’t come to my mind originally. This way no new annotation is needed.

Daniel, how about pushing a modified solution directly to the core and VaadinServlet? We could also introduce webjar:// protocol that would be interpreted by the Vaadin client side. Then the URLs would be bit prettier and it might also be possible to support to portlets pretty easily as well.

cheers,
matti

Hi,

I would like to see it in VaadinServlet - I will create a ticket next week.
@Matti, @Sebastion: Can one of you guide me when it comes around to intorduce the webjar:// protocol? My knowlage about the Vaadin client side stuff is quite near to zero.

If its not going to be accepted, I would create an addon out of it.

Best wishes

Daniel

Hi,

i’ve suggested creating a pull request in the vaadin trac:
https://dev.vaadin.com/ticket/12201#comment:5

Best wishes

Daniel