9

So I've spent literally hours on hours googling, reading, studying ect, but no one (not even Alan Storm!) has spelt this out to me. It seems the entire internet is interested in adding JS or CSS to a particular page of Magento 2, but what I'm looking for is adding JS/CSS to a particular block.

So, here's my question in a nutshell :

What is the best way to add JS (and additionally CSS) to a particular block, so that if the block is present on the page (*) the JS/CSS is loaded, if the block is not there, no CSS/JS??

* This means anywhete a block can be set up, on a page/template via layout.xml, on a custom page from my module, via the toHtml method of a block/page or most importantly a block embedded in a WYSIWYG of a category/product description/CMS Block/CMS Page.

I have read ALOT of Alan's great articles (Kudos again to this guy!!), not to mention the reams of other articles around this, however I get the feeling everyone wants to add to a page, a specific page, not where ever the block is used.

I feel I am familiar with the various techniques however I might be missing something here so I'd like a concensus from the community, as well as perhaps a bit of a sign post for all the front enders throught to full stack devs out there seeking similar questions and pondering the options as I am.

Previously, in Magento 1, I would look to the blocks constructor, get the layout, get the head reference and call addJs/addCss there, or if possible use the methods in the layout.xml. This would meanthe JS was "added" to the resourse list on the blocks constructor (before the theme level would output the head block). But this doesn't seem possible now.

I have read up on how to go about adding JS/CSS (this is not a simple "how do I???" this is a more consise "what is the correct/mag2 way???") and am familiar with these techniques :

  • /view/[area]/layout/[default/page_id].xml technique, using the <head></head> root elements
  • Adding a Head block to my module, appened into head.additional, with some sort of logic regarding if my block is loaded
  • using the \Asset\GroupedCollection and \Asset\Repository Objects to inject from custructor of a page/Template (this doesn't seem to ork with blocks though), potentually the order of loading??
  • Using RequireJS and appliying a requireJS config to my module

Have I missed anything??

One believes the correct way would be to use the RequireJS library, and either x-magento-init attributes or just a script with a require("my_module", function(){ ... }) syntax in an inline script. But this seems klunky to me? I would have to set up scripts to load scripts, am forced to inline at least some of my JS, however it seems the most susinct way of saying "here's my block, now require me some JS", by popping this into my phtml.

I would however really like to be able to do this via PHP, as a backend/stack programmer I'd ideally like to encapsulate the JS away and (ideally) allow my front end team to write this as they want it. In short take care of the loading (Back End Dev to Frond End Dev "heres the phtml, override in the theme if you like, likewise heres the js file, its dependant libs and heres the css for the block").

This suggests the __construct method with Injected Dependancies on the Assets system. I can not however get this to work, this seems to be confirmed in Alan Storms quick article here : Magento Quickies: Magento 2: Adding Frontend Asset Files Programmatically

Note the sign off "So any thoughts of creating blocks that carry their frontend assets with them is out the window." ... bummer :(

Thank you guys for taking the time to read and consider. I look forward to hearing your responses!

PS> Obviously this is StackExchange so I'll mark the answer as the best course for what I'm trying to achieve (block specific resource loading) however I will endevour to also list as refences at the bottom of my post any and all answers that either add to the discussion or suggest a solid solution!

5

For js it should be easy because magento 2 uses require.js.
So this means you can include a js on the fly when you need it.

A block has to be rendered (in most cases) by a template.
So you need to add this in your template:

<script type="text/javascript">
    require([
        "jquery",
        .... //any other js dependencies you have
        "Namespace_Module/js/filename_here"
    ], function(){
        //some js code here. 
        //if you don't need any additional js code just have an empty function
    });
</script>

Now create your js file view/adminhtml|frontend/web/js/filename_here.js where all your js code is present.

require.js will know how to pick up your file when it is requested.

For css files I don't know if it's possible.
The css files should go to the head section of the page, but for example if you have your block inside the content of a cms page like this {{block class="..." template="..."}}, when the content of the cms page should be processed, the html up to that point is already rendered so you cannot add anything else to the head block via php. You can try to add it in the template like <style... but that's not what you want (I assume).

  • Thanks Marius, I am exploring the requireJS route as one of the possabilities I listed above, but thank you for the consise example! I agree however it does not offer a solution to the CSS issue, however this may be a mute point due to Magento's Less compiler ect. Also, it's abit abstract but I suppose we could use requireJS to load our JS which in turn could add a link to the stylesheet tothe DOM, its asyncronous at least!!! – cygnus digital Aug 16 '16 at 9:31
  • In fact, I find this on the requirejs pages, how to load CSS with RequireJS! : requirejs.org/docs/faq-advanced.html#css – cygnus digital Aug 16 '16 at 9:58
  • @cygnusdigital. Nice. So problem solved :) – Marius Aug 16 '16 at 11:03
  • Hi Marius, well yes and no, it's certianly a good candidate for the solution, it fits the requirements so I do thank you for your input, but I am looking for discussion and alternatives. Perhaps its just me being a dinosaur however I do feel its more consise to have JS/CSS loading within a constructor, ie. "if the block is built/included, add this to the head." :D I do wonder if its possible to conditionally add to the head.additional block on the fly. (i.e. add a head block from my module to head.additional at the block constructor stage). – cygnus digital Aug 16 '16 at 12:42
  • you can do that with regular blocks that are added via layout files, but as I explained you cannot do this for blocks included in the page contents via {{block}} directives, because the head section is already rendered when the block class is instantiated. – Marius Aug 16 '16 at 12:45

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.