Magento 2: Adding Frontend Files to your Module

Last time we discussed how Magento serves and generates front end (javascript, css) files to end users from its own modules. Like most of Magento’s feature, if it’s done in a core module, third party developers can do it in their own modules. This time we’ll be creating a module of our own, and using it to add front end javascript/css files to the system.

The specifics in this article refer to the official Magento 2.0 released in the fall of 2015. While the specifics may change in future versions, the concepts should apply to all versions of Magento 2.

A Quick Note on File Permissions

If you take a close look at how Magento handles unix file permissions in its code generation systems, one thing is clear. The core team are not fans of Apache’s mod_phpmodule, and probably run their systems using some sort of PHP-FPM/FastCGI implementation.

As a result, if you’re running PHP with the Apache mod_php module (the most common out of the box way of running PHP) you may end up running into problems with Magento created files. Specifically, files created via command line mode that PHP can’t read/write when running in web server mode, or vice versa.

“The right” thing to do here would be to ensure the apache user and your own shell user are in the unix same group. That’s a non-trivial thing to do though. If you’re not up for it, and you understand the security implications, another way of dealing with it is just chmod 777ing your development files in pub/static and var/ folders. Here’s two quick findcommands that will do this for you.

$ find /path/to/magento2/pub/static -exec chmod 777 '{}' +
$ find /path/to/magento2/var/ -exec chmod 777 '{}' + 

This isn’t my favorite approach, but until Magento 2 gets its permission situation in order it’s the only simple way of dealing with your development environment.

Creating the Module

Concerns about file permissions aside — step one is creating a new module. We’re going to run through the steps, cookbook style, below. If you’re curious on a more in depth look at creating modules in Magento, try our Introduction to Magento 2 — No More MVC article.

We’re going to name our module Pulsestorm_FrontendTutorial1. To create this module, we’ll need to create two files. First, create the module.xml file.

#File: app/code/Pulsestorm/FrontendTutorial1/etc/module.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd">
    <module name="Pulsestorm_FrontendTutorial1" setup_version="0.0.1" />
</config>

Then, create the register.php file

#File: app/code/Pulsestorm/FrontendTutorial1/registration.php
<?php
    \Magento\Framework\Component\ComponentRegistrar::register(
        \Magento\Framework\Component\ComponentRegistrar::MODULE,
        'Pulsestorm_FrontendTutorial1',
        __DIR__
    );?>

With these two files in place, use Magento’s CLI program to enable your module.

$ php bin/magento module:enable Pulsestorm_FrontendTutorial1
The following modules have been enabled:
- Pulsestorm_FrontendTutorial1

To make sure that the enabled modules are properly registered, run 'setup:upgrade'.
Cache cleared successfully.
Generated classes cleared successfully. Please re-run Magento compile command
Info: Some modules might require static view files to be cleared. Use the optional --clear-static-content option to     clear them.

And then run the CLI’s setup:upgrade command.

$ php bin/magento setup:upgrade
Cache cleared successfully
File system cleanup:
/Users/alanstorm/Sites/magento-2-with-keys/magento2/var/generation/Composer
/Users/alanstorm/Sites/magento-2-with-keys/magento2/var/generation/Magento
/Users/alanstorm/Sites/magento-2-with-keys/magento2/var/generation/Symfony
Updating modules:
Schema creation/updates:
Module 'Magento_Store':

//...

Module 'Pulsestorm_FrontendTutorial1':

Please re-run Magento compile command

After doing the above, your (functionless) module will be installed into the Magento system.

System Assumptions

If you finished the article from last week, you know that Magento’s front end file serving behaves differently depending on

  1. The root folder you’ve chosen for your website
  2. The “mode” Magento is running in

We’re going to start this article assuming

  1. That you’ve setup your web site’s root folder as the pub/ folder
  2. That you’re running in developer mode

If you’re unsure of how to do this, or curious why we need to have this disclaimer, be sure to read Magento 2: Serving Front End Files

Adding Front End Files

Our goal for this article is to have URLs similar to the following

http://magento.example.com/static/frontend/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js

return javascript files from our module. Let’s start with hello.js. First, add the following file to our Magento module.

#File: app/code/Pulsestorm/FrontendTutorial1/view/base/web/hello.js
alert("Hello World");

With the above in place, load the following URL in your browser or via a command line program like curl

http://magento.example.com/static/frontend/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js

You should see the contents of your file! Congratulations, you’ve just added your first front end file to a Magento 2 system

What Just Happened — Module Files

Magento 2 allows a module developer (some might say forces them) to include javascript and CSS files under their main module folder. The old Magento top level skin folder is gone, and app/design is reserved exclusively for Magento theme files.

The top level module view folder (app/code/Pulsestorm/FrontendTutorial1/view) above, is where a module developer places all files related to Magento 2’s user interface. This includes the front end files we’re interested in today, as well as Magento’s layout handle XML files and phtml template files.

The next folder, view, is the area folder. Areas area a way to split individual Magento 2 applications into different areas of functionality, based on the URL. i.e. The cart application is Magento’s frontend area, the backend admin console is the adminhtml area.

So what’s the base area? This is one of those places where Magento 2 has improved on Magento 1 — the base area is a special folder that will allow you to serve your files fromeither the frontend or adminhtml areas. We’ll talk more about this below.

Next up is the web folder. Files in web are ones that will be served via http or https. While they’re beyond the scope of this article, other folders at this level are email, layout,page_layout, templates, and ui_component.

Finally, we have our file, hello.js. Notice, unlike Magento 1, there’s no need for us to create a sub-folder for our assets. Because these files already live in a Magento module folder, Magento’s smart enough to know where they should go. That said, some Magento core modules still separate out these files with an additional folder/namespace. While you’re free to do this, there’s no need to do this.

What Just Happened — Asset URLs

Module files explained, next up let’s take a look at that URL.

http://magento.example.com/static/frontend/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js

We’re going to examine each URL segment and describe where it comes from. For the most part, you won’t need to remember all this — Magento will do most of the URL generating for you. However, understanding the URL asset path will be useful if you’re debugging a system that’s returning 404s, 500s, or some other error when browsers request front end asset files.

The first part of a Magento 2 front end asset URL is static. This points to the actual

pub/static

folder in your Magento install. If you’re using the root level index.php file instead ofpub/index.php, this should be

http://magento.example.com/pub/static/...

The next URL segment is the Magento area. In our example URL above, this is frontend. However, because we created a file in the base area folder, we can also fetch this file via the following URL

http://magento.example.com/static/adminhtml/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js

i.e., using adminhtml instead of frontend. We’ll talk more about this in the area section below — for now all we need to know is this URL segment is the area.

The next URL segment is Magento — this is the vendor prefix for the theme name. This is similar to the design package in Magento 1. This is Magento because the theme we’re using is in the app/design/frontend/Magento folder.

After the vendor prefix is the theme’s actual name. In our case, we’re using the blanktheme in the URL. However, we could also use the luma theme that ships with Magento 2.0, or any theme installed in the system.

The penultimate folder, en_US, is the locale folder. Magento allows different front end asset files per locale — think of hard coded language strings in javascript, or locale specific images in CSS files. If we wanted a version of our file for french, we’re use fr_FR in the URL

http://magento.example.com/static/adminhtml/Magento/blank/fr_FR/Pulsestorm_FrontendTutorial1/hello.js

and create our french version of hello.js in

app/code/Pulsestorm/FrontendTutorial1/view/base/web/i18n/fr_FR/hello.js

Notice the i18n folder — this stands for internationalization, and is topic well beyond the scope of this, or any single, article.

Our final url segment is the name of our module — Pulsestorm_FrontendTutorial1. This is why Magento 2 doesn’t need you to self-organize your files in the view/[area]folder. By generating URLs with the module name in them, Magento 2 has enough information to find the file in your module folder. Change this segment of the URL, and Magento won’t find your file.

http://magento.example.com/static/science/Magento/blank/en_US/Some_OtherModuleWithAHelloJs/hello.js

With developer mode enabled, Magento will use each of these URL segments to find the correct file, read it into memory via PHP, and echo it back out to the end user. While this is appropriate for an individual developer’s machine, this sort of dynamic lookup would be unacceptable for a medium to high traffic production system.

That’s where static asset generation comes into play.

Generating Static Assets for Production

If you’ve read the previous article in this series, you know the first time we accessed each of the above URLs, Magento automatically generated a file for us in pub/static. In the first non-beta Magento 2.0 release, these files generated during development mode are symlinks to the actual file. You can see this for yourself with ls -lh

$ ls -lh pub/static/frontend/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js 
lrwxrwxrwx  1 _www  staff   112B Dec 27 11:19 pub/static/frontend/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/ hello.js -> /path/to/magento2/app/code/Pulsestorm/FrontendTutorial1/view/base/web/hello.js

This is a common approach for many PHP frameworks. However, another common approach is to just copy the file over the first time its requested. There’s no clear right way here, and I wouldn’t be surprised to see the Magento core team flip-flop its approach in a future release.

Regardless of whether they’re symlinks or actual files, generating these on the fly is inappropriate for a production environment. In addition to putting additional burden on the server’s file system, it also introduces a potential attack vector for black hat hackers. That’s why if we remove the generated file(s)

$ rm pub/static/frontend/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js
$ rm pub/static/adminhtml/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js

and then switch our system into production mode

#If you're having trouble, did you check pub/.htaccess for mode setting?
#File: .htaccess
SetEnv MAGE_MODE production

Magento will return a blank screened, 404 for the file.

$ curl -i 'http://magento.example.com/static/frontend/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js'
HTTP/1.1 404 Not Found
Date: Sun, 27 Dec 2015 20:30:30 GMT
Server: Apache/2.4.10 (Unix) PHP/5.6.16
X-Powered-By: PHP/5.6.16
X-Frame-Options: SAMEORIGIN
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
Content-Type: text/html; charset=UTF-8

When you deploy your Magento application, you need to generate these files on your own. You can do this by running the following command

$ php bin/magento setup:static-content:deploy
Requested languages: en_US
=== frontend -> Magento/blank -> en_US ===
.....................................

...

---

New version of deployed files: 1451248980

After the above finishes running, you’ll have a statically generated file for every web file inevery Magento module. We can see this if we use the command line find program to look for generated hello.js file.

$ !find
find pub/ -name hello.js
pub//static/adminhtml/Magento/backend/en_US/Pulsestorm_FrontendTutorial1/hello.js
pub//static/frontend/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js
pub//static/frontend/Magento/luma/en_US/Pulsestorm_FrontendTutorial1/hello.js

Here we can see that Magento has created three hello.js files. One is for the backendtheme in the Magento adminhtml area. The other two are for the frontend area — one for the blank theme, another for the luma theme.

Before we move on to our promised discussion of areas — there’s a few things to take note of. First (at the time of this writing), the files generated for production are not symlinks, and are actual copies of the file

$ ls -lh pub/static/frontend/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js 
-rw-rw-rw-  1 alanstorm  staff    21B Dec 27 13:10 pub/static/frontend/Magento/blank/en_US/ Pulsestorm_FrontendTutorial1/hello.js

This means if you change something directly in a module folder on your production system, you won’t see the change reflected on the live site. Also, if you do directly edit the files in the pub/static sub-folders, your changes will be deleted the next time someone deploys to the production server.

Another thing to watch out for here: If the symlinks from development mode are still present in the pub/static sub-folders when you run setup:static-content:deploy, Magento will not remove them. On one hand — this shows whomever implementedsetup:static-content:deploy cared enough to make sure their command wasn’t destructive. On the other hand — if your deployment procedure isn’t super tight, this means you may end up with symlinks on your production website.

Understanding the Area Hierarchy

The first thing we had you do in this tutorial was create the following file

#File: app/code/Pulsestorm/FrontendTutorial1/view/base/web/hello.js
alert("Hello World");

This added the hello.js file to both the adminhtml and frontend areas.

http://magento.example.com/static/frontend/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js
http://magento.example.com/static/adminhtml/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js

It’s also possible, and far more common, to have a javascript or css file that’s only needed in one of the two areas. For example, try creating the following file

#File: app/code/Pulsestorm/FrontendTutorial1/view/frontend/web/hello-2.js
alert("Hello Frontend World");

Notice its location is almost exactly the same as our original file — the only difference is we’ve replaced the base folder with a folder named frontend. With the above in place, and developer mode reenabled, try loading the file’s corresponding URL

$ curl -i 'http://magento.example.com/static/frontend/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello-2.js'
HTTP/1.1 200 OK
Date: Mon, 28 Dec 2015 02:06:30 GMT
Server: Apache/2.4.10 (Unix) PHP/5.6.16
Last-Modified: Mon, 28 Dec 2015 02:04:32 GMT
ETag: "1e-527ebb9d38c00"
Accept-Ranges: bytes
Content-Length: 30
X-Content-Type-Options: nosniff
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Cache-Control: public
Content-Type: application/javascript

alert("Hello Frontend World");

You should see your file returned without a hitch. However, if you attempt to load the same file using an adminhtml area, you should see a 404 Not Found error.

$ curl -I 'http://magento.example.com/static/adminhtml/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello-2.js'
HTTP/1.1 404 Not Found

If there’s a file in both a specific area folder and the base area folder

app/code/Pulsestorm/FrontendTutorial1/view/base/web/hello-2.js
app/code/Pulsestorm/FrontendTutorial1/view/frontend/web/hello-2.js

the file in the specific area folder (frontend above) will win out.

Generally speaking, you should try to keep your front end asset files in the appropriate area folder. If you need a javascript file that adds features for the backend admin console, it’s best to keep that in the adminhtml folder. However, if there’s a library file your module needs in both locations, then using the base folder is appropriate.

Developer Mode Quirks

Finally, when you’re working with your system in developer mode, there are a few quirks to be aware of. Consider the URL for our original helloworld.js file

http://magento.example.com/static/frontend/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js

If you change the URL to use a fake area like the one below

http://magento.example.com/static/fake-area-that-is-not-there/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js

You’ll be surprised to find out Magento still returns the file found in the base folder

$ curl -I http://magento.example.com/static/fake-area-that-is-not-there/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js
HTTP/1.1 200 OK

The same is true for the theme vendor, theme name, and locale portions of the URL (replaced with bar, foo, and baz below)

http://magento.example.com/static/fake-area-that-is-not-there/bar/foo/baz/Pulsestorm_FrontendTutorial1/hello.js

This behavior can go from curious anomaly to crazy making if you’re manually creating URLs (say, for a tutorial). Consider the following typos

http://magento.example.com/static/frotnend/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js
http://magento.example.com/static/frontend/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js

http://magento.example.com/static/front-end/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js
http://magento.example.com/static/fronternd/Magento/blank/en_US/Pulsestorm_FrontendTutorial1/hello.js

