Again: dealing with 3rd-party JS modules in Plone 5 is a major pain

Hi there,

<disclaimer>
Frusted by the state of JS handling in Plone 5 and the lack of JS related resources for porting Plone 4 add-ons to Plone 5.
</disclaimer>

I tried today to port zopyx.plone.persistentlogger to Plone 5. In order to avoid any trouble with the legacy bundle (as it happened pretty often in the past with other add-ons) I moved all resources and all JS code into a single page template (no jsregistry.xml involved).

The same code is working with Plone 4.3, it is breaking with Plone 5.0:

Why is this happening? Why isn't is possible to support 3rd-party JS modules out-of-the-box in a reasonable way? I want to stress out again: no resource registries involved here. The complete JS situation in Plone 5 is a huge, huge mess. None out of six add-ons that I tried to port from Plone 4.3 to Plone 5 with custom JS code is actually working.

JS handling in Plone 5 is broken. If not, please explain why it is not and please explain how to deal with the situation.

-aj

Could it be that it the js loads before its dependecies, try loading the js in the body (skip the fill slot).

It seems so, but aren't Plone JS now loaded at the end of the body?
If this is true: putting it into the body will not change anything... and so we need a javasciprt_footer_slot?

javascript_head_slot is still a valuable friend, but it seems that is not reliable as before because is not the last JS loaded now (and Diazo is not a solution in this case, we can't rely on a theme rule for fixing an add-on issue).

Hello Andreas,

You must use require to load your dependencies.

basically something like this should work:

require([
    'jQuery',
    './++resource++zopyx.plone.persistentlogger/DataTables/media/js/jquery.dataTables.js',
    './++resource++zopyx.plone.persistentlogger/DataTables/extensions/FixedHeader/js/dataTables.fixedHeader.min.js',
    './++resource++zopyx.plone.persistentlogger/DataTables/extensions/TableTools/js/dataTables.tableTools.min.js'
], function($, dt, plugin1, plugin2) {
     // your code here
     $.DataTable(blablabla);
});

require can load pre-declared resources (like jQuery in this example, which is declared by the Plone JS bundle, and we could have add pat-tinymce or anything else from Mockup) or resources specified by their URL (you might use CDN urls by the way if you do not want to host Datatables and its plugins in your site), and those resources are passed as parameters to a callback function able to use them, here I just use $, the 3 others (dt, plugin1, plugin2) are not useful as they are used through jquery, and they could be removed from your callback:

], function($) {
         // your code here
         $.DataTable(blablabla);
 });
4 Likes

Well, the problem is that DataTables behaves differently if it's loaded when requirejs is present (as it is in Plone 5) than when it is not. If requirejs is present it registers itself as a requirejs module rather than initializing itself immediately.

Eric's suggestion will work when requirejs is present, but doesn't help for Plone 4 where you don't know if requirejs will be present.

I've worked around this before by adding this script before I load DataTables:

  <script type="text/javascript">define = undefined; // make sure datatables doesn't register itself with requirejs</script>

That basically hides requirejs from DataTables so that it will initialize itself immediately.

2 Likes

jQuery should be lowercase: jquery and the paths to your own files without extension .js.

require([
    'jquery',
    './++resource++zopyx.plone.persistentlogger/DataTables/media/js/jquery.dataTables',
    './++resource++zopyx.plone.persistentlogger/DataTables/extensions/FixedHeader/js/dataTables.fixedHeader.min',
    './++resource++zopyx.plone.persistentlogger/DataTables/extensions/TableTools/js/dataTables.tableTools.min'
], function($) {
     // your code here
     $.DataTable(blablabla);
});
1 Like

I forgot to mention an alternative: instead of loading your dependencies with RequireJS, you can also package them into a resource (see http://docs.plone.org/adapt-and-extend/theming/resourceregistry.html#resources) and add then to your view using add_resource_on_request

I like the add_resource_on_request approach, seems sane (if a bit magical). Will try it on a future project.

That still doesn't help an add-on developer who's trying to support both Plone 4 and Plone 5, does it?

1 Like

No that's right (but I don't think Andreas is trying to support both Plone 4 and Plone 5, as far as I understand, he said he was trying to port his module to Plone 5).

