Magento 2 Frontend: Adding Javascript and CSS via Layout XML

Layout system critiqued, let’s get back to the practical business of adding a front end file to our Magento module. Before we begin, per pervious articles in this series, the following assumes

  1. You’re working with developer mode enabled (SetEnv MAGE_MODE developer in your apache config)
  2. That you’ve disabled the full page caching in System -> Cache Management

You may want to create your own module manually you can follow the instructions in our Introduction to Magento 2 — No More MVC article.

The Layout Head Section

One of the new features Magento 2 introduces is context aware layout update XML files. By context aware we mean that end-programmer-users can add commands/directives to their layout XML files that only effect a particular section of the document. In plain english — layout update XML files now have a <head/> section where you can add head specific information about a file.

Sample code is often worth 1,000 words. Open up your module’s layout handle XML file

and add the following <head/> node

app/code/Ecommage/HelloWorldMVVM/view/frontend/layout/ecommage_helloworldmvvm_index_index.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
    <head>
        <css src="Ecommage_HelloWorldMVVM::test.css"/>
        <link src="Ecommage_HelloWorldMVVM::test.js"/>
    </head>
    <referenceBlock name="content">
        <block template="content.phtml" class="Ecommage\HelloWorldMVVM\Block\Main" name="ecommage_helloworldmvvm_block_main" />
    </referenceBlock>
</page>

After making the above changes, clear your cache,

$ php bin/magento cache:flush

you’re telling Magento

use the test.css file found in the Ecommage_HelloWorldMVVM module.

Without these Vendor_Module::... identifiers Magento would try loading these files from the theme hierarchy.

With the URLs generated, if you add corresponding files to your module at

app/code/Ecommage/HelloWorldMVVM/view/frontend/web/test.js

alert("hello");

 

app/code/Ecommage/HelloWorldMVVM/view/frontend/web/test.css

body{
    background-color:#f00;
}

and reload the page, you’ll see that Magento has loaded them correctly into the system.

Adding Files Via PHP

In addition to using Magento 2’s layout XML system to automatically add front end asset URLs to your project, you can also create these URLs via PHP using a Magento\Framework\View\Asset\Repository object. We’ll show you how to do this below, as well as how to add arbitrary HTML to the <head/> of a Magento HTML page.

Starting with the later item, add the following node to our layout handle XML file

app/code/Ecommage/HelloWorldMVVM/view/frontend/layout/ecommage_helloworldmvvm_index_index.xml

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="1column" xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
    <!-- ... -->
    <referenceBlock name="head.additional">
        <block  template="head.phtml"
                class="Ecommage\HelloWorldMVVM\Block\Head"
                name="ecommage_helloworldmvvm_block_head" />
    </referenceBlock>
    <!-- ... -->
</page>

The above code

  1. Gets a reference to the head.additional block
  2. Creates a new Ecommage\HelloWorldMVVM\Block\Head block named ecommage_helloworldmvvm_block_head that uses the head.phtml template.
  3. Adds that new block to the head.additional block using the reference from #1

The head.additional block is a special block. Any block added to head.additional will automatically be output into the <head/> area of a Magento page. If you read our critique above, this is another bit of confusion added by the <head/> context. Even though our ultimate goal is to add something to <head/>, we need to operate inside the layout handle XML file’s <body/> tag.

Regardless, once we’ve got the layout XML in place, we’ll want to create our new Head block class

app/code/Ecommage/HelloWorldMVVM/view/frontend/templates/head.phtml

<?php
namespace Ecommage\HelloWorldMVVM\Block;
class Head extends \Magento\Framework\View\Element\Template
{
}

As well as a template

app/code/Ecommage/HelloWorldMVVM/view/frontend/templates/head.phtml

<!-- Hello There -->

With the above in place, clear your Magento cache and reload your page. You should see the <!-- Hello There --> comment in your page’s <head/> node.

With a new template rendered in <head/>, we’re ready to render an asset URL using the asset repository.

The Asset Repository

The Magento\Framework\View\Asset\Repository object will allow us to create asset objects. Asset objects can convert a file identifier like foo/test.js or Ecommage_HelloWorldMVVM::test.js into a full URL.

Like any object in Magento 2, when we want an instance of an object we don’t directly instantiate it — we inject it in another object’s constructor. Change your Head.php file so it matches the following

app/code/Pulsestorm/JavascriptCssExample/Block/Head.php

<?php
namespace Ecommage\HelloWorldMVVM\Block;
class Head extends \Magento\Framework\View\Element\Template
{
    public $assetRepository;
    public function __construct(
        \Magento\Framework\View\Element\Template\Context $context,
        array $data = [],   
        \Magento\Framework\View\Asset\Repository $assetRepository
    )
    {
        $this->assetRepository = $assetRepository;
        return parent::__construct($context, $data);
    }
}

What we’ve done above is use Magento 2’s automatic constructor dependency injection to create a\Magento\Framework\View\Asset\Repository object, and assign it to the assetRepository property of our block object. The other parameters in __construct and the call to parent::__construct are there for compatibility with the base template block class. Also, notice we made assetRepository a public property. This means we’ll be able to access it in our phtml template.

Edit your head.phtml file so it matches the following.

app/code/Ecommage/HelloWorldMVVM/view/frontend/templates/head.phtml

<?php
    $asset_repository = $this->assetRepository;
    $asset  = $asset_repository->createAsset('Ecommage_HelloWorldMVVM::test.js');
    $url    = $asset->getUrl();
?>
<!-- Hello There -->
<script src="<?php echo $url; ?>"></script>

With the above in place, clear your cache, delete the files in var/generate/* (because you changed an automatic constructor dependency injection constructor), and reload the page. If you view the raw HTML source, you should see a new <script/> tag rendered with a full asset URL.

What we’ve done above is use the createAsset method of the asset repository object to create an asset object. Then, we use the getUrl method of the asset object to fetch the HTTP url of the asset. All we need to know is the file identifier — Magento handles the grunt work of pulling together the correct URL path parameters.

Wrap Up

Today, after a long winded critique of Magento 2’s layout language, we demonstrated how to use that language to add front end CSS and Javascript assets to a Magento page. We also investigated directly using the underlying PHP asset repository that makes this possible.

In the past few articles, we’ve been entirely focused on getting “raw” front end asset files into our system. Next time we’ll start investigating how Magento has integrated with the new higher level front end abstractions like RequireJS and Less CSS.

Comment

There is no comment on this post. Be the first one.

Leave a comment