Fortunately though, as hinted at throughout this article, you shouldn’t need to manually create paths to front-end static assets. Magento 2 contains a number of different systems for pulling in javascript and CSS files, and a number of PHP objets and methods for generating these paths programmatically.

Now that we have a better understanding of how Magento serves front end static assets, and also understand how we can add individual front end assets to our own modules, our next steps in exploring the various Magento systems that will let you use these assets. That’s where we’ll start next time.

Magento 2: Serving Frontend Files

When Magento 1 was initially released, “front end development” as it exists today was barely a thing. CSS was still largely written by hand, and jQuery vs. PrototypeJS was still a legitimate question for a new software project. Magento 2 enters a world where javascriptis the dominant language in the interactive agency world, and the thought of not using a CSS preprocessor is considered barbaric.

While Magento 2 does use many modern front end technologies (RequireJS, LESS for CSS, etc.), at its heart Magento’s still a full stack PHP framework. This means, in addition to understanding these new technologies, a well rounded Magento developer also needs to understand how the Magento core team has integrated these new technologies into the system. This is a huge topic, and one we’ll be covering over the next few articles.

These articles are written against the official, Magento 2.0 released in the fall of 2015. Concepts should hold for future versions of Magento 2, but minor details may change.

Serving CSS and Javascript Files

We’re going to start at the bottom of the front end abstraction tree, and talk about including “raw” front end resource files (.js, .css, etc.) with your Magento module. This may be lower down the stack than you’re used to working as a modern front end developer, but understanding how these fundamentals work will be an important part of your job if you’re working with, or developing modules/themes for, Magento 2 systems.

Before we talk about javascript and CSS though, we need to talk about Magento 2’s root web folder, and Magento 2’s modes.

Magento 2 Root Folder

When you setup a PHP based framework, you need to point your webserver (Apache, nginx, etc) at a “root” folder. In other words, if someone requests URLs like these

http://magento2.example.com/index.php
http://magento2.example.com/path/to/file.js

it means they’re accessing files on your system like these

/var/www/html/index.php
/var/www/html/path/to/file.js

In the examples above /var/www/html is the root web folder. Which folder you pick as root is beyond the scope of this article, and is going to depend on which version of which linux distribution you’re using, and how the packagers have decided to setup Apache, nginx, etc on that system. What is in scope of this article is where in the Magento source tree you choose to put your root web folder.

Specifically, Magento 2 ships with two index.php files.

/path/to/magento2/index.php
/path/to/magento2/pub/index.php

One is at the absolute top level of Magento 2’s distribution folder. The second is inside the “pub” folder. There’s also separate but similar .htaccess files for each of theseindex.php files.

The file you want to use for your Magento system’s root folder is pub. This is a modern PHP framework convention, and is one layer in a layered approach to protecting your PHP and configuration files from being exposed to the world. However, Magento still ships with the root level index.php because many hosting companies make changing this web root difficult and/or impossible.

If you’re smart enough to seek out online articles about your ecommerce programming framework, you’re smart enough to know that the pub folder is the one you want to use. However, which folder you choose will have consequences for the paths to your front end asset files. Unless we explicitly state otherwise, assume we’ve setup apache to point to thepub folder.

Magento 2 Modes

The second bit of Magento infrastructure we’re interested in is Magento’s “modes”. You may already be familiar with developer mode. If you add the following to your.htaccess file

SetEnv MAGE_MODE developer

You’ll be running Magento in developer mode. In developer mode, Magento’s much more strict about PHP errors, will display raw exceptions, and generally do things that make it easier to develop Magento 2 modules. There are two other modes Magento can run in. If you haven’t set an explicit MAGE_MODE, you’re running in default mode. The other explicit Magento mode is production mode.

If you’re running Magento 2 in production mode, Magento 2 will do everything possible to hide errors and exceptions from end users. Magento 2 will also turn off most (if not all) of its magic code generation. If you’ve read through our series on the object system you know Magento 2 automatically creates many of the boilerplate classes needed for its object system features. There are similar code generation systems for Magento’s front end file serving. These front end file generators will also be turned off when Magento’s running inproduction mode. We’ll be starting this article in developer mode, and discuss the consequences of production mode as we go.

For completionists — Magento’s default mode is a weird combination of productionand developer mode. A better name for it might be demo mode. Similar to productionmode, default mode will suppress many of Magento’s unfriendly-but-useful technical error messages, but will automatically generate code.

I imagine default mode came out of a meeting where folks were concerned with the non-tech-savvy deploying a stock system without understanding production mode. Generally speaking, if you’re working with Magento you’ll want to run in developer mode, and deploy to production mode. The default is a weird hybrid that serves no one well.

Serving a Frontend Asset File

Magento infrastructure covered, we’re ready to discuss how Magento serves its javascript and CSS files. As mentioned above, we’re starting with a system whose web root is pointed at the pub folder, and one that’s running in developer mode.

If you view the source of your Magento homepage, you’ll see source code that looks like the following.

<link rel="stylesheet" type="text/css"  media="all" href="http://magento.example.com/static/frontend/Magento/luma/en_US/mage/calendar.css" />
<script  type="text/javascript" src="http://magento.example.com/static/_requirejs/frontend/Magento/luma/en_US/requirejs-config.js"></script>

While the wonders of modern front end systems often remove the need for manually adding javascript and CSS files yourself, their abstractions must, at some level, create HTML to include an HTTP resource on that page. i.e. The following URLs

http://magento.example.com/static/frontend/Magento/luma/en_US/mage/calendar.css
http://magento.example.com/static/_requirejs/frontend/Magento/luma/en_US/requirejs-config.js

need to return plain old javascript and CSS. If you load them in your browser or via a command line program like curl, you should see a file returned

$ curl -i 'http://magento.example.com/static/frontend/Magento/luma/en_US/mage/calendar.css'
HTTP/1.1 200 OK
Date: ...

/**
 * Copyright © 2015 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
.ui-datepicker {
    display: none;
    z-index: 999999 !important;
}
/* ... */

If you take a closer look at that URL

http://magento.example.com/static/frontend/Magento/luma/en_US/mage/calendar.css

you’d expect to find a file at

/path/to/magento2/pub/static/frontend/Magento/luma/en_US/mage/calendar.css

If your system is setup correctly, that expectation is correct.

Pretty straight forward — but here’s the first Magento 2 twist. Remember how we said Magento has two index.php files? If you change your web server configuration to point to the base index.php file (not the one in pub/), your request will now return an HTTP status 404 Not Found.

$ curl -I 'http://magento.example.com/static/frontend/Magento/luma/en_US/mage/calendar.css'
HTTP/1.1 404 Not Found

If you go back and load your Magento home page, you’ll notice the href has changed toinclude the pub folder as a prefix

http://magento.example.com/pub/static/frontend/Magento/luma/en_US/mage/calendar.css

Magento is smart enough to recognize where its web root is, and generate the correct URL.However, this does have consequences for third party Magento developers — mainly that adding javascript and CSS outside of Magento 2’s abstractions is no longer a reliable way to distribute simple code. i.e., in Magento 1, it was common for many extension vendors to simply add a folder to the top level js folder

/path/to/magento/js/packagename_vendor/...

and then hard code link and script tags to this folder

<script src="/js/packagename_vendor/file.js" ... >
<link href="/js/packagename_vendor/file.css" ... >

Since Magento 2 has two possible root folders, this is no longer a reliable way to ship code for wider use.

Also, despite this, it’s also likely that single merchant integrations (i.e. an offshore team hacking together a Magento 2 system for someone) will include javascript/css like this that’s incompatible with one of the two root folders.

If you’re cleaning up a Magento 2 system in the wild — be careful suggesting merchants just switch their cart over to serving files out of pub/. While this is “the right” thing to do, there may be hacked in javascript and CSS files that rely on the “less right” configuration.

Magento 2 Static Asset Serving

So, assuming you’ve re-re-configured your web server to properly point to the pub folder, and you’re still running in developer mode, lets return to our static asset file

http://magento.example.com/static/frontend/Magento/luma/en_US/mage/calendar.css

Depending on your system configuration (locale, theme, etc.) your URL may looks slightly different

http://magento.example.com/static/frontend/Magento/themename/en_UK/mage/calendar.css

However, the non-domain portion of the URL should refer to an actual static file in pub. The rest of this article will assume a theme of luma and a locale of en_US.

Let’s open up our calender.css file and add a comment (/* I am learning how to serve CSS files in Magento 2 */) to the top

/* File: pub/static/frontend/Magento/luma/en_US/mage/calendar.css */

/* I am learning how to serve CSS files in Magento 2 */

/**
 * Copyright © 2015 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
.ui-datepicker {/* ... */}
/* ... */

Save the file, and load the calendar.css URL in your browser or via curl. You should see your new comments at the top of the file

$ curl -i 'http://magento.example.com/static/frontend/Magento/luma/en_US/mage/calendar.css' 

HTTP/1.1 200 OK
...

/* I am learning how to serve CSS files in Magento 2 */

/**
 * Copyright © 2015 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
.ui-datepicker {
    /*...*/
}
/*...*/

So far, nothing out of the ordinary. Next up, let’s rename the calendar.css file

$ mv pub/static/frontend/Magento/luma/en_US/mage/calendar.css pub/static/frontend/Magento/luma/en_US/mage/calendar.bak

Now, if I asked you what would happen if we tried to download the file again, you’d tell me that the web server would return a 404 Not Found error. However, if you do try to access the file again

$ curl -i 'http://magento.example.com/static/frontend/Magento/luma/en_US/mage/calendar.css'
HTTP/1.1 200 OK
//...

you’ll find the file’s still there. Even more confusing, if you check the file system, you’ll find the calendar.css file has been restored!

$ find pub/static -name 'calendar.css'
//...
pub/static/frontend/Magento/luma/en_US/mage/calendar.css

So what gives? When you’re running your system in developer mode, if Magento can’t find a static asset file, it will automatically copy or symlink that file from that file’s source module. If you don’t believe us, setup your system to run in production mode by editing the .htaccess file.

#File: .htaccess

SetEnv MAGE_MODE production

Then, remove the calendar.css file

$ rm pub/static/frontend/Magento/luma/en_US/mage/calendar.css

Finally, with Magento’s mode set to production, try to download the calendar.css file again.

curl -I 'http://magento.example.com/static/frontend/Magento/luma/en_US/mage/calendar.css'
HTTP/1.1 404 Not Found
//...

Instead of generating a new calendar.css file, Magento 2 returns a 404 Not Found error. As previously mentioned, when Magento’s running in production mode, it will notautomatically generate files. This is a smart security precaution on the part of the core team — code generation opens all sorts of surface area for system attacks. By enabling Magento devops folks to turn off code generation, Magento has closed off an entire class of attack vectors into the application.

For Every Answer, More Questions

A few questions that might immediately pop into your mind are

  1. How do we ensure all the files for production mode are generated?
  2. What sort of magic is Magento doing to enable the developer mode file creation?
  3. How can our own modules take advantage of this?

The first question is the easiest to answer. Magento 2’s command line application ships with a command named setup:static-content:deploy. If you run this command, Magento will run through every module in the system

$ php bin/magento setup:static-content:deploy
Requested languages: en_US
=== frontend -> Magento/blank -> en_US ===
//...
Successful: 845 files modified
---
New version of deployed files: 1450920725

After running the above command, you’ll find that calendar.css has been restored to the file system.

The basic idea is that deployment of a Magento 2 system should only happen via a formalized deployment process, and part of that deployment will always include running this command in production — either manually, or via your deployment system. This is similar to the asset pipeline in the Symfony framework.

As for how Magento performs the magic of creating files in developer and defaultmode, the answer lies in the .htaccess file found in the static folder

#File: pub/static/.htaccess
#...
<IfModule mod_rewrite.c>
    RewriteEngine On

    # Remove signature of the static files that is used to overcome the browser cache
    RewriteRule ^version.+?/(.+)$ $1 [L]

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-l

    RewriteRule .* ../static.php?resource=$0 [L]
</IfModule>
#...

This is a separate .htaccess file from Magento’s main .htaccess file. The above rewrite rule basically says

If the requested file does not exist on the system, redirect the request to the static.php file one directory up, with the file path included as a resource parameter.

i.e., the following request

http://magento.example.com/static/frontend/Magento/luma/en_US/mage/calendar.css

is identical to this request

http://magento.example.com/static.php?resource=frontend/Magento/luma/en_US/mage/calendar.css

While it’s beyond the scope of this article — the static.php file contains a mini-application built using Magento’s framework code.

#File: pub/static.php     
/**
 * Entry point for static resources (JS, CSS, etc.)
 *
 * Copyright © 2015 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */

require __DIR__ . '/../app/bootstrap.php';
$bootstrap = \Magento\Framework\App\Bootstrap::create(BP, $_SERVER);
/** @var \Magento\Framework\App\StaticResource $app */
$app = $bootstrap->createApplication('Magento\Framework\App\StaticResource');
$bootstrap->run($app);

This “Static Resource” application is the one that’s responsible for copying or symlink-ing files back to their original module folder. If you’re curious in tracing this application’s execution, you can start in the launch method here

#File: vendor/magento/framework/App/StaticResource.php
public function launch()
{
    // disabling profiling when retrieving static resource
    \Magento\Framework\Profiler::reset();
    $appMode = $this->state->getMode();
    if ($appMode == \Magento\Framework\App\State::MODE_PRODUCTION) {
        $this->response->setHttpResponseCode(404);
    } else {
        $path = $this->request->get('resource');
        $params = $this->parsePath($path);
        $this->state->setAreaCode($params['area']);
        $this->objectManager->configure($this->configLoader->load($params['area']));
        $file = $params['file'];
        unset($params['file']);
        $asset = $this->assetRepo->createAsset($file, $params);
        $this->response->setFilePath($asset->getSourceFile());
        $this->publisher->publish($asset);
    }
    return $this->response;
}    

As for the final question

How can our own modules take advantage of this?

That’s one that will need to wait for next time. In this article, we’ve introduced the concept of generated front end files, and how file generation interacts (or doesn’t) with both Magento’s production, default, and development modes and the index.php andpub/index.php deployment approaches. Once that meal digests, you’ll be ready for our next article, which covers adding front end asset files in your own Magento 2 modules.

Introduction to Magento 2 — No More MVC-alanstorm

Today we’re going to run through a basic “Hello World” example in Magento 2. We’ll setup a new URL, and show you how to create a phtml template for that URL. Along the way we’re going to be talking about Design Patterns™, design patterns, and how Magento got here. However, at the heart of this article are a set of simple, repeatable, steps to follow that will help you get started with module development in Magento 2.

