Blog

Prepare for Magento Future

5 minutes reading

A lot of people are talking about the next big e-commerce thing - PWA. If you are not familiar with that term - check this video.

It will be a refresh of existing e-commerce storefront. Every part of front-end will be new, because PWA implementation will not rely on existing one.

To illustrate:

PWA will be implemented like a new application and the only way to communicate with e-commerce platform will be using API - make sure your modules expose all the business logic needed by the front end thru the API.

There are some ongoing development on VueStorefront, Deity and on Magento PWA Studio, but no one knows how they will last and survive updates.

The biggest problem with it is that you can’t just apply it to a specific part of the app - you need to go big or go home. So for projects that are already in production or under development it might be tricky - nothing is backward compatible because front end will be fully in JavaScript, CSS, and HTML - split into components.

But there is a solution for those of you that like me are a bit scared to accept one solution and want to start in small increments.

So to make migration to PWA incremental and start it today we can bridge both worlds.

As we all know Magento 2 ships with Require JS that is an AMD module loader and a lot of other JavaScript goodies - Knockout, Prototype, jQuery that are used to develop cool user interfaces.

If you are following JavaScript world then you might know that those are legacy tools but Magento will keep them in their package with ability to chose for separate PWA application with React and Webpack or default front end with Knockout and RequireJS.

Webpack + AMD = Prepare for PWA

Let’s do this! You can start to create next code in any front-end library you prefer + do it in ES6.

In this example I will use Vue.js because I like its community, good documentation and easy set up. Also, it is easy to plug it into those blocks where you want it without applying it to the whole application. If you are comfortable with React, Angular or other MVVM framework - just go with it.

    "devDependencies": {
      "@babel/core": "^7.0.0-beta.44",
      "@babel/preset-env": "^7.0.0-beta.44",
      "babel-loader": "^7.1.4",
      "vue-template-compiler": "^2.5.16",
      "webpack": "^3.11.0"
    },
    "dependencies": {
      "babel-core": "^7.0.0-beta.32",
      "babel-register": "^6.26.0",
      "vue": "^2.5.16",
      "vue-loader": "^13.7.1",
      "vue-resource": "^1.5.0"
    }

We need to create a new module. In our example Bitbull_Newsletter. Inside its root folder we will set up package.json.

Next up is Webpack. Those who don’t know what’s Webpack, just check the manual. The main thing is to set up libraryTarget to AMD, so our ES6 modules will be exported as AMD and we can use them in RequireJS like every normal JavaScript module. So make sure that on your Webpack configuration webpack.config.js libraryTarget is set correctly!

    output: {
        libraryTarget: 'amd'
    }

Then we need to adapt the template. Here we set the output for Vue.js component and pass needed parameters to make it work.

    <div id="app">
        <newsletter
                ref="newsletter"
                action-url="<?php echo $block->escapeUrl($block->getFormActionUrl()) ?>"
        ></newsletter>
    </div>
    
    <script type="text/x-magento-init">
        {
            "#app": {
                "BitbullNewsletter": {}
            }
        }
    </script>

This will be the main entry point for all ES6 files. As you can see the vue object will be be exported and compiled to AMD thanks to Webpack. So then we can import vue object in our native Magento JavaScript implementation using RequireJS.

import Vue from 'vue';
import Newsletter from './components/Newsletter.vue';
import VueResource from 'vue-resource';
Vue.use(VueResource);

export var vue  = new Vue({
    el: '#app',
    components: {
        'newsletter': Newsletter
    },
    data: {
        translate
    }
});

Now to the Vue.js component side! The first part that we cover will be the template part. Pretty much simple newsletter subscribe template prepared for Vue.js By using translate method the built-in Magento translate AMD module will be used, so no need to do anything additional.

<div class="block newsletter">
    <div class="title"><strong>{{ translate("Newsletter") }}</strong></div>
    <div class="content">
        <div class="form subscribe" id="newsletter-validate-detail">
            <div class="field newsletter">
                <label class="label" for="newsletter"><span>{{ translate("Sign Up for Our Newsletter:") }}</span></label>
                <div class="control">
                    <input v-model="email"
                           name="email" type="email" id="newsletter"
                           v-bind:placeholder="translate('Enter your email address')"
                    />
                </div>
            </div>
            <div class="actions">
                <button v-if="isValidEmail" @click="send" class="action subscribe primary" title="Subscribe"  >
                    <span>{{ translate("Subscribe") }}</span>
                </button>
            </div>
        </div>
    </div>
</div>

This one is ViewModel part that will communicate with the template. Just send, translate method, isValidEmail computed property that shows/hide email field.

export default {
        data: function () {
            return {response: null, email: ''}
        },
        props: ['actionUrl'],
        computed: {
            isValidEmail: function(){..............}
        },
        methods: {
            send: function () {
                if (this.email) {
                    var formData = new FormData();
                    formData.append('email', this.email);
                    this.$http.post(this.actionUrl, formData).then(response => {
                        this.response = response;
                        this.email = '';
                    }, response => {
                        this.response = response;
                    });
                }
            },
            translate: function (value) {
                if (!value) return '';
                return this.$parent.$data.translate(value);
            }
        }
    }

The part that should be familiar for those that already did something with Magento 2 front-end. Here we will connect Webpack exported AMD module in our newsletter AMD module. Basically, two things are going on here. We pass Magento translate the object to Vue.js component. Then we use Vue.js watch method to check for response data property change. This is for loading customer notification fo customer,because there is a need to show website customer a notification on successful or failed subscription action. For that we will use customer-data model that already has a functionality to deal with it.

define([
    './dist/index',
    'Magento_Customer/js/customer-data',
    'mage/translate'
], function (script, customerData,tr) {
    "use strict";
    return function (config) {
        script.vue.$data.translate = tr;
        script.vue.$refs.newsletter.$watch('response', function (newVal, oldVal) {
            customerData.reload('messages');
        });
    }
});

So after this you created one part that will be easier to migrate once you decide migrate to PWA. In conclusion - nothing is changing in PHP side and everything is achievable in incremental steps

GitHub repo

Mārtiņš Saukums