Our Magento experts are going to describe the various systems Magento 2 has for kicking off execution of javascript code without an embedded <script type=”text/javascript”> tag.
Javascript Init Methods
First thing is , they are providing a standard technique to discourage directly embedding javascript into a page.
Secondly they provide a way to invoke a stand alone RequireJS module (defined with define) as a program.
Third, they are providing a way to pass that program a server side generated JSON object.
Lastly, they are providing a way to tell that program which (if any) DOM nodes it should operate on.
Keep these four steps in mind.
Set up a Module
We’re going to use pestle to create a module named Toweringmedia_JavascriptInitTutorial with a single URL endpoint by running the following three commands
$ pestle.phar generate_module Toweringmedia_JavascriptInitTutorial 0.0.1
$ pestle.phar generate_route Toweringmedia_JavascriptInitTutorial frontend Toweringmedia_javascriptinittutorial
$ pestle.phar generate_view Toweringmedia_JavascriptInitTutorial frontend toweringmedia_javascriptinittutorial_index_index Main content.phtml 1column
$ php bin/magento module:enable Toweringmedia_JavascriptInitTutorial
$ php bin/magento setup:upgrade
Now run the above, you will be able to access the following URL in your browser.
http://magento.example.com/toweringmedia_javascriptinittutorial/
and you see the rendered app/code/Toweringmedia/JavascriptInitTutorial/view/frontend/templates/content.phtml template.
Set up a RequireJS Module
Now we are doing a quick review and make a RequireJS module. First, create the following file
//File: app/code/Toweringmedia/JavascriptInitTutorial/view/frontend/web/example.js
define([], function(){
alert(“A simple RequireJS module”);
return {};
});
Due to the module’s location on the file system, and the way Magento loads javascript files, this module’s name/identifier is Toweringmedia_JavascriptInitTutorial/example
Next, change this contents of content.phtml so they can match the following.
#File: app/code/Toweringmedia/JavascriptInitTutorial/view/frontend/templates/content.phtml
<script type=”text/javascript”>
requirejs([‘Toweringmedia_JavascriptInitTutorial/example’],function(example){
alert(“Loaded”);
console.log(example);
});
</script>
Now we makes a RequireJS program with a single module dependency. The dependency is our just makes a module (Toweringmedia_JavascriptInitTutorial/example). Load the
http://magento.example.com/toweringmedia_javascriptinittutorial/
URL in your system, and you should see the wary.
If any of the above was foreign to you, you can want to review our Magento 2 and RequireJS article.
X-Magento-Init
#File: app/code/Toweringmedia/JavascriptInitTutorial/view/frontend/templates/content.phtml
<div id=”one” class=”foo”>
Hello World
</div>
<div id=”two” class=”foo”>
Goodbye World
</div>
<script type=”text/x-magento-init”>
{
“*”: {
“Toweringmedia_JavascriptInitTutorial/example”:{}
}
}
</script>
Give your page a reload with the above, and you will see the wary from our example.js file.
If you’ve never seen this syntax before, it’s look like a little kinky. Let’s take it segmented by pieces.
First is the <script/> tag
#File: app/code/Toweringmedia/JavascriptInitTutorial/view/frontend/templates/content.phtml
<script type=”text/x-magento-init”>
//…
</script>
The tag is not a javascript tag. Notice the type=”text/x-magento-init” attribute. When the browser doesn’t identify the value in a script’s type tag, it will skip the contents of that tag. Magento (similar to other modern javascript frameworks) uses this behavior to its advantage. While it’s beyond the scope of this tutorial, there’s Magento javascript code running that will scan for text/x-magento-init script tags. If you want to pursue this yourself, this Stack Exchange question and answer is a good position to start.
The another part of the x-magento-init code shred we can talk about instantly is the following object
#File: app/code/Toweringmedia/JavascriptInitTutorial/view/frontend/templates/content.phtml
{
“Toweringmedia_JavascriptInitTutorial/example”:{}
}
Magento will looks at the principal of this object, and include it (the key) as a RequireJS module. That’s what loading our example.js script.
You may also be thinking why the whole thing is a part of other object with a * as a main content.
#File: app/code/Toweringmedia/JavascriptInitTutorial/view/frontend/templates/content.phtml
{
“*”: {/*…*/}
}
Before we can talk about that, we’ll need to discussion about javascript components.
Magento Javascript Components
The above example runs our RequireJS module as a program. This works, and Magento itself often uses the x-magento-init procedure to invoke a RequireJS module as a program. However, the strength of x-magento-init is the ability to makes a Magento Javascript Component.
Magento Javascript Components are RequireJS modules that return a function. Magento’s system code will call this function in a particular mode that disclose more functionality.
If that didn’t make sense, try changing your RequireJS module so it matches the following.
//File: app/code/Toweringmedia/JavascriptInitTutorial/view/frontend/web/example.js
define([], function () {
var mageJsComponent = function()
{
alert(“A simple magento component.”);
};
return mageJsComponent;
});
Here we’ve defined a function and imposed it to a variable named mageJsComponent. Then, we return it.
If you refresh the page with the above in place, you will see A Simple Magento Component in an alert box.
It may seem silly — what’s the point of returning a function if all Magento does is call it? You’d be right, but that’s because we left something out. Try alternative our phtml template so it matches the following
#File: app/code/Toweringmedia/JavascriptInitTutorial/view/frontend/templates/content.phtml
<div id=”one” class=”foo”>
Hello World
</div>
<div id=”two” class=”foo”>
Goodbye World
</div>
<script type=”text/x-magento-init”>
{
“*”: {
“Toweringmedia_JavascriptInitTutorial/example”:{“config”:”value”}
}
}
</script>
and changing our RequireJS module so its view like
//File: app/code/Toweringmedia/JavascriptInitTutorial/view/frontend/web/example.js
define([], function () {
var mageJsComponent = function(config)
{
alert(“Look in your browser’s console”);
console.log(config);
//alert(config);
};
return mageJsComponent;
});
If you refresh the page, you will see the changed alert message. If you look into the browser’s javascript console, you can also see the following
> Object {config:”value”}
When we make a Magento Javascript Component, Magento calls the returned function and includes the object from text/x-magento-init.
“Toweringmedia_JavascriptInitTutorial/example”:{“config”:”value”}
It is why the RequireJS module name is a key — the value of this object is the object we want to pass to our component.
However, in a real module, we’d be yield that JSON with PHP. This system allows us to render the JSON in our phtml template, and then have that passed to Javascript code. This helps skipped the problem of produces Javascript directly with PHP, which can lead to too much Garbage code, and build it easy to accidentally slip in an error or security problem.
Before we complete with x-magento-init, there’s one final feature to discuss. You’ll retain in mind that we said that x-magento-init
provides a mode to pass that program a server side created JSON object.
prescribe a mode to provide that program with the DOM nodes it should operate on.
We’ve covered how to pass in server side generated JSON — but what about the DOM nodes?
Change the RequireJS module so it looks like this
//File: app/code/Toweringmedia/JavascriptInitTutorial/view/frontend/web/example.js
define([], function () {
var mageJsComponent = function(config, node)
{
console.log(config);
console.log(node);
//alert(config);
};
return mageJsComponent;
});
What we’ve completed here is add a another parameter to our mageJsComponent function. This another parameter will be the DOM node we want our program to operate on. if you refresh the page with the above in position, you’ll see the following in your console.
> Object {config:”value”}
> false
Magento did pass in a value for node — but that value was wrong. What gives?
Its necessary that, Change your phtml template so it matches the following.
#File: app/code/Toweringmedia/JavascriptInitTutorial/view/frontend/templates/content.phtml
<div id=”one” class=”foo”>Hello World</div>
<div id=”two” class=”foo”>
Goodbye World
</div>
<script type=”text/x-magento-init”>
{
“#one”: {
“Toweringmedia_JavascriptInitTutorial/example”:{“config”:”value”}
}
}
</script>
Here, we’ve changed the * to a #one. The * we used earlier is actually a particular incident, for programs that don’t need to operate on DOM nodes. The principal for this object is actually a CSS/jQuery style selector that tells Magento which DOM nodes the program in Toweringmedia_JavascriptInitTutorial/example should operate on. If we refresh our page with the above in position, we’ll see the following in your console
> Object {config: “value”}
> <div id=”one” class=”foo”>Hello World</div>
You’re not limited to IDs — you can uses CSS class identifiers. Change it to
#File: app/code/Toweringmedia/JavascriptInitTutorial/view/frontend/templates/content.phtml
“.foo”: {
“Toweringmedia_JavascriptInitTutorial/example”:{“config”:”value”}
}
and you’ll see the console below.
> Object {“config”:”value”}
> <div id=”one” class=”foo”>Hello World</div>
> Object {“config”:”value”}
> <div id=”two” class=”foo”>Goodbye World</div>
By constructing this sort of system, Magento is encouraging developers to skip hard coding their DOM nodes into their RequireJS modules. The x-magento-init means there’s a system level path forward for building Javascript modules that confide on server side rendered JSON, and operate on any arbitrary DOM node. It’s evermore possible for Magento module developers to performance their own systems for this sort of functionality, but Magento 2 provides a standard, built in way to achieve this.
Data-mage-init Attribute
In addition to <script type=”text/x-magento-init”>, there’s other mode to invoke analogous functionality on a specific DOM node, and that’s with the data-mage-init attribute. Try replacing the existing phtml template with the following
<div data-mage-init='{“Toweringmedia_JavascriptInitTutorial/example”: {“another”:”example”}}’>A single div</div>
Refresh the page with the above in position, you will see the javascript console.
> Object {another: “example”}
> <div>A single div</div>
Here, we’ve added a data-mage-init attribute to a perticular div. This attribute’s value is a JSON object. Similar to x-magento-init, this object’s principal is the RequireJS module we want to invoke as a program or Magento Javascript Component, and the merit is a nested JSON object to pass into our Magento Javascript Component function as the config parameter.
On a pedantic note — you’ll see what we used single quotes with our attribute
<div data-mage-init=’…’>A single div</div>
The value of the data-mage-init attribute is parsed by a strict JSON parser, which means the JSON object’s quotes must be double quote — which means we can’t use double quotes for our attribute. While this is technically very well in HTML5, for web programmers of a particular age it brings back some poor Microsoft Frontpage memories.
Wrap Up
Whether you finishing by using the <script type=”text/x-magento-init”> component or a data-mage-init attribute in a specific node, both these techniques gives a standard, system unified mode of proposing Javascript entry points onto your page. Many of Magento’s front end and back end UI features rely on this syntax, so even if you personally throw away them, understanding how these systems work is significant part of being a Magento 2 developer.