For the impatient we’ve placed a completed module on GitHub, but we highly recommend working your way through the creation process yourself. The comments below are good for short questions, or links to your longer Stack Exchange questions.

One big surprise for Magento 1 developers is that Magento 2 is not a Model, View, Controller system. While Magento 2’s architects haven’t staked out a particular label for their system, it’s closer to a Model, View, ViewModel (MVVM) system.

In Magento 2, when you request a URL

http://magento.example.com/hello_mvc/hello/world

The system will route the request to a controller object’s execute method, similar to the way an MVC system will route a URL to a controller object’s action method. However, unlike a traditional MVC system, this controller object is only responsible for

  • Deciding which page layout to use
  • Handling saving data from POST requests
  • Either
    • Telling the system to render the HTTP response
    • Or redirecting users to the next/previous page

You’ll notice there’s no mention of setting variables in views. That’s because it’s the responsibility of each view to fetch its own information from the model layer, request object, or any other external system. Magento breaks up an HTML page into sections called containers. Each container holds a nested tree of objects called blocks. Each block object has a phtml template file which specifies the HTML a block object renders.

In MVVM parlance, a Magento block object is the View Model. The block object will do any programmatic reading of the CRUD models, request object, external systems, etc. TheView is the phtml template file. It only interacts with the ViewModel (the block object).

As a module developer, if you want to create a new URL in Magento, you need to

  • Configure a module to tell Magento which controller it should use for a URL
  • Configure a module to tell Magento which Block objects should be added to the system

If that’s all a little overwhelming, don’t worry. The rest of this introductory article will walk you through each step that’s necessary in setting up a Magento 2 “Hello World” module. After you run through it a few times, you’ll start to get a hang of the new terminology, and what code needs to go where.

A Note on “Cache Clearing”

Magento 2, like most modern frameworks, uses a number of different cache files to speed up slow running operations. These caches are meant to make a production system run faster — but this is often at the expense of Magento not picking up changes to configuration or source files.

It’s often necessary to clear your cache when creating a new feature on a Magento 2 system. You can do this via the bin/magento CLI application (cache:clean), the GUI (System -> Cache Management), or by deleting the cache files (in var/cache/ — unless you’re running an alternative cache storage engine).

In addition to these cache files, Magento 2 also generates a number of boilerplate classes for you on-the-fly. These files are generated in the var/generate folder. When you change certain configurations or code files, it’s often necessary to regenerate these generated code files. There are not, at this time, any features (CLI or GUI) for doing this. The only way to clear the generated code folders is to manually delete the files in var/generation.

We’ll try to make a note whenever you need to clear your cache and generated code files — but if you’re running into something you think should be working, but isn’t working, it’s always worth clearing your cache and generated code files, and then trying to reload the page/re-run the command. A quick