To me, supporting both Plone 4 and 5 with a unique version is nice when possible, but it is most part of time more difficult than having 2 different versions.

1 Like

I am trying to support Plone 4 and Plone 5 in all my add-ons (that are worth it).

Again: I am loading the JS resources myself with the JS head slot which is working. I am trying to initialize my
stuff within my own JS code within the same template with document.ready(). So this is supposed to work
ootb, right? I don't see why I do have to need require.js here? Why I am not using require.js or the plone legacy bundle or my own bundle...because nothing is working properly (I filed various tickets and ask earlier).

The problem is possibly related to DataTables using require.js in some way itself (not a JS guy, just a consumer).

It is really important to find a way for integrators for dealing with such JS modules in a reasonable way for Plone 4 and Plone 5. This is critical for Plone 5 to take off (especially for developers working with Plone 5 below the integrators level).

Andreas

1 Like

To answer your question: yes putting stuff in the JS head slot like you do should work OOTB.
But apparently you are facing a JS dependency issue (Datatables is not loaded in the correct jquery, or not at the right time, not in the right order, ...), and that's precisely the kind of problems RequireJS can solve.
Hence, using RequireJS is probably the easiest way to go. But it implies you either use the strategy described by David, either manage 2 different version of your add-on (one for Plone 5, using the new JS approach, and one for Plone 4).

Note: I would like to highlight that the global JS status is much better in Plone 5 than it used to be in Plone 4. I agree it is more complex, and it really requires to learn about frontend development, but it worths it.

1 Like

Hi,

I have just submitted a PLIP regarding extra CSS/JS management in Plone 5: https://github.com/plone/Products.CMFPlone/issues/1277

It is not 100% related to the topic discussed here, but you might want to comment it.

Eric

2 Likes

I think I'm having a related issue but I need a break down of the steps to fix it.

Here's what I have:

  • Plone 5
  • My theme is copy of Barceloneta
  • Modified album_view (via portal_view_customizations)
  • 2 custom javascript files and 1 custom css—only for the customized album_view

Followed these instructions to add them to the Resource Registry. I then created a bundle with those two resources with "depends" on "plone."

I have tried various configurations without success, I keep getting this error:

  • The bundle is not referenced in my custom album_view (ex: not using javascript_head_slot).
  • When the bundle is enabled, the menus do not expand from the toolbar.
  • The view and the bundle (with both javascript files and css) load without other errors
  • I tested the custom view, javascript files, and css with a straight html page (no Plone involved, just from my desktop) and everything works as expected

I think the error means that jquery can't be found...? I tried adding it as a dependency and also to the bundle but neither worked. Been fiddling with this all day and have run out of ideas. Any help is appreciated!

This is an approach I used recently.
Perhaps it can help you:

It could be the order the javascripts load , ie: your magnificPopup loads before jquery.
Another option is that the url is wrong (maybe you used relative links and you are loading some js 'from zope' ( localhost:8080/somesite/something/popupjavascript.js actually try to load as localhost:8080/something/popupjavascript.js.

That said: you could consider loading the js inline since it is just for this view

All javascripts (including jQuery) are loaded by RequireJS. Asynchronous Module Definition makes the load order irrelevant. Each module defines it's own dependencies.

To add a jQuery plugin to $ namespace you need to use a require statement like this:

require([
    'jquery',
    '++theme++project/path/to/magnificpopup'  // Note: no `.js`
], function($) {
    $('.popup-gallery').magnificPopup();
});
1 Like

Tried all suggestions. I keep getting the below error.

Error: Mismatched anonymous define() module: function (a).....

Here's my current implementation http://pastie.org/10697976

I've read the react.js Common Errors but I'm just not getting it.

I tried your code, and it works (well I get an error when I try to open the popup because my markup is probably wrong, but anyway, the jQuery plugin is loaded and is available).

The only difference is regarding the scripts tags, I have removed <script src="++theme++osa/image-gallery-popup.js"></script> because it is not needed (require will load it, no need to load it twice), but I do not think that can cause the error you get.

The other part of the

Error: Mismatched anonymous define() module: function (a).....

is

var e = new Error(msg + '\nhttp://requirejs.org/docs/errors.html#' + id);

which starts at line 165 in require.js