rm -rf /path/to/magento/var/generation/*
rm -rf /path/to/magento/var/cache/*    

is often all you need to get back on the right path.

Magento 2 Hello World Module

Our goal today is to

Setup a new page at the URL http://magento.example.com/hello_mvc/hello/worldthat displays the message Hello World inside the default Magento 2 frontend/cart theme

From a high level, the steps we’ll need to take to accomplish this are.

  • Create a Magento module to hold our code
  • Configure this module with a route for a URL
  • Create a class for our controller object
  • Create a full action name layout handle XML file
  • Use the full action name layout handle XML file to add a new block to the content container
  • Create a template for our block

Some of the terminology above we’ve already talked about. Some of it we haven’t. Some of it may seem familiar from Magento 1, but enough has changed that you may want to blank your mind on how things worked in Magento 1. While there’s a lot of places in Magento 2 where your Magento 1 knowledge will be invaluable, assuming something works the same as it did in Magento 1 may not lead to the outcome you’re looking for.

Creating a Magento 2 Module

In Magento 2, a module allows programmers to add new code to the system in a structured way. When you add your code to Magento via a module, the system code knows where to look for it. Modules also define rules for using PHP namespaces to avoid conflicts with other developer’s code. Modules are first class citizens in Magento 2 — the core team itself uses modules to implement all the frontend-cart and backend-admin features.

Modules live in the app/code folder. Every module in Magento 2 has unique name that’s made up of two parts. The first part is a word that describes the company, individual, or group that built the extension. This is sometimes called the “vendor” namespace. The second part of a module’s name is a word that describes what the module does.

For example, one module that ships with Magento 2 is named Magento_Contact. The first part, Magento, describes the group that built the extension (the Magento core team). The second part, Contact, describes what the extension does (adds a contact form to Magento).

For our tutorial we’re going to create a module named Pulsestorm_HelloWorldMVVM. To create this module, create the following file, with the following content, in your Magento system

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/etc/module.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd">
    <module name="Pulsestorm_HelloWorldMVVM" setup_version="0.0.1" />
</config>

All modules are located in the app/code folder, in a folder structure based on the module’s full name (Pulsestorm_HelloWorldMVVMPulsestorm/HelloWorldMVVM).

A Magento 2 module will contain many XML configuration files, and configuration files live in etc. This module.xml file is the main module configuration file. This file is what Magento’s core code looks for when scanning the system for modules.

The root <config/> node is just boilerplate.

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/etc/module.xml -->    
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../lib/internal/Magento/Framework/Module/etc/module.xsd">

Magento 2 uses XSD schema files to validate the contents of all module configuration files. Just copy and paste this part every-time. It doesn’t change.

The part of this configuration file we’re interested in is

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/etc/module.xml -->    
<module name="Pulsestorm_HelloWorldMVVM" setup_version="0.0.1" />

The <module/> node tells Magento we want to add a module to the system. The nameattribute tells Magento what that module’s name is, and the setup_version attribute tells Magento which version of our module this is. This version node is important to Magento’s setup-resource/migration system, but we won’t be covering that today.

With the above in place, there’s one last step we need to take, and that’s adding our module to the global module list at app/etc/config.php. If you look at this file

#File: app/etc/config.php
<?php
return array (
  'modules' => 
  array (
    'Magento_Store' => 1,
    'Magento_AdvancedPricingImportExport' => 1,
    'Magento_Directory' => 1,
    //...
  ),
);            

You’ll see a PHP include file with a long list of modules. This file is here so the core code doesn’t need to do a literal directory scan of app/code/* on every page request. Let’s add our module to the end of the array

#File: app/etc/config.php
<?php
return array (
  'modules' => 
  array (
    'Magento_Store' => 1,
    'Magento_AdvancedPricingImportExport' => 1,
    'Magento_Directory' => 1,
    //...
    'Pulsestorm_HelloWorldMVVM'=> 1
  ),
);            

With the above in place, we have a fully configured module!

Merchant Beta Changes Update: Quick interruption here from editorial. The Magento core team made some significant changes to Magento’s module architecture after the merchant beta, and if you’re using a system built on the official Magento 2.0 release, then there’s one more step you’ll need to take. In addition to the files mentioned above, every module also needs a registration.php file that looks like this

#File: app/code/Pulsestorm/HelloWorldMVVM/registration.php
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Pulsestorm_HelloWorldMVVM',
    __DIR__
);

These files are identical for all modules, with the exception of thePulsestorm_HelloWorldMVVM text. This should be your module’s full name (combining the package name of Pulsestorm and the vendor name of HelloWorldMVVM).

Why is this here here? It’s part of how Magento identifies which modules are installed into a system. This replaces the old Magento 1 app/etc/module files, and takes the ultimate source of truth for module presence away from app/etc/config.php.

Installation Troubleshooting

There’s a few different ways to check if a module’s installed correctly. One is to use the CLI application’s module:status command

$ php bin/magento module:status
List of enabled modules:
Magento_Store
Magento_AdvancedPricingImportExport
//...
Pulsestorm_HelloWorldMVVM

List of disabled modules:
//...    

This command will list all the installed and uninstalled modules in the system.

Another way is to check the “Disable Modules Output” section of the admin console at

Stores -> Configuration -> Advanced -> Advanced -> Disable Modules Output

Although unrelated to enabling modules, this section does contain a list of every module in the system.

One final note on module creation. After installing a new module into the system, you may see the following error

Please upgrade your database: Run "bin/magento setup:upgrade" 
from the Magento root directory. 
The following modules are outdated:
//...

Without getting too deep into the reasons for it, this is a warning from Magento’s setup resource migration system. It’s telling you that the configured version of the module does not match the last version ran by the setup resource migration scripts.

Don’t worry if that didn’t make sense — just run the following command from the terminal

$ php bin/magento setup:upgrade    

and the error will go away.

Adding a Controller Action

With a module created, that’s one item crossed off our list

  • Create a Magento module to hold our code
  • Configure this module with a route for a URL
  • Create a class for our controller object
  • Create a full action name layout handle XML file
  • Use the full action name layout handle XML file to add a new block to the content container
  • Create a template for our block

Next, we’re going to tackle configuring a route for a URL, and creating a class for our controller object. Before we do that, there’s a quick step we’ll want to take that will make our lives much easier.

One of the neat things about Magento 2 is many features that only existed in Enterprise Edition have been brought to Community Edition. This includes the full page caching feature. While this is a great feature for production systems, it’s a bit of a pain in the behind to have turned on when you’re setting up a new URL. You can turn this feature off by logging into the admin and going to

System -> Cache Management

and clicking the checkbox next to Page Cache, choosing “Disable” from the dropdown menu, and then clicking the Submit button.

If you choose to leave full page caching on, you’ll need to delete the full page cache between every request for the rest of this tutorial. These cache files are in the

var/page_cache

folder. This is a separate cache system from Magento’s normal cache and generated code folders.

With full page caching off, we’re ready to proceed.

Routing in Magento 2

We’re going to make Magento 2 respond to the following URL

http://magento.example.com/index.php/hello_mvvm/hello/world

The index.php portion is optional — if you have mod_rewrite (or your web server’s equivalent) enabled, this URL is the same as

http://magento.example.com/hello_mvvm/hello/world

In Magento 2, each individual module can claim a “front name”. The “front name” is the first segment of the URL — in our case that’s hello_mvvm. When a module “claims” a front name, that is its way of saying

Hello Magento systems code — if you see any URLs that start with /hello_mvvm, I have controllers for them

To have our module “claim” the hello_mvvm front name, add the following configuration file.

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/etc/frontend/routes.xml -->
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../../../lib/internal/Magento/Framework/App/etc/routes.xsd">
    <router id="standard">
        <route id="hello_mvvm" frontName="hello_mvvm">
            <module name="Pulsestorm_HelloWorldMVVM" />
        </route>
    </router>
</config>    

This new routes.xml file is where we add the configuration that tells Magento our module wants to claim a front name. Notice that this file lives in a sub-folder named frontend. The Magento 2 system allows developers to create multiple “application areas”. Areas control things like what sessions load, what access control rules get checked, and which configuration files get loaded. The frontend area is the cart application. If that didn’t make complete sense yet, don’t worry, just put your file in the specified location and you’ll be all set.

As for this file’s contents, the top level (after the root node) <router/> node contains all our routes.

<router id="standard">
    <!-- ... -->
</router>

The id="standard" attribute is a little confusing. If you’re setting up a URL for the frontend area, you’ll want to use the standard router. If you’re setting up a URL for the admin console application, you’ll want to use id="admin" instead. The reasons for this are mostly historical, and we’ll (hopefully) cover them in a future article.

Inside the <router/> tag, we’ll find our individual <route/> tags

<route id="hello_mvvm" frontName="hello_mvvm">
    <module name="Pulsestorm_HelloWorldMVVM" />
</route>

Each individual <route/> node tells Magento we want to claim a particular front name. The value of the frontName attribute is the actual text of the URL’s first segment we want to claim. i.e. frontName="hello_mvvm" tells Magento we want to claim URLs that look like

http://magento.example.com/hello_mvvm/*

The id attribute of the route node uniquely identifies this node across all Magento 2 modules installed in the system. This id is also sometimes called the route’s name. By convention, and 99.9% of the time, this id value should match the frontName. If you don’t know why that’s true — you don’t need to know why that’s true. Just make them the same and you’ll be happier for it.

Within the route node you’ll find

<module name="Pulsestorm_HelloWorldMVVM" />   

This name attribute node should be the name of our module. With the above configuration in place, we’re ready to move on to creating our controller file.

Creating a Controller File

Magento 2 uses a traditional PHP “transform the URL into a controller class name” approach to controller naming. Let’s take another look at our URL

http://magento.example.com/hello_mvvm/hello/world

To come up with a controller class name, Magento 2 will look at the second and third URL segments (hello, and world above, respectively). That is

  1. The class name starts with Pulsestorm\HelloWorldMVVM since we configuredPulsestorm_HelloWorldMVVM to claim the hello_mvvm front name.
  2. Then we append Controller, because we’re defining a controller (Pulsestorm\HelloWorldMVVM\Controller)
  3. Then Magento appends the second URL segment (hello) with an upper casing (Pulsestorm\HelloWorldMVVM\Controller\Hello)
  4. Then Magento appends the third URL segment (world) with an upper casing (Pulsestorm\HelloWorldMVVM\Controller\Hello\World)

That gives us a final controller name ofPulsestorm\HelloWorldMVVM\Controller\Hello\World. Let’s create the class file now!

#File: app/code/Pulsestorm/HelloWorldMVVM/Controller/Hello/World.php
<?php
namespace Pulsestorm\HelloWorldMVVM\Controller\Hello;
class World extends \Magento\Framework\App\Action\Action
{
    public function execute()
    {
        echo '<p>You Did It!</p>';
        var_dump(__METHOD__);
    }    
}

The controller’s file location is dictated by its full name (per the PSR-0 standard)

Class Name: Pulsestorm\HelloWorldMVVM\Controller\Hello\World;
   app/code/Pulsestorm/HelloWorldMVVM/Controller/Hello/World.php

The base class Magento\Framework\App\Action\Action is the standard base class for frontend controllers, (the name “action” is derived from the historical name “action controller”).

In Magento 2, each controller has one, and only one, entry point. That’s the executemethod. This is a step Magento 2’s architects took to help avoid conflicts with a large development teams all editing the same controller file for different features.

With the above in place, try loading the following URL in your Magento system (after clearing your generated code and configuration folders, of course)

http://magento.example.com/hello_mvvm/hello/world

If you’ve followed the steps above correctly, you should see the following output.

You Did It
string 'Pulsestorm\HelloWorldMVVM\Controller\Hello\World::execute' (length=57)

Congratulations — you just created your first Magento 2 controller!

Passing off to the View

Earlier we stated a controller’s job was

  • Deciding which page layout to use
  • Handling saving data from POST requests
  • Either
    • Telling the system to render the HTTP response
    • Or redirecting users to the next/previous page

Today we’re only interested in the Telling the system to render the HTTP responseresponsibility. In Magento 2, if you want a controller to render an HTML page, you need to have the controller’s execute method return a “page” object. This is a three step process

  1. You need to inject a “page factory” object via automatic constructor dependency injection
  2. Use that page factory object to create a page
  3. Return the created page

That’s a lot less complicated than it sounds. Just change your controller’s file so it matches the following

<?php
namespace Pulsestorm\HelloWorldMVVM\Controller\Hello;
use Magento\Framework\View\Result\PageFactory;
use Magento\Framework\App\Action\Context;

class World extends \Magento\Framework\App\Action\Action
{
    protected $pageFactory;
    public function __construct(Context $context, PageFactory $pageFactory)
    {
        $this->pageFactory = $pageFactory;
        return parent::__construct($context);
    }

    public function execute()
    {        
        var_dump(__METHOD__);
        $page_object = $this->pageFactory->create();;
        return $page_object;
    }    
}

We’ll get to what we did in this file in a second, but try (after clearing your cache andclearing the contents of var/generation) reloading your page. You may be confused, as all you’ll see is the method name from our var_dump statement

string 'Pulsestorm\HelloWorldMVVM\Controller\Hello\World::execute' (length=57)

However, if you view source on this page, you should see something like the following

<html >
    <head >
                <meta charset="utf-8"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="description" content="Default Description"/>
<meta name="keywords" content="Magento, Varien, E-commerce"/>

The problem here isn’t incorrect code — it’s that we haven’t instructed the layout system what it needs to do on the http://magento.example.com/hello_mvvm/hello/worldpage. Because of that, Magento’s rendered the outer HTML skeleton, but no content. We’ll get to how we instruct the layout system what it needs to do, but only after we explain what we’ve done above.

The first two changes to the controller file are

use Magento\Framework\View\Result\PageFactory;
use Magento\Framework\App\Action\Context; 

These lines aren’t strictly needed, but they will allow us to use the short class namePageFactory and Context for these two classes below. If you’re not familiar with PHP namespace, our short primer is a great place to start.

Next up is the constructor

protected $pageFactory;
public function __construct(Context $context, PageFactory $pageFactory)
{
    $this->pageFactory = $pageFactory;
    return parent::__construct($context);
}

This is Magento 2’s automatic constructor dependency injection in action — if you’re not familiar with it you may want to work your way through our object manager series. The short version is, Magento has some magic code that will automatically create objects for you if you type hint them in a constructor. So, this is how we create our PageFactoryobject, and assign it to our new pageFactory property.

Even if you’re familiar with automatic constructor dependency injection, the $contextvariable above may confuse you. This is here because it’s also in the parent object’s(Magento\Framework\App\Action\Action) constructor, and we need to callparent::__construct to make sure any work in the parent constructor still happens.

Finally, everything comes together in the execute method

public function execute()
{        
    var_dump(__METHOD__);
    $page_object = $this->pageFactory->create();;
    return $page_object;
}  

Here we use the PageFactory object to create a page object, and then return that page object.

Creating the View

If we come back to our to do list

  • Create a Magento module to hold our code
  • Configure this module with a route for a URL
  • Create a class for our controller object
  • Create a full action name layout handle XML file
  • Use the full action name layout handle XML file to add a new block to the content container
  • Create a template for our block

We’ll see there’s only three steps left. Let’s get to it!

For end-user-programmers (us!) Magento’s Page Layout system is controllable via an XML based domain specific language. In less engineering speak, this means we can create XML files with a list of instructions for the layout system. Covering this system in full is beyond the scope of this (or even a new single) article, but if you have questions the comments andStack Exchange are a great place to ask.

We’re going to blitz through this to get a working example in place, and then cover what we did afterwards.

Create the following file in the following location.

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/layout/hello_mvvm_hello_world.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">        
    <referenceBlock name="content">
        <block 
            template="content.phtml" 
            class="Pulsestorm\HelloWorldMVVM\Block\Main" 
            name="pulsestorm_helloworld_mvvm"/>
    </referenceBlock>
</page>    

Then, add the following PHP Block class.

#File: app/code/Pulsestorm/HelloWorldMVVM/Block/Main.php
<?php
namespace Pulsestorm\HelloWorldMVVM\Block;
use Magento\Framework\View\Element\Template;

class Main extends Template
{    
    protected function _prepareLayout()
    {

    }
}

and finally, add the following phtml template.

#File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/templates/content.phtml
<h1>Hello World</h1>

With the above in place, clear your cache and generated code folders, reload the page, and you should see our Hello World title, surrounded by the Magento design.

What Just Happened

The next few sections cover everything we just did above — this is probably the most confusing part of the article so far. We’re introducing new terminology and redefining old Magento 1 terminology. You don’t need to fully comprehend these sections to proceed, but they’re a great jumping off point for deeper dives into Magento’s core systems code.

Even if the next few sections are a little over your head, you’ll definitely want to skip ahead to the View/View Model section near the end.

The Full Action Name Layout Handle XML File

Let’s start with that XML file we created

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/layout/hello_mvvm_hello_world.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">
    <referenceBlock name="content">
        <block 
            template="content.phtml" 
            class="Pulsestorm\HelloWorldMVVM\Block\Main" 
            name="pulsestorm_helloworld_mvvm"/>
    </referenceBlock>
</page> 

This is something called a full action name layout handle XML file. As we mentioned, developers communicate with Magento’s Page Layout system via instructions placed in an XML file. This is that XML file. A “layout handle” is sort of like an event or notifier for the Page Layout System. Certain handles “fire” on each page, and these handles tell Magento which Layout Handle XML Files it should load.

Every controller action page fires a full action name handle. A full action name is a string that combines the configured <route/> ID (hello_mvvm, usually identical to the front name) and the second and third URL segments (hello and world). In our case, the full action name is hello_mvvm_hello_world.

So, when we create a file named hello_mvvm_hello_world.xml, we’re telling Magento

If the handle hello_mvvm_hello_world is issued, use the layout instructions in this file.

As for this file’s placement in the module folder hierarchy

app/code/Pulsestorm/HelloWorldMVVM/view/frontend/layout/hello_mvvm_hello_world.xml

Every module in Magento has a view folder for view related files. Under view are folders for each individual application area (frontend above), and within the area folder are folders for specific asset types (layout files, templates files, etc.)

Finally, there’s the actual instructions in this layout file.

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/layout/hello_mvvm_hello_world.xml -->
<referenceBlock name="content">
    <block 
        template="content.phtml" 
        class="Pulsestorm\HelloWorldMVVM\Block\Main" 
        name="pulsestorm_helloworld_mvvm"/>
</referenceBlock>

The above XML instructions are roughly equivalent to the following pseudo code.

//pseudo code -- does not work
$our_view_block = 
    $layout->createNewBlockWithClass('Pulsestorm\HelloWorldMVVM\Block\Main')
$our_view_block->setName('pulsestorm_helloworld_mvvm');
$out_view_block->setTemplate('content.phtml');

$layout->addBlockToContentContainer($our_view_block);

Or, in plain english

Magento, please create a block object using the classPulsestorm\HelloWorldMVVM\Block\Main. This block should render with the template in the content.phtml file, and let’s give the block a globally unique name ofpulsestorm_helloworld_mvvm

A block’s name should be a globally unique string, and can be used by other code to get a reference to our block object. Speaking of blocks and templates, they’re up next!

Creating a Block Class

As mentioned previously, a Magento 2 page layout is a collection of nested containers and blocks. Above, using the full action name layout handle XML file, we told Magento we wanted to insert a Pulsestorm\HelloWorldMVVM\Block\Main block. When we created the following file, we were defining that block class.

#File: app/code/Pulsestorm/HelloWorldMVVM/Block/Main.php
<?php
namespace Pulsestorm\HelloWorldMVVM\Block;
use Magento\Framework\View\Element\Template;

class Main extends Template
{    
    protected function _prepareLayout()
    {

    }
}

A Magento block class is responsible for rendering an HTML string that’s a portion of the entire HTML page. The base block class above,Magento\Framework\View\Element\Template, is Magento’s base template class. When a block extends from this class, it will render the HTML contents in its template property. We set this template property (content.phtml) in the full action name layout handle XML file.

A block’s file path, like all PHP classes in Magento, is determined by the rules of the PSR-0 autoloader.

app/code/Pulsestorm/HelloWorldMVVM/Block
         Pulsestorm\HelloWorldMVVM\Block

Creating a Template File

Most blocks in Magento 2 are template blocks — this means they render a phtml template. When we added the following attribute

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/layout/hello_mvvm_hello_world.xml -->    
<block <!-- ... -->
    template="content.phtml"         
<!-- ... -->        

We told Magento we wanted our block to use the content.phtml template file.

#File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/templates/content.phtml
<h1>Hello World</h1>

Template files are considered view assets, so they belong in a module’s view folder. Like routes, and layout files, they’re specific to a Magento area (in this case, frontend), and they belong in the templates subfolder since, well, they’re templates!

You can, if you wish, organize templates into subfolders

<!-- File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/layout/hello_mvvm_hello_world.xml -->    
<block <!-- ... -->
    template="some/sub/folder/content.phtml"         
<!-- ... -->          

View/View Model

With that, we’ve finished up our entire to do list

  • Create a Magento module to hold our code
  • Configure this module with a route for a URL
  • Create a class for our controller object
  • Create a full action name layout handle XML file
  • Use the full action name layout handle XML file to add a new block to the content container
  • Create a template for our block

There is, however, one last thing to cover. Earlier we described Magento as a Model, View, View Model system, with Magento blocks acting as the View Model, and the phtmltemplate acting as the view. Here’s how that works.

Open up your block object, and add the following to the _prepareLayout method.

#File: app/code/Pulsestorm/HelloWorldMVVM/Block/Main.php
protected function _prepareLayout()
{
    $this->setMessage('Hello Again World');
}

Then, in your phtml file, add the following

#File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/templates/content.phtml
<h1><?php echo $this->escapeHtml($this->getMessage()); ?></h1>

Reload your page, and you should see your Hello Again World message.

As a view developer, it’s your job to fetch or calculate any data needed for the template. You can do this via the magic set and get methods above, or by defining methods directly on the block object that you can call from the phtml template.

#File: app/code/Pulsestorm/HelloWorldMVVM/Block/Main.php    
public function getGoodbyeMessage()
{
    return 'Goodbye World';
}

#File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/templates/content.phtml    
<h2><?php echo $this->escapeHtml($this->getGoodbyeMessage()); ?></h2>

Block objects also give you access to the request object — try loading the following URL

http://magento-2-july-8.dev/hello_mvvm/hello/world/name/bill

In Magento 2 (similar to other PHP frameworks) key/value pairs after the first three URL segments are considered get parameters. Give the following _prepareLayout method

#File: app/code/Pulsestorm/HelloWorldMVVM/Block/Main.php    
protected function _prepareLayout()
{
    $this->setMessage('Hello');
    $this->setName($this->getRequest()->getParam('name'));
}

and template changes

#File: app/code/Pulsestorm/HelloWorldMVVM/view/frontend/templates/content.phtml      
<h1>
    <?php echo $this->escapeHtml($this->getMessage()); ?>
    <?php echo $this->escapeHtml($this->getName()); ?>
</h1>

<h2><?php echo $this->escapeHtml($this->getGoodbyeMessage()); ?></h2>        

a try.

This is a bit of a shift from traditional PHP MVC systems, where view variables are set from controller actions. By shifting to a Model, View, View Model system, Magento 2 has created more separation between what’s normally considered “business logic” (saving models) and “template logic” (populating variables for HTML generation).

While this move is probably a net positive for a large team of corporate consulting developers, each with their own responsibility, it’s also probably a net negative for full stack developers, who now have one more level of abstraction to traverse when working on new site features and extensions.

This MVVM pattern was possible in Magento 1, but Magento 1’s “Zend Framework” roots (and its too many chefs problem) led to a heavy bias towards a traditional MVC approach, using either the global registry or direct setting of block properties after callingloadLayout. It’s no longer possible to directly set block object properties in Magento 2, and while a registry object still exists in Magento 2, the unofficial recommendation is to avoid its use.

Regardless of whether Magento 2’s patterns elicit a “finally PHP gets it”, or a “WTF is this”, you’ve just successfully created a new landing page and application entry point in Magento 2. You’ve also been exposed to core Magento 2 concepts like clearing the cache, clearing generated files, and the hierarchy of configuration and view files in a Magento 2 module. You’re well on your way to unlocking the mysteries of Magento 2, and all the opportunities that will open for you in the future.

What is the best blog solution for Magento 2 website ?

What is the best blog solution for Magento website ?
What is the best blog solution for Magento 2 website ?

I used few blog extensions of magefan.com , aheadwork and Mageplaza but I uninstall all of them .

Why?

1. Those extensions can make my site load slower because they have a lot of class and files and make site load slower than normal. they have few errors too.

2. Copy article from another blog to my block some time was broken. Because other blogs almost using wordpress and content on them suit with WordPress only.

What is the best solution ?

With my purpose, I think wordpress is best for blog in Magento 😀 . You can write blog in WordPress and create a link from Menu Magento site to wordpress blog. A lot of big sites do the same thing.

You can use my free extension to create new Menu to link in WordPress.
Longtt_Easymenu extension.

Feel free comment and I will answer you in free time.

How To Upload Image In Magento2 , Custom File Uploads in Magento 2 (100% work)

I don’t know why 2 top topics in google don’t work because silly things.

this link :

how to upload image in magento2

It doesn’t work because

$path = $this->_filesystem->getDirectoryRead(DirectoryList::MEDIA)
     
    ->getAbsolutePath('images/'); 
_filesystem used without declaring in contruct method.
In this link https://www.smartiehastheanswer.co.uk/magento2/custom-file-uploads-in-magento2.html
It doesn’t work because don’t have instruction about create form with enctype
$form = $this->_formFactory->create(
[‘data’ => [‘id’ => ‘edit_form’, ‘action’ => $this->getData(‘action’), ‘method’ => ‘post’ ,’enctype’=>’multipart/form-data’]]
);
I will prepare time upload extension have upload function only soon

Knockout Observables for Javascript Programmers

This entry is part 5 of 5 in the series Magento 2: Advanced Javascript. Earlier posts include Magento 2: Javascript Init Scripts, KnockoutJS Primer for Magento Developers, Magento 2: KnockoutJS Integration, and The Curious Case of Magento 2 Mixins. This is the most recent post in the series.

The concept of “observables” can be a little tricky to wrap your head around in Knockout.js. Today we’re going to have a quick tutorial on how observables work outsidethe context of a normal Knockout.js view. We need to do this because Magento 2’s javascript frameworks make heavy use of observables that goes above and beyond what a normal front end developer needs to be aware of.

If you’re trying to think rationally about your Magento systems, you’ll not only need to understand how observables work, but you’ll often need to know how the internals are implemented.

What are Observables

Observables are stand-alone setter/getter objects. From a Magento bootstrapped page, run the following code in your browser’s javascript console. You should also be able to do this outside of Magento in systems that use a global ko variable instead of Magento’s special RequireJS module.

//load the Knockout.js library -- normally you'd do this in a `define`
ko = requirejs('ko');

//create the observable object with a default value
var objectToHoldMyValue = ko.observable('default value');

//fetch that value by calling the observable as a function
console.log( 'Value: ' + objectToHoldMyValue() );
"default value"

//set a new value by calling the observable as a function with an argument
objectToHoldMyValue('new value')

//fetch that value by calling the observable as a function
console.log( 'Value: ' + objectToHoldMyValue() );
"new value"    

As you can see from the above code and comments, the first job of an observable object is to store a value, return a value, and change a stored value. The syntax may be a little weird if you’re not used to the “objects can be anonymous functions” nature of javascript, but this is nothing too crazy. Also — nothing too necessary either, until you consider subscribers.

//subscribe to a change
objectToHoldMyValue.subscribe(function(newValue){
    console.log("The subscriber sees: " + newValue);
});     

The above code sets up a callback that is, in other terms, an event listener (i.e. you’re subscribing to an event). The event you’re subscribing to? A change in value of the observable. If you run the value setting code again.

objectToHoldMyValue('a second new value')
The subscriber sees: a second new value

you’ll see Knockout calls your subscriber method.

Important: Subscribers are only called when a value changes. If you pass in the observable’s current value, Knockout will not call subscriber callbacks

objectToHoldMyValue('a third new value')
The subscriber sees: a third new value

objectToHoldMyValue('a third new value')
[no output, because the subscriber was not called]

While our example is a little silly, in a real program observables let you take actions whenever the value of a variable changes. That’s an incredibly powerful feature.

The Importance of Observables

Observables are what enables Knockout’s “update the model, automatically update the UI” behavior. If you consider a simple Knockout.js data binding (from the official intro tutorial)

<input data-bind="value: firstName" ... />

Behind the scenes, the value data-binding will check if firstName is an observable. IffirstName is an observable, the value binding implementation will setup a subscriber that updates the <input/>. This means whenever a programmer updates the value stored infirstName, the binding’s subscriber runs, and the <input/>‘s value is updated.

Knockout.js does all this behind the scenes. Even if you create a custom binding, Knockout handles setting up the subscriber, and your binding’s update method gets called. There’s no need for you, as a binding developer, to know about subscribers.

The subscribe method feels like something that should be a private API, but since this is javascript and everything’s public by default, developers can and will setup their own subscribers for observables.

Something else that may cause you, as a javascript or PHP programmer, a bit of cognitive dissonance is the lack of empty parameter () parenthesis when someone uses an observable in a data binding

<input data-bind="value: firstName" ... />

When I first started with Knockout.js, the lack of any clear distinction between a regular object property and an observable — at the template level — was a little confusing. Once you understand that observables are just callable javascript objects, and understand that the binding needs to receive this object and not its stored value, things start to make a little more sense. Developers from a civilized language (like ruby), where you don’t need parenthesis to call a method, are now free to laugh.

For Magento 2 Developers

As a Knockout.js developer, you can live a life that’s mostly ignorant of how observables are implemented. Magento 2 developers don’t have this luxury. The UI Component systems make heavy use of observable properties, and also setup their own subscribers.

The good news is: When you see something like

//...
someProp: ko.observable('default value')
//...

you don’t need to panic. The program is just using someProp to store a value.

The bad news is — that observable may have a number of subscribers. These subscribers may come from a Knockout.js template’s data-bind attributes. They may come from Magento core code setting up their own subscribers. You can view the number of callbacks an observer has via the _subscriptions property

console.log(objectToHoldMyValue._subscriptions);
Object
    change: Array[3]
        0: ko.subscription
        1: ko.subscription
        2: ko.subscription

Or peek at a particular callback like this

console.log(
    objectToHoldMyValue._subscriptions.change[1].callback
);

However — you’re at the mercy of your debugger w/r/t to how this information is displayed, and there’s no easy way to tell where a particular subscriber comes from. Also, we’re deep into Knockout.js internals at this point, and relying on this sort of code for anything other than debugging introduces enormous potential for instabilities.

The Curious Case of Magento 2 Mixins

We’re back, with an unexpected fourth article in our Advanced Javascript series.

At the end of our UI Component series, we said

The same applies to system integrators, but if you’re the one building and maintaining a specific Magento instance I’d say it’s safe to also use RequireJS’s selective “monkey patching” abilities to modify the behavior of core javascript objects. Keep an eye on Magento Quickies for an upcoming article on how to do this with Magento’s specialrequirejs-config.js files.

However, when I started researching RequireJS monkey patching and the best way to apply it in Magento 2, I was in for a bit of a surprise. Joe Constant pointed me towards a feature Magento 2 erroneously calls mixins, and all sorts of confusion resulted.

Our end goal today is to teach you a technique for applying a safe method rewrite technique using Magento 2 javascript techniques — one that goes beyond the usual RequireJS tools. To get there though, we’ll need to wade through some very puzzling choices made by Magento’s product and engineering teams.

We’re going to start with a quick programming lesson, continue with some related feature highlights of Magento’s javascript system, and then fall into discussing Magento’s weirdly named mixins system.

What is a Mixin?

A “mixin” is, from one point of view, an alternative to traditional class inheritance. Mixins date back to lisp programming of the early/mid 1980s.

In “classical OOP”, you might define three classes like this

class A
{
    public function foo()
    {
    }
}

class B extends A
{
    public function bar()
    {
    }
}

class C extends B
{
    public function baz()
    {
    }
}

$object = new C;

This hierarchy chain means your instantiated $object will have a baz method, a barmethod, and a foo method.

Mixins offer a different approach. With mixins, you get to pick and choose where you class’s (or object’s) method(s) come from. With an imaginary mixin based language, the above might look like

class A
{
    public function foo()
    {
    }
}

class B
{
    public function bar()
    {
    }
}

class C
{
    mixin A;
    mixin B;

    public function baz()
    {
    }
}
$object = new C;

Notice no classes inherit from one another. Instead, the programmer indicates that class Cshould get methods from class A and class B. The result is more flexibility, at the expense of some ambiguity about how conflicts between different mixins should work, or what the language syntax should be for specifying mixins.

If you’ve ever used PHP Traits, you’ve used a simplified mixin system. In PHP, you need to define explicit traits, and then can combine those traits in your classes. Traits, by themselves, can’t be instantiated as objects. PHP classes, by themselves, can’t be used as traits.

Contrast this with ruby, which allows one module to completely “include” (or “mix in”) another module’s methods.

You’ll also see the idea of multiple inheritance thrown about in mixin discussions. With multiple inheritance, classes still extend other classes, but you’re allowed to have a single class extend more than one class.

Javascript and Mixins, Sitting in a Tree

Unlike classes, there doesn’t seem to be a consensus on how mixin syntax should work across languages. Some languages have explicit mixins while other languages have de-facto mixins due to the nature of their object system.

Javascript is an example of the later. Javascript doesn’t have any native classes. In javascript, you define methods by attaching functions to objects

var foo = {};

foo.someMethod = function(){
    //...
};

Since objects can be easily reflected into, Javascript is a fertile enviornment for developers who want to build systems for creating mixin like objects. One library that offers this sort of functionality (although the word mixin isn’t used) is underscore.js.

Using the extend method in underscore.js, you can have a de-facto mixin-like behavior. Consider the following

var a = {
    foo:function(){
        //...
    }
};

var b = {
    bar:function(){
        //...
    }

}

c = _.extend(a, b);    

The object in c will end up having both a foo, and a bar method. The _.extend method lets us say

Hey, javascript, create a new object with stuff from these other objects

Confusingly, underscore.js has an actual method named mixin, but this method is for adding methods to the underscore JS object itself. Something something cache invalidation and naming things.

Magento uiClass Objects

If you’ve worked your way through the UI Component series, you’re already familiar withMagento’s uiClass objects. These objects also have an extend method. This method looks similar to the underscore.js method

var b = {
    bar:function(){
        //...
    }

}
UiClass = requirejs('uiClass');

// class NewClass extends uiClass
var NewClass = UiClass.extend(b);

// class AnotherNewClass extends NewClass
var AnotherNewClass = NewClass.extend({});

var object = new NewClass;
object.bar();

However, the uiClass extend method is used for something slightly different. The purpose of the uiClass.extend is to create a new javascript constructor function that’s based on an existing javascript constructor function. Above, NewClass won’t get a bar method, but objects instantiated from it will.

While this feels more like straight inheritance, there might be some folks who would call this a mixin due to the uiClass‘s implementation details.

We’re now going to jump to a completely different topic, but keep all of the above in mind.

Magento 2 RequireJS Mixins

Magento 2’s requirejs-config.js files have a feature that’s labeled as a “mixin”. This feature has (almost) nothing to do with traditional computer science mixins, so we’ll continue to refer to them with the “skeptical quotes”.

Despite all the shade we’re throwing at the feature’s name, it’s actually a very good and important feature. A Magento 2 RequireJS “mixin” allows you to programmatically listen for the initial instantiation of any RequireJS module and manipulate that module before returning it.

If that didn’t make sense, a quick sample module should make things clearer. First, create and enable a blank module with the following pestle commands (or use your own module creating methodology).

$ pestle.phar generate_module Pulsestorm RequireJsRewrite 0.0.1
$ php bin/magento module:enable Pulsestorm_RequireJsRewrite
$ php bin/magento setup:upgrade    

If you’re interested in creating a module by hand, or curious what the above pestle command is actually doing, take a look at our Introduction to Magento 2 — No More MVCarticle.

Once that’s complete, create the following requirejs-config.js file

//File: app/code/Pulsestorm/RequireJsRewrite/view/base/requirejs-config.js
var config = {
    'config':{
        'mixins': {
            'Magento_Customer/js/view/customer': {
                'Pulsestorm_RequireJsRewrite/hook':true
            }
        }
    }
};    

and the following RequireJS module/file.

//File: app/code/Pulsestorm/RequireJsRewrite/view/base/web/hook.js
define([], function(){
    'use strict';    
    console.log("Called this Hook.");
    return function(targetModule){
        targetModule.crazyPropertyAddedHere = 'yes';
        return targetModule;
    };
});

With the above in place, if you load the Magento homepage (or any page which uses theMagento_Customer/js/view/customer RequireJS module) you should see the

Called this Hook   

message output to your javascript console. Also, if you examine theMagento_Customer/js/view/customer module via the console, you’ll see it has an extracrazyPropertyAddedHere property

> module = requirejs('Magento_Customer/js/view/customer');
> console.log(module.crazyPropertyAddedHere)
"yes"

With the above code, we changed the object returned by theMagento_Customer/js/view/customer module. If used judiciously, this is an incredibly powerful feature.

What Just Happened?

Before we jump to the implications of this feature, lets talk about the code we just wrote.

//File: app/code/Pulsestorm/RequireJsRewrite/view/base/requirejs-config.js
var config = {
    'config':{
        'mixins': {
            'Magento_Customer/js/view/customer': {
                'Pulsestorm_RequireJsRewrite/hook.js':true
            }
        }
    }
};    

If you’re not familiar with these requirejs-config.js files, they allow individual Magento modules to provide configuration values for RequireJS. You can read more about them in our Magento 2 and RequireJS article.

If you are familiar with them, you may be confused by the mixins configuration key. This is not a part of standard RequireJS. This is a special configuration flag Magento introduced to their RequireJS system. Don’t let the word mixins confused you. As we previously mentioned, this has (almost) nothing to do with the programming concept we discussed earlier. It’s just a poorly chosen name.

The mixins property should be an object of key values pairs. The key (Magento_Customer/js/view/customer) is the object whose creation you want to listen for. The value is another object

{
    'Pulsestorm_RequireJsRewrite/hook':true
}

The key of this second object is the RequireJS module that’s going to be listening. We named this module hook, but that’s not required. You can use any module name you like here.

Next, we defined our RequireJS module.

//File: app/code/Pulsestorm/RequireJsRewrite/view/base/web/hook.js
define([], function(){
    'use strict';    
    console.log("Called this Hook.");
    return function(targetModule){
        targetModule.crazyPropertyAddedHere = 'yes';
        return targetModule;
    };
});

If you’re not familiar with how Magento resolves RequireJS module names to URLs, you may want to read through the front end articles in our Magento 2 for PHP MVC Developersseries.

These “listener/hook” modules are standard RequireJS modules. They should return a callable object (i.e. a javascript function). This is the function Magento will call after loading a RequireJS module. It has a single parameter (targetModule) above. This variable will be a reference to whatever the spied on RequireJS module (Magento_Customer/js/view/customer in our example) returns.

Whatever this callback function returns will be treated by the rest of the system as the actual module. That’s why Magento_Customer/js/view/customer had our extracrazyPropertyAddedHere property.

Class Rewrites for Javascript

As we’ve mentioned a few times, this is an incredibly powerful feature. One thing you could do with it os replace method implementations on RequireJS modules that return objects

define([], function(){
    'use strict';    
    console.log("Called this Hook.");
    return function(targetModule){
        targetModule.someMethod = function(){
            //replacement for `someMethod
        }
        return targetModule;
    };
});

Also, if the module in question returns a uiClass based object? You could use uiClass‘sextend method to return a different class that extended the method, but used uiClass‘s_super() feature to call the parent method.

define([], function(){
    'use strict';    
    console.log("Called this Hook.");
    return function(targetModule){
        //if targetModule is a uiClass based object
        return targetModule.extend({
            someMethod:function()
            {
                var result = this._super(); //call parent method

                //do your new stuff

                return result;
            }
        });
    };
});

These are powerful techniques that allow a careful developer, with a small amount of code, to change existing system behavior in exactly the way she wants to. However, like class rewrites in Magento 1, and the class <preference> feature in Magento 2, the above examples are a winner take all situation. While multiple developers can all safely setup their own hooks, they can’t all redefine the same method or function. One person’s modifications will win out over the other person’s.

Fortunately, Magento 2 has a solution for that in the mage/utils/wrapper module.

Wrapping Function Calls

The mage/utils/wrapper module allows for functionality similar to a Magento 2 backend around plugin. Here’s a simple example that should demonstrate what it means to “wrap” a function.

var example = {};
example.foo = function (){
    console.log("Called foo");
}

var wrapper = requirejs('mage/utils/wrapper');

var wrappedFunction = wrapper.wrap(example.foo, function(originalFunction){        
    console.log("Before");
    originalFunction();
    console.log("After");
});

//call wrapped function
wrappedFunction();

//change method definition to use wrapped function
example.foo = wrappedFunction;

If you run the above code, you’ll see the following output

Before
Called foo
After

The wrap method accepts two arguments. The first is the original function you want to wrap. The second is the function you want to wrap it with. The originalFunctionparameter will be a reference to the function you’re trying to wrap (example.foo above). The wrap method returns a function that, when called, will call your function. Your function can, if you desire, call the original function.

The point of the wrapper module is to wrap an existing function call with new code without needing to edit the original function. It’s another powerful technique that javascript’s flexible object system enables. Another great thing about wrapping is, multiple people can do it. Try running the following after running the above code.

var wrappedFunction2 = wrapper.wrap(wrappedFunction, function(originalFunction){        
    console.log("Before 2");
    originalFunction();
    console.log("After 2");
});   

wrappedFunction2();

This means if you’re using it to replace method definitions, you can avoid the winner take all situation we described earlier. Consider the following “mixin” hook.

define(['mage/utils/wrapper'], function(wrapper){
    'use strict';    
    console.log("Called this Hook.");
    return function(targetModule){

        var newFunction = targetModule.someFunction;
        var newFunction = wrapper.wrap(newFunction, function(original){
            //do extra stuff

            //call original method
            var result = original();    

            //do extra stuff                

            //return original value
            return result;
        });

        targetModule.someFunction = newFunction;
        return targetModule;
    };
});    

Here we’ve replaced the definition of someFunction with our wrapped function. This technique also has the advantage of working with RequireJS modules that return functions instead of objects.

Why Call this a Mixin

All this still leaves the question of why Magento calls this feature a mixin. A developer can certainly use this feature to implement mixin like behavior, but the feature itself is more of a listener/hook and has nothing to do with adding methods to objects.

I’m sure, to a non-technical user, all this terminology seems like interchangeable jargon but — words still mean things. When a junior, or even intermediate developer, encounters this feature they’re going to expect some sort of real mixin functionality. They’ll spend hours, possibly days, spinning their wheels trying to make it work until they give up.

When a senior developer encounters this they’re going to wonder why something a simple peer/code-review should have caught made it through engineering, through product, and into a production system. Like so much of Magento 2, it feels like we’re looking at the skeleton of a great new building, but being sold office space.

Regardless, this not really a mixin functionality is powerful, and is the perfect mechanism for changing your system’s behavior. As an extension developer, it’s still a crap shoot as to whether a particular RequireJS module will still be around version to version, but for system integrators who own a particular Magento system (and can adjust customizations over time) not really a mixin customizations will be a boon.

Magento 2: KnockoutJS Integration

While KnockoutJS bills itself as an MVVM (model, view, view model) framework, PHP developers will find the model portion a little thin. KnockoutJS itself has no native concept of data storage, and like many modern javascript frameworks it was designed to work best with a service only backend. i.e. KnockoutJS’s “Model” is some other framework making AJAX requests to populate view model values.

Something else that might catch you off guard with KnockoutJS is it’s not a “full stack” javascript application framework (and to its credit, doesn’t bill itself as such). KnockoutJS has no opinion on how you include it in your projects, or how you organize your code (although the documentation makes it clear the KnockoutJS team members are fans ofRequireJS).

This presents an interesting challenge for a server side PHP framework like Magento. Not only is there a degree of javascript scaffolding that needs to surround KnockoutJS, but Magento 2 is not a service only framework. While the new API features of Magento 2 are making strides in this direction, Magento 2 is not a service only framework. i.e. The backend framework developers also need to build scaffolding to get business object datainto KnockoutJS.

Today we’re going to dive into Magento 2’s KnockoutJS integration. By the end of this tutorial you’ll understand how Magento 2 applies KnockoutJS bindings as well as how Magento 2 initializes its own custom bindings. You’ll also understand how Magento has modified some core KnockoutJS behavior, why they’ve done this, and the additional possibilities these changes open for your own applications and modules.

This article is part of a longer series covering advanced javascript concepts in Magento 2. While reading the previous articles isn’t 100% mandatory, if you’re struggling with concepts below you may want to review the previous articles before pointing to yourMagento Stack Exchange question in the comments below.

Creating a Magento Module

While this article is javascript heavy, we’ll want our example code to run on a page with Magento’s baseline HTML. This means adding a new module. We’ll do this the same as we did in the first article of this series, and use pestle to create a module with a URL endpoint

$ pestle.phar generate_module Pulsestorm KnockoutTutorial 0.0.1

$ pestle.phar generate_route Pulsestorm_KnockoutTutorial frontend pulsestorm_knockouttutorial

$ pestle.phar generate_view Pulsestorm_KnockoutTutorial frontend pulsestorm_knockouttutorial_index_index Main content.phtml 1column

$ php bin/magento module:enable Pulsestorm_KnockoutTutorial

$ php bin/magento setup:upgrade

These commands should be familiar to anyone who’s worked their way through theMagento 2 for PHP MVC developers series. Once you’ve run the above, you should be able to access the following URL in your system

http://magento.example.com/pulsestorm_knockouttutorial/

and see the renderedapp/code/Pulsestorm/KnockoutTutorial/view/frontend/templates/content.phtmltemplate. Pestle isn’t mandatory here — if you have a preferred way of working with a page in Magento, feel free to use it.

RequireJS Initialization

In our previous article, and in the official KnockoutJS tutorials, KnockoutJS initialization is a simple affair.

object = SomeViewModelConstructor();
ko.applyBindings(object);

For tutorial applications, this makes sense. However, if you were to keep all your view model logic, custom bindings, components, etc. in a single chunk of code, KnockoutJS would quickly grow un-manageable.

Instead, Magento’s core team has created the Magento_Ui/js/lib/ko/initializeRequireJS module that, when listed as a dependency, will perform any and all KnockoutJS initialization. You can use this module like this

requirejs(['Magento_Ui/js/lib/ko/initialize'], function(){
    //your program here
});

One interesting thing to note about this RequireJS module is it returns no value. Instead, the sole purpose of listing the RequireJS module as a dependency is to kickoff Magento’s KnockoutJS integration. This might confuse you when you see it in the wild. For example, consider this code from a different Magento RequireJS module.

#File: vendor/magento/module-ui/view/base/web/js/core/app.js
define([
    './renderer/types',
    './renderer/layout',
    'Magento_Ui/js/lib/ko/initialize'
], function (types, layout) {
    'use strict';

    return function (data) {
        types.set(data.types);
        layout(data.components);
    };
});

Three RequireJS dependencies are declared,

#File: vendor/magento/module-ui/view/base/web/js/core/app.js
[
'./renderer/types',
'./renderer/layout',
'Magento_Ui/js/lib/ko/initialize'
]

but only two parameters are used in the resulting function

#File: vendor/magento/module-ui/view/base/web/js/core/app.js
function (types, layout) {
    //...
}

It’s not clear to me if this is a clever bit of programming, or if its something that violates the spirit of RequireJS. Maybe it’s both.

Regardless, the first time you use this library in your own RequireJS based programs Magento will initialize KnockoutJS. Subsequent inclusions will effectively do nothing, as RequireJS caches your modules the first time you load them.

KnockoutJS Initialization

If we take a look at the source of of the Magento_Ui/js/lib/ko/initialize module

#File: vendor/magento/module-ui/view/base/web/js/lib/ko/initialize.js
define([
    'ko',
    './template/engine',
    'knockoutjs/knockout-repeat',
    'knockoutjs/knockout-fast-foreach',
    'knockoutjs/knockout-es5',
    './bind/scope',
    './bind/staticChecked',
    './bind/datepicker',
    './bind/outer_click',
    './bind/keyboard',
    './bind/optgroup',
    './bind/fadeVisible',
    './bind/mage-init',
    './bind/after-render',
    './bind/i18n',
    './bind/collapsible',
    './bind/autoselect',
    './extender/observable_array',
    './extender/bound-nodes'
], function (ko, templateEngine) {
    'use strict';

    ko.setTemplateEngine(templateEngine);
    ko.applyBindings();
});

We see a program that’s relatively simple, but that also includes nineteen other modules. Covering what each of these modules does is beyond the scope of this article. Consider the following a highlight reel.

The ko module is an alias to the knockoutjs/knockout module.

vendor/magento/module-theme/view/base/requirejs-config.js
11:            "ko": "knockoutjs/knockout",
12:            "knockout": "knockoutjs/knockout"

The knockoutjs/knockout module is the actual knockout library file. Theknockoutjs/knockout-repeat,knockoutjs/knockout-fast-foreach, and
knockoutjs/knockout-es5 modules are KnockoutJS community extras. None of these are formal RequireJS modules.

The modules that start with ./bind/* are Magento’s custom bindings for KnockoutJS. These are formal RequireJS modules, but do not actually return a module. Instead each script manipulates the global ko object to add bindings to KnockoutJS. We’ll discuss thescope binding below, but if you’re the curious type try investigating the implementation details of the other bindings. It’s a useful exercise. Hopefully Magento gets us official documentation soon.

The two extender modules are Magento core extensions to KnockoutJS’s functionality.

The ./template/engine module returns a customized version of KnockoutJS’s template engine, and is the first customization we’ll dive deeply into.

Magento KnockoutJS Templates

To review, in a stock KnockoutJS system, templates are chunks of pre-written DOM/KnockoutJS code that you can use by referencing their id. These chunks are added to the HTML of the page via script tags, with a type of text/html

<script type="text/html" id="my_template">
    <h1 data-bind="text:title"></h1>
</script>

This is a powerful feature, but presents a problem for a server side framework — how do you get the right templates rendered on a page? How can you be sure the template will be there without recreating it every-time? The KnockoutJS solution for this is to use the component binding with a library like RequireJS, but this means your templates are tied to a specific view model object.

Magento’s core engineers needed a better way to load KnockoutJS templates — and they did this by replacing the native KnockoutJS template engine with the engine loaded from the Magento_Ui/js/lib/ko/template/engine RequireJS module.

#File: vendor/magento/module-ui/view/base/web/js/lib/ko/initialize.js
define([
    'ko',
    './template/engine',
    //...
], function (ko, templateEngine) {
    'use strict';
    //...
    ko.setTemplateEngine(templateEngine);
    //...
});

If we take a peek at the Magento_Ui/js/lib/ko/template/engine RequireJS module

#File: vendor/magento/module-ui/view/base/web/js/lib/ko/template/engine.js
/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
define([
    'ko',
    './observable_source',
    './renderer'
], function (ko, Source, Renderer) {
    'use strict';

    var RemoteTemplateEngine,
        NativeTemplateEngine = ko.nativeTemplateEngine,
        sources = {};

    //...

    RemoteTemplateEngine.prototype = new NativeTemplateEngine;


    //...
    RemoteTemplateEngine.prototype.makeTemplateSource = function (template) 
    {
        //...        
    }
    //...

    return new RemoteTemplateEngine;
});

we see Magento makes a new object that prototypically inherits from the native KnockoutJS rendering engine, and then modifies a few methods to add custom behavior. If you’re not up on your javascript internals, this means Magento copies the stock KnockoutJS template system, changes it a bit, and then swaps its new template engine in for the stock one.

The implementation details of these modifications are beyond the scope of this article, but the end result is a KnockoutJS engine that can load templates via URLs from Magento modules.

If that didn’t make sense, an example should clear things up. Add the following to ourcontent.phtml file.

#File: app/code/Pulsestorm/KnockoutTutorial/view/frontend/templates/content.phtml    
<div data-bind="template:'Pulsestorm_KnockoutTutorial/hello'"></div>

Here we’ve added a KnockoutJS template binding and passed it the stringPulsestorm_KnockoutTutorial/hello. If we reload our page with the above in place, you’ll see an error like the following in your javascript console

> GET http://magento-2-0-4.dev/static/frontend/Magento/luma/en\_US/Pulsestorm\_KnockoutTutorial/template/hello.html 404 (Not Found)

Magento has taken our string (Pulsestorm_KnockoutTutorial/hello) and used the first portion (Pulsestorm_KnockoutTutorial) to create a base URL to a view resource, and use the second portion (hello) with a prepended template and an appended .html to finish the URL. If we add a KnockoutJS view to the following file

#File: app/code/Pulsestorm/KnockoutTutorial/view/frontend/web/template/hello.html
<p data-bind="style:{fontSize:'24px'}">Hello World</p>

and reload the page, we’ll see Magento has loaded our template from the above URL, and applied its KnockoutJS bindings.

This feature allows us to avoid littering our HTML page with <script type="text/html"> tags whenever we need a new template, and encourages template reuse between UI and UX features.

No View Model

Coming back to the initialize.js module, after Magento sets the custom template engine, Magento calls KnockoutJS’s applyBindings method. This kicks off rendering the current HTML page as a view. If we take a look at that code, something immediately pops out.

#File: vendor/magento/module-ui/view/base/web/js/lib/ko/initialize.js
ko.setTemplateEngine(templateEngine);
ko.applyBindings();

Magento called applyBindings without a view model. While this is a legal KnockoutJS call — telling KnockoutJS to apply bindings without data or view model logic seems pretty useless. What is a view without data going to be good for?

In a stock KnockoutJS system, this would be pretty useless. The key to understanding what Magento is doing here is back up in our KnockoutJS initialization

#File: vendor/magento/module-ui/view/base/web/js/lib/ko/initialize.js
define([
    //...
    './bind/scope',
    //...
],

Magento’s KnockoutJS team created a custom KnockoutJS binding named scope. Here’s an example of using scope — lifted from the Magento 2 homepage.

<li class="greet welcome" data-bind="scope: 'customer'">
    <span data-bind="text: customer().fullname ? $t('Welcome, %1!').replace('%1', customer().fullname) : 'Default welcome msg!'"></span>
</li>

When you invoke the scope element like this

data-bind="scope: 'customer'"

Magento will apply the customer view model to this tag and its descendants.

You’re probably wondering — what the heck’s the customer view model?! If you look a little further down in the home page’s source, you should see the following script tag

<script type="text/x-magento-init">
{
    "*": {
        "Magento_Ui/js/core/app": {
            "components": {
                "customer": {
                    "component": "Magento_Customer/js/view/customer"
                }
            }
        }
    }
}
</script>

As we know from the first article in this series, when Magento encounters a text/x-magento-init script tag with an * attribute, it will

  1. Initialize the specified RequireJS module (Magento_Ui/js/core/app)
  2. Call the function returned by that module, passing in the data object

The Magento_Ui/js/core/app RequireJS module is a module that instantiates KnockoutJS view models to use with the scope attribute. Its full implementation is beyond the, um, “scope” of this article, but at a high level Magento will instantiate a new javascript object for each individual RequireJS module configured as a component, and that new object becomes the view model.

If that didn’t make sense, lets run through an example with the above x-magento-init. Magento looks in the components key, and sees one key/object pair.

"customer": {
    "component": "Magento_Customer/js/view/customer"
}

So, for the customer key, Magento will run code that’s equivalent to the following.

//gross over simplification
var ViewModelConstructor = requirejs('Magento_Customer/js/view/customer');
var viewModel = new ViewModelConstructor;
viewModelRegistry.save('customer', viewModel);

If there’s extra data in a specific component object

"customer": {
    "component": "Magento_Customer/js/view/customer",
    "extra_data":"something"
}

Magento will add that data to the view model as well.

Once the above is done, the view model registry will have a view model named customer. This is the view model Magento will apply for the data-bind="scope: 'customer'"binding.

If we take a look at the implementation of the scope custom binding

#File: vendor/magento/module-ui/view/base/web/js/lib/ko/bind/scope.js
define([
    'ko',
    'uiRegistry',
    'jquery',
    'mage/translate'
], function (ko, registry, $) {
    'use strict';

    //...
        update: function (el, valueAccessor, allBindings, viewModel, bindingContext) {
            var component = valueAccessor(),
                apply = applyComponents.bind(this, el, bindingContext);

            if (typeof component === 'string') {
                registry.get(component, apply);
            } else if (typeof component === 'function') {
                component(apply);
            }
        }
    //...

});

It’s the registry.get(component, apply); line that fetches the named view model from the view model registry, and then the following code is what actually applies the object as a view model in KnockoutJS

#File: vendor/magento/module-ui/view/base/web/js/lib/ko/bind/scope.js

//the component variable is our viewModel
function applyComponents(el, bindingContext, component) {
    component = bindingContext.createChildContext(component);

    ko.utils.extend(component, {
        $t: i18n
    });

    ko.utils.arrayForEach(el.childNodes, ko.cleanNode);

    ko.applyBindingsToDescendants(component, el);
}

The registry variable comes from the uiRegistry module, which is an alias for theMagento_Ui/js/lib/registry/registry RequireJS module.

vendor/magento/module-ui/view/base/requirejs-config.js
17:            uiRegistry:     'Magento_Ui/js/lib/registry/registry',

If a lot of that flew over your head, don’t worry. If you want to peek at the data available in a particular scope’s binding, the following debugging code should steer you straight.

<li class="greet welcome" data-bind="scope: 'customer'">
    <pre data-bind="text: ko.toJSON($data, null, 2)"></pre>            
    <!-- ... -->
</li>

If you’re one of the folks interested in diving into the real code that creates the view models (and not our simplified pseudo-code above), you can start out in theMagento_Ui/js/core/app module.

#File: vendor/magento/module-ui/view/base/web/js/core/app.js
define([
    './renderer/types',
    './renderer/layout',
    'Magento_Ui/js/lib/ko/initialize'
], function (types, layout) {
    'use strict';

    return function (data) {
        types.set(data.types);
        layout(data.components);
    };
});

This module has a dependency named Magento_Ui/js/core/renderer/layout. It’s in this dependency module that Magento initializes the view models, and adds them to the view model registry.

#File: vendor/magento/module-ui/view/base/web/js/core/renderer/layout.js

The code’s a little gnarly in there, but if you need to know how those view models are instantiated, that’s where you’ll find them.

A Component by Any Other Name

One sticky wicket in all this is the word component. This scope binding + x-magento-init system is basically a different take on the native KnockoutJS component system.

By using the same component terminology as KnockoutJS, Magento has opened up a new world of confusion. Even the official documentation seems a little confused on what a component is or isn’t. Such is life on a large software team where the left hand doesn’t know what the right hand is doing — and the rest of the body is freaking out about a third hand growing out of its back.

When discussing these features with colleagues, or asking questions on a Magento forum, it will be important to differentiate between KnockoutJS components, and a Magento components.

Changes in the 2.1 Release Candidate

To wrap up for today, we’re going to talk about a few changes to the above in the Magento 2.1 release candidates. Conceptually, the systems are still the same, but there’s a few changes to the details.

First off, KnockoutJS’s initialization now happens in theMagento_Ui/js/lib/knockout/bootstrap RequireJS module

#File: vendor/magento/module-ui/view/base/web/js/lib/knockout/bootstrap.js
define([
    'ko',
    './template/engine',
    'knockoutjs/knockout-es5',
    './bindings/bootstrap',
    './extender/observable_array',
    './extender/bound-nodes',
    'domReady!'
], function (ko, templateEngine) {
    'use strict';

    ko.uid = 0;

    ko.setTemplateEngine(templateEngine);
    ko.applyBindings();
});

Notice that Magento’s core developers moved all the binding loading to an individual module Magento_Ui/js/lib/knockout/bindings/bootstrap, defined in

#File: vendor/magento/module-ui/view/base/web/js/lib/knockout/bindings/bootstrap.js

Finally, the “Magento Javascript Component” returned by Magento_Ui/js/core/app has a changed method signature that includes a merge parameter, and the arguments to thelayout function make it clear layout‘s signature has changed as well.

#File: vendor/magento/module-ui/view/base/web/js/core/app.js
define([
    './renderer/types',
    './renderer/layout',
    '../lib/knockout/bootstrap'
], function (types, layout) {
    'use strict';

    return function (data, merge) {
        types.set(data.types);
        layout(data.components, undefined, true, merge);
    };
});

Beyond being interesting for folks who who are interested in implementation details, these changes point to the fact that Magento’s javascript modules and frameworks are changing rapidly, and unlike the PHP code, Magento’s RequireJS modules don’t have @api markings to indicate stability.

Unless you absolutely need to, it’s probably best to steer clear of dynamically changing the behavior of these core modules, and keep your own javascript as separate as possible.

KnockoutJS Primer for Magento Developers

Before we can continue our exploration of Magento’s advanced javascript features, we need to take a crash course in KnockoutJS. KnockoutJS bills itself as a javascript MVVM system, and its the dominant DOM manipulation framework in Magento 2.

This is a crash course meant to get a working Magento developer familiar with basic KnockoutJS concepts, with a focus on the features Magento uses. We highly recommend working through the official KnockoutJS tutorials if you plan on building anything of substance with KnockoutJS.

Hello Model, View, View Model

The quickest way to wrap your head around KnockoutJS is a basic example. First, lets create the following HTML page

<!-- File: page.html -->  
<!DOCTYPE html>
<html lang="en">
<head>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
    <script src="https://code.jquery.com/jquery-3.0.0.min.js" integrity="sha256-JmvOoLtYsmqlsWxa7mDSLMwa6dZ9rrIdtrrVYRnDRH0=" crossorigin="anonymous"></script>    
</head>
<body>
<div id="main">
    <h1></h1>
    <p></p>
</div>
</body>
</html>

This page

  1. Loads the KnockoutJS library from the cloudflare CDN
  2. Loads the jQuery library from the jQuery code CDN
  3. Sets up an empty DOM node structure

You don’t need to load jQuery and KnockoutJS from a CDN, but its easiest for this tutorial if you do.

If you load this page in a browser, it will be completely blank. That’s because we need to

  1. Add the javascript code that creates a view model and applies the KnockoutJS bindings
  2. Add the view code to the HTML page that reads from the view model

Tackling the first of those, let’s add a third javascript file to our page. We’ll create a file named ko-init.js with the following contents

//File: ko-init.js
jQuery(function(){
    viewModel = {
        title:"Hello World",
        content:"So many years of hello world"
    };
    ko.applyBindings(viewModel);
});

and then add ko-init.js to our page with a third script tag.

<!-- File: page.html -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script>
<script src="https://code.jquery.com/jquery-3.0.0.min.js" integrity="sha256-JmvOoLtYsmqlsWxa7mDSLMwa6dZ9rrIdtrrVYRnDRH0=" crossorigin="anonymous"></script>    
<script type="text/javascript" src="ko-init.js"></script>

Finally, change the h1 and p tags so they include the following data-bind attributes.

<!-- File: page.html -->
<div id="main">
    <h1 data-bind="text:title"></h1>
    <p data-bind="text:content"></p>
</div>

With the above in place, reload your page and you should see your title and content rendered.

Congratulations! You just created your first KnockoutJS view model and view.

What Just Happened

KnockoutJS bills itself as an “MVVM” system. This stands for Model, View, View Model. Really though, KnockoutJS is better billed as a VVM system, since its agnostic about what sort of model code you use to fetch data. The view is your HTML page. The view model is the javascript object that contains data.

Take a look at the javascript code

//File: ko-init.js
jQuery(function(){
    viewModel = {
        title:"Hello World",
        content:"So many years of hello world"
    }; 
    ko.applyBindings(viewModel);
});

While jQuery isn’t required, KnockoutJS can’t start rendering a view until the entire document/DOM is loaded, and jQuery’s default document ready functionality is a good way to achieve this.

Here we’ve created a view model with simple key/value pairs.

//File: ko-init.js
viewModel = {
    title:"Hello World",
    content:"So many years of hello world"
}; 

Then, we’ve applied the KnockoutJS bindings. Another way to say this is we’ve told KnockoutJS to render the view with our view model. Again, the view is the entire HTML page.

If we look at the important section of our view, we see the data-bind attributes

<!-- File: page.html -->  
<div id="main">
    <h1 data-bind="text:title"></h1>
    <p data-bind="text:content"></p>
</div>

When you call applyBindings, KnockoutJS will scan the entire HTML page for data-bind attributes. When it finds these attributes, it parses the attribute for the binding name and value, and then invokes a set of rules based on the name of the binding.

For example — the binding we invoke above is the text binding. The value we passed to the text binding is title. The set of rules the text binding applies are “use the value passed into the binding to fetch a value from the view model object, and add that value as a text node”. The end result is something that, if written in pure javascript, might look like this

value = viewModel['title'];
textNode = document.createTextNode(value);
h1.appendChild(textNode);

KnockoutJS’s first trick is it gets developers out of the business of directly using javascript to create and update DOM nodes. Instead, developers can write HTML, mark it up withdata-bind attributes, and just assign values to an object. You’re not limited to key/value pairs either. Consider this more sophisticated view model.

//File: ko-init.js
jQuery(function(){
    var viewModelConstructor = function()
    {
        this.getTitle = function()
        {
            return "Hello Method World";
        }
       this.content = "So many years of hello world";        
    }

    viewModel = new viewModelConstructor;
    ko.applyBindings(viewModel);
});

Here we’ve used a javascript constructor to create a simple object, with a getTitlemethod. If we change our view to call the getTitle method, you’ll see it works as you’d expect

<!-- File: page.html -->  
<div id="main">
    <h1 data-bind="text:getTitle()"></h1>
    <p data-bind="text:content"></p>
</div>

Another way of thinking about binding parameters is they’re a temporary, limited javascript scope to access your view model’s values and methods.

Other Bindings

While this example is simple, you can start to see how this basic building block could implement far more complicated view logic. The business of updating the DOM is left to the data-bindings, and the business of updating the model is left to pure non-DOM javascript code.

You can start to see the value of this with other bindings. For example, lets add a theValueproperty in our viewModelConstructor

//File: ko-init.js
jQuery(function(){
    var viewModelConstructor = function()
    {
        this.getTitle = function()
        {
            return "Hello World";
        }
       this.content = "So many years of hello world";        
       this.theValue = "2";
    }
    viewModel = new viewModelConstructor;
    ko.applyBindings(viewModel);        
});

and then add an input tag with a new binding.

<!-- File: page.html -->  
<div id="main">
    <h1 data-bind="text:getTitle()"></h1>
    <p data-bind="text:content"></p>
    <input type="text" data-bind="value:theValue"/>        
</div>

Reload the page, and you’ll see an HTML input tag with a value of 2.

Here we’ve used a new (to us) KnockoutJS binding

data-bind="value:theValue"      

We use the value binding to apply a value to the form field. Next, let’s change that input to a select with the same binding

<!-- File: page.html -->  
<div id="main">
    <h1 data-bind="text:getTitle()"></h1>
    <p data-bind="text:content"></p>
    <select data-bind="value:theValue">
        <option value="">-- Choose --</option>
        <option value="1">First</option>
        <option value="2">Second</option>        
        <option value="3">Third</option>                
    </select>      
</div>

If you reload the page, you’ll see the binding has set the value of the select for us.

While this example is simple, the concept behind it is not. Without needing to change anyjavascript application code, the value binding lets us change our UI.

Observables

So far, what we’ve seen is a powerful parlor trick. Neat, maybe useful, but it only sets the stage for KnockoutJS’s real “knockout” feature — observables.

Again, we’re going to dive in with an example. Change your view so it matches the following

<!-- File: page.html --> 
<div id="main">
    <p data-bind="text:theValue"></p>
    <select data-bind="value:theValue">
        <option value="">-- Choose --</option>
        <option value="1">First</option>
        <option value="2">Second</option>        
        <option value="3">Third</option>                
    </select>    
    <input type="text" data-bind="value:theValue"/>

    <div>
    <br/><br/>
    <button id="button">Do It</button>
    </div>
</div>

and change your view model and binding so they match the following.

//File: ko-init.js
jQuery(function(){
    var viewModelConstructor = function()
    {   
       this.theValue = ko.observable("1");
    }

    window.viewModel = new viewModelConstructor;
    ko.applyBindings(window.viewModel);        
});  

If you reload the page, you’ll see we’ve bound the value of 1 to our <input/> and <p/>tag. So far our view has nothing new — this is the same sort of binding we were doing previously. However, you’ll notice that we’ve done something different in our view model.

//File: ko-init.js
this.theValue = ko.observable("1");

Instead of setting theValue to a hard coded value, or a custom function, we’ve set the value to be something KnockoutJS calls an observable. An observable is a special sort of getter and setter.

If you open up your javascript console, and type the following, you’ll see we can fetch the value of the observable by calling it as a function (viewModel is available via the console since we defined it as a global object on the window object)

> viewModel.theValue()    
> "1"

We can set a value on the observable by passing in a parameter. Here’s how you’d set, and then get the value of, an observable.

> viewModel.theValue("3")
//...
> viewModel.theValue()
> "3"

However, the real power of an observable is in what happens to the DOM nodes we’ve bound that observable to. Try changing the value of the observer via the console and watch the browser window

> viewModel.theValue("3");
> viewModel.theValue("2");
> viewModel.theValue("1");
> viewModel.theValue("10");

As you update the value of the observable, the value of the bound nodes change in real time. Again, as developers, we’re freed from having to worry how the DOM nodes get updated — once we’ve set the value on our model, this value is automatically reflected in the user interface.

While its beyond the scope of this article, you can see how this comes together to form complex javascript applications when our view model includes methods

//File: ko-init.js
jQuery(function(){
    var viewModelConstructor = function()
    {   
        this.theValue = ko.observable("1");
        var that = this;
        this.pickRandomValue = function(){
            var val = Math.floor(Math.random() * (3));
            that.theValue(val);
        };
    }

    window.viewModel = new viewModelConstructor;
    ko.applyBindings(window.viewModel);        
});

and you use KnockoutJS’s event bindings, like click

<!-- File: page.html -->  
<button data-bind="click:pickRandomValue">Do It</button>

We’ll leave parsing through that one as an exercise for the reader

Template Binding

Another binding that’s important to understand is KnockoutJS’s template binding. Consider a view model like this

//File: ko-init.js
jQuery(function(){
    var viewModelConstructor = function()
    {   
        this.first = {
            theTitle:ko.observable("Hello World"),
            theContent:ko.observable("Back to Hello World")
        };
        this.second = {
            theTitle:ko.observable("Goodbye World"),
            theContent:ko.observable("We're sailing west now")            
        };            
    }

    viewModel = new viewModelConstructor;
    ko.applyBindings(viewModel);        
});

Here we’ve created a standard view model, but with nested data objects. If you combine this with a view like this

<!-- File: page.html --> 
<div id="main">
    <div id="one" data-bind="template:{'name':'hello-world','data':first}"></div>

    <div id="two" data-bind="template:{'name':'hello-world','data':second}">
    </div>

    <script type="text/html" id="hello-world">
        <h1 data-bind="text:theTitle"></h1>
        <p data-bind="text:theContent"></p>
    </script>
</div>

you’ll see the following

The template binding accepts a javascript object as a parameter

<!-- File: page.html --> 
<div id="one" data-bind="template:{'name':'hello-world','data':first}"></div>

The data parameter is the property of the view model we want to render the template with. The name of the template is just that — the template name to lookup and render.

The most basic way of adding a named template to the system is adding a <script/> tag with a type of text/html.

<!-- File: page.html --> 
<script type="text/html" id="hello-world">
    <h1 data-bind="text:theTitle"></h1>
    <p data-bind="text:theContent"></p>
</script>   

If you’ve never seen this before it may seem weird/foreign, but many modern javascript frameworks use non-text/javascript <script/> tags as a way to add non-rendered (but DOM accessible) content to a page. A template is just a standard set of HTML nodes with KnockoutJS bindings.

Components

The final binding we’ll at is the component binding. Components are a way to package together a KnockoutJS template, and a KnockoutJS view file. This means you can have a relatively simple view

<!-- File: page.html -->      
<div data-bind="component:'component-hello-world'"></div>

which hides the complexity of a registered component.

//File: ko-init.js
jQuery(function(){    
    var viewModelConstructor = function()
    {   
        this.message = "Hello World";
    }  

    var theTemplate = "<h1 data-bind=\"text:message\"></h1>";    

    ko.components.register('component-hello-world', {
        viewModel:viewModelConstructor,
        template:theTemplate
    });    

    ko.applyBindings();        
});

The register function of the component object expects a name for your component, and then a KnockoutJS component object. A component object is a javascript script object with two properties. The viewModel property expects a view model constructor function, and the template property should be a string with a KnockoutJS template. Once registered, you can use your component by passing the name of the component (as a string) into the binding.

<!-- File: page.html -->  
<div data-bind="component:'component-hello-world'"></div>

If you don’t want to use the data-bind syntax — KnockoutJS gives you the ability to insert a component with a custom tag name based on the component name. Try this in your view/HTML-file

<!-- File: page.html -->  
<component-hello-world></component-hello-world>

This only scratches the surface of what’s possible with KnockoutJS. The official docs have a pretty good overview of the component binding.

Custom Binding

The final KnockoutJS feature we’ll discuss today is the custom binding feature. KnockoutJS gives javascript developers the ability to create their own bindings. For example, here we’re invoking a custom binding named pulseStormHelloWorld, and passing it the value of the message property of our viewModel.

<!-- File: page.html -->  
<div data-bind="pulseStormHelloWorld:message"></div>

Without an implementation, KnockoutJS will ignore our binding. Instead, try the following in ko-init.js

//File: ko-init.js
jQuery(function(){    
    var viewModelConstructor = function()
    {   
        this.message = "Hello World";
    }  

    ko.bindingHandlers.pulseStormHelloWorld = {
        update: function(element, valueAccessor){
            jQuery(element).html('<h1>' + valueAccessor() + '</h1>');
        }
    };    
    ko.applyBindings(new viewModelConstructor);        
});

To add the custom binding to KnockoutJS, all we need to do is add a property to the koobject’s binidngHandlers object. The name of this property is the name of our binding. The handler is a JS object with an update method. KnockoutJS calls the update method whenever a binding is invoked — either during applyBindings, or via an observable.

With the above in place, reload your HTML page and you’ll see the custom binding invoked. This is, of course, a trivial example, but with custom bindings you can make KnockoutJS do anything your programatic mind thinks up.

The KnockoutJS core documentation has a pretty good tutorial on custom bindings if you’re interested in learning more.

Wrap Up

KnockoutJS is a powerful, modern, javascript framework. Its semantics, and disregard for traditional HTML concepts, may make some developers shy away at first, but as a tool of organizing and taming DOM complexity in a modern javascript application KnockoutJS has few peers. Our tour here is incomplete, but hopefully it’s enough to get you interested in working through KnockoutJS’s tutorials and documentation for yourself.

All that said, KnockoutJS by itself isn’t enough to build a full javascript application. Next week we’ll take a look at the framework Magento has built around KnockoutJS — both so you can better understand how the core Magento system works, and also so you can incorporate KnockoutJS into your own modules and applications.

Magento 2: Javascript Init Scripts

Today we’re starting a new series that will cover advanced Javascript systems in Magento 2. This series is sponsored by my patreon campaign. If you like what you see here, please consider donating to keep the tutorials coming.

Back in our Magento 2 for PHP MVC series, we stated that RequireJS was the library that underlies nearly every javascript feature built in Magento 2. This is true enough, but RequireJS only scratches the surface of what’s possible with javascript and Magento 2.

Today we’re going to explore the various systems Magento 2 has for kicking off execution of javascript code without an embedded <script type="text/javascript"> tag.

Javascript Init Methods

The Magento javascript init methods we’re going to discuss solve a few different problems.

First, they provide a standard mechanism to discourage directly embedding javascript into a page.

Second, they provide a way to invoke a stand alone RequireJS module (defined withdefine) as a program.

Third, they provide a way to pass that program a server side generated JSON object.

Fourth, they provide a way to tell that program which (if any) DOM nodes it should operate on.

Keep these four goals in mind. They may help you if you’re struggling with the mental model behind these custom framework features.

Setting up a Module

Toady’s tutorial doesn’t involved much in the way of Magento’s PHP code — you can run these examples from any Magento phtml template that’s rendered on a stock Magento page.

We’re going to use pestle to create a module namedPulsestorm_JavascriptInitTutorial with a single URL endpoint by running the following three commands

$ pestle.phar generate_module Pulsestorm JavascriptInitTutorial 0.0.1

$ pestle.phar generate_route Pulsestorm_JavascriptInitTutorial frontend pulsestorm_javascriptinittutorial

$ pestle.phar generate_view Pulsestorm_JavascriptInitTutorial frontend pulsestorm_javascriptinittutorial_index_index Main content.phtml 1column

$ php bin/magento module:enable Pulsestorm_JavascriptInitTutorial

$ php bin/magento setup:upgrade

These commands should be familiar to anyone who’s worked their way through theMagento 2 for PHP MVC developers series. Once you’ve run the above, you should be able to access the following URL in your system

http://magento.example.com/pulsestorm_javascriptinittutorial/

and see the renderedapp/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtmltemplate.

Setting up a RequireJS Module

Now that we’ve setup a Magento module, lets do a quick review and create a RequireJS module. First, create the following file

//File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/web/example.js
define([], function(){
    alert("A simple RequireJS module");
    return {};    
});    

This is a very simple RequireJS module. Due to the module’s location on the file system, and the way Magento loads javascript files, this module’s name/identifier isPulsestorm_JavascriptInitTutorial/example

Next, change the contents of content.phtml so they match the following.

#File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtml
<script type="text/javascript">
    requirejs(['Pulsestorm_JavascriptInitTutorial/example'],function(example){
        alert("Loaded");
        console.log(example);
    }); 
</script>

Here we’re creating a RequireJS program with a single module dependency. The dependency is our just created module (Pulsestorm_JavascriptInitTutorial/example). Load the

http://magento.example.com/pulsestorm_javascriptinittutorial/

URL in your system, and you should see the alerts.

If any of the above was foreign to you, you may want to review our Magento 2 and RequireJS article.

X-Magento-Init

The first initialization technique we’re going to discuss is the <script type="text/x-magento-init"> method. The most basic version of this initialization method allows you to run a RequireJS module as a program. Change the contents of the content.phtmltemplate so they match the following

#File: app/code/Pulsestorm/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">
    {
        "*": {
            "Pulsestorm_JavascriptInitTutorial/example":{}
        }
    }        
</script>

Give your page a reload with the above, and you should see the alert statement from ourexample.js file.

If you’ve never seen this syntax before, it can look a little strange. Let’s take it apart piece by piece.

First is the <script/> tag

#File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtml
<script type="text/x-magento-init">
    //...
</script>

This tag is not a javascript tag. Notice the type="text/x-magento-init" attribute. When a browser doesn’t recognize the value in a script’s type tag, it will ignore 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 explore this yourself, this Stack Exchange question and answer is a good place to start.

The other part of the x-magento-init code chunk we can talk about immediately is the following object

#File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtml
{
    "Pulsestorm_JavascriptInitTutorial/example":{}            
}

Magento will look at the key of this object, and include it (the key) as a RequireJS module. That’s what loading our example.js script.

You’re probably wondering why this is an object with no value. You may also be wondering why the whole thing is a part of another object with a * as a key.

#File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtml
{
    "*": {/*...*/}
}           

Before we can talk about that, we’ll need to talk 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 method to invoke a RequireJS module as a program. However, the real power of x-magento-init is the ability to create a Magento Javascript Component.

Magento Javascript Components are RequireJS modules that return a function. Magento’s system code will call this function in a specific way that exposes extra functionality.

If that didn’t make sense, try changing your RequireJS module so it matches the following.

//File: app/code/Pulsestorm/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 assigned it to a variable named mageJsComponent. Then, we return it.

If you reload the page with the above in place, you should see A Simple Magento Component in an alert box.

This 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 changing our phtmltemplate so it matches the following

#File: app/code/Pulsestorm/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">
    {
        "*": {
            "Pulsestorm_JavascriptInitTutorial/example":{"config":"value"}
        }
    }        
</script>

and changing our RequireJS module so it looks like this

//File: app/code/Pulsestorm/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 reload the page, you should see the changed alert message. If you look in your browser’s javascript console, you should also see the following

> Object {config:"value"}

When we create a Magento Javascript Component, Magento calls the returned function andincludes the object from text/x-magento-init.

"Pulsestorm_JavascriptInitTutorial/example":{"config":"value"}         

This is why the RequireJS module name is a key — the value of this object is the object we want to pass to our component.

This may seem like a silly bit of abstraction in these examples. However, in a real module, we’d be generating that JSON with PHP. This system allows us to render the JSON in ourphtml template, and then have that passed to Javascript code. This helps avoid the problem of generating Javascript directly with PHP, which can lead to very messy code, and makes it easy to accidentally slip in an error or security problem.

Before we finish up with x-magento-init, there’s one last feature to discuss. You’ll remember we said that x-magento-init

provides a way to pass that program a server side generated JSON object.

provides a way 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 your RequireJS module so it looks like the following

//File: app/code/Pulsestorm/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 done here is add a second parameter to our mageJsComponent function. This second parameter will be the DOM node we want our program to operate on. However, if you reload the page with the above in place, you’ll see the following in your console.

> Object {config:"value"}
> false

Magento did pass in a value for node — but that value was false. What gives?

Well, Magento can’t magically know which DOM nodes we want to operate on. We need to tell it! Change your phtml template so it matches the following.

#File: app/code/Pulsestorm/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": {
            "Pulsestorm_JavascriptInitTutorial/example":{"config":"value"}          
        }
    }        
</script>

Here, we’ve changed the * to a #one. The * we used previously is actually a special case, for programs that don’t need to operate on DOM nodes. The key for this object is actually a CSS/jQuery style selector that tells Magento which DOM nodes the program inPulsestorm_JavascriptInitTutorial/example should operate on. If we reload our page with the above in place, 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 here as well. Change that to

#File: app/code/Pulsestorm/JavascriptInitTutorial/view/frontend/templates/content.phtml
".foo": {
    "Pulsestorm_JavascriptInitTutorial/example":{"config":"value"}          
}

and you’ll see the following in your console.

> Object {"config":"value"}
> <div id="one" class="foo">Hello World</div>
> Object {"config":"value"}
> <div id="one" class="foo">Goodbye World</div>

By building this sort of system, Magento is encouraging developers to avoid 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 rely on server side rendered JSON, and operate on any arbitrary DOM node. It’s always been possible for Magento module developers to implement 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 another way to invoke similar functionality on a specific DOM node, and that’s with the data-mage-initattribute. Try replacing the existing phtml template with the following

<div data-mage-init='{"Pulsestorm_JavascriptInitTutorial/example": {"another":"example"}}'>A single div</div>

Reload the page with the above in place, you should see the following in your javascript console.

> Object {another: "example"}  
> <div>A single div</div>

Here, we’ve added a data-mage-init attribute to a specific div. This attribute’s value is a JSON object. Similar to x-magento-init, this object’s key is the RequireJS module we want to invoke as a program or Magento Javascript Component, and the value is a nested JSON object to pass into our Magento Javascript Component function as the configparameter.

On a pedantic note — you’ll notice we used single quotes with our attribute

<div data-mage-init='...'>A single div</div>

This is, unfortunately, required. The value of the data-mage-init attribute is parsed by astrict 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 OK in HTML5, for web programmers of a certain age it brings back some bad Microsoft Frontpage memories.

Wrap Up

Whether you end up using the <script type="text/x-magento-init"> component or adata-mage-init attribute in a specific node, both these techniques provide a standard, system unified way of introducing 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 eschew them, understanding how these systems work is an important part of being a Magento 2 developer.

Between these techniques and the base RequireJS implementation in Magento 2, we’re ready to cover our next topic — Magento 2’s use of the KnockoutJS library.