How and why mkdev moved to Vue.js

An illustration of a perplexed chef pulling at a large stretch of noodle dough with a bowl in front, a bag of flour, and garlic cloves on the counter. An illustration of a perplexed chef pulling at a large stretch of noodle dough with a bowl in front, a bag of flour, and garlic cloves on the counter.

Disclaimer: in this article the one who can’t stand the front-end and doesn’t know how to work with it, writes about working with it and choosing JavaScript framework, and why Vue.js is a great choice. You’ve been warned.

I’m very bad with the front-end and don’t like it. The rest of our team has the same feelings. We prefer to focus on the back-end and either outsource the front-end or do it dreadfully ourselves. Then we make it over miserably, again and again, as our co-founder Leonid Suschev doesn’t let us jerk around his designs.

In early 2017 we almost solved the problem with the front-end, as we hired an expert who made the whole site over using BEM, proper grid etc. It’s nearly impossible to start using BEM for a three-year old project in a single sitting. We’re still working on that and when we’re done, we’ll tell about the result here.

But when it came to the problem with our JavaScript code, we didn’t even try to solve it. Mkdev is not a single-page application; we don’t have a huge pile of interactive elements and complex visual editors. In some places we use datepicker, in some there’s a drag & drop technology to sort, for the articles we use the simplest Markdown. Everything is so boring, that we have used ready-made jQuery plug-ins for three years and haven’t given a damn about the rest. Until recently.

99 mistakes one can make while writing the code using jQuery

We have a payment form which can be seen in the profile of any mentor. There’s a slider to choose the amount of weeks in the form. The buttons are dynamically updated according to the slider. Well, over the years the front-end code of this form transformed into the most horrible thing the one can do with jQuery.

Let’s have a look at some of the examples of this horror.

Far too many conditional statements

if (window.location.pathname == "/dashboard/account") {
  $("#button-payment-block, .link-cancel").hide();
} else {
  $("#payment-block, #mentor-payment__login-form-container").hide();
  var value = $("#button-payment-block").text().split(" ").splice(0,3).join(" ");
  $("#button-payment-block").html(value);
}

Hideous string parsing

if ( $('.payment-form__weekly-price').html() ) {
  var arr = $(".payment-form__weekly-price").html().split(" ");
  var current_price_for_week = (options.total / (number_of_days / 7)).toFixed(1);
  arr.splice(3, 1, 'ˆ' + current_price_for_week);
  $('.payment-form__weekly-price').html(arr.join(" "));
}

Constant operations with DOM

$cardForm.show();
$cardForm.find('input[type=submit]').prop("disabled", false);
$('.upgrade-block .upgrade__payment-block, .upgrade-block .upgrade__title, .upgrade-block .upgrade__info').hide();
$('#upgrade__payment-block--payment-button').hide();
$('.upgrade-block').css({'border': 'none'});


There are 284 lines of such unbearable mingle, which is absolutely impossible to grasp for anybody. The added complication is that element IDs are not even close to the point of this element: #upgrade__payment-block--payment-button might turn up to be a button to upgrade the subscrption or just a payment button. So go figure.

So our dear reader hopefully got an idea about how enormous the disaster is.

Which JavaScript framework to choose

We were considering two options:

  1. Rewriting all the code from scratch, using the same technologies, but doing it properly now;
  2. Choosing any library, which will allow us to write good code out-of-the-box.

We were afraid that if we chose the first option, in a year we wouldn’t be able to understand our own code again. So we chose the second one. We needed a library or a framework, which:

  1. Could be implemented iteratively, only for the smallest parts of frontend;
  2. Being out-of-the-box, would be powerful enough to solve all our problems without further ado;
  3. Would be simple enough to grasp it decently for a couple of weekend hours.

We chose it right away and without any second thought. Frankly speaking, the choice was done right after we read the wonderful Ivan Shamatov’s article Rails 5 & Vue.js: how to stop worrying and love the frontend. Looking through the documentation to Vue.js, I decided to give it a shot.

Webpacker, PostCSS, Babel, Yarn, go away

Taking the Ivan Shamatov’s advice into account, I ran Rails generator for all the trendy frontend things, hoping to implement webpack real quick. Unfortunately, it generated also babel and postcss on top that, which I didn’t plan to generate at all. Moreover, it turned out that I have to use Yarn now. On this stage I ran out of my patience so I just eliminated all the junk generated by Rails.

Stop saying that, Kirill! It’s not junk, these are the most important tools of the modern front-end development!!11

Yean, I agree. I guess the time for ‘modern’ front-end development for mkdev code hasn’t come yet. The standard assets pipeline works fine for us, as well as loading the third-party js-libraries through Gems or adding vendor/assets. We hate implementing dozens of new technologies, just because it’s cool and trendy.

I took vuejs-rails and added another couple of libraries (e.g. for slider) directly to vendor/assets. This approach has its own disadvantages for sure, but they are not really significant at this time.

Falling in love with Vue.js

I’m not going to teach you how to use Vue.js – it has superior documentation. But if you want to master it (especially combined with Ruby on Rails), you should hire Ivan Shamatov as your mentor. So in the rest of the article you’ll see how we implemented Vue.js to mkdev.me, our experience and feelings.

Learning curve of Vue.js

Having spent about 6-7 hours in total on Vue.js, I managed to completely rewrite the whole front-end part of our payment form using Vue.js and to send it to production. Not so shabby, keeping in mind that the technology is absolutely new for me.

Code in Vue.js is by far shorter and clearer

If we estimate the amount of code lines, we’ll see that payments.js has been cut to 166 lines. More than 30 of them are used for the slider component appearance. The component is nice, but it forces us to describe its appearance right in the js code for reasons unknown.

This Vue.js component just defines its properties and methods. Without any unnecessary details, here’s how our payment form looks right now:

 Vue.component('payment-form', {
      template: '#payment-form-template',
      props: [
        'initial_upgrade',
        'mentor_id',
        'initial_number_of_weeks',
        'current_user_is_present',
        'has_mentor'
      ],
      data: function() {
        return {
          coupon: "",
          error_message: "",
          final_form_visible: this.mentor_id == null, // Either login or card details form
          loading: false,
          number_of_weeks: this.initial_number_of_weeks,
          price: 0,
          upgrade: this.initial_upgrade,
          upgrade_notice_visible: this.initial_upgrade,
          weekly_price: 0,
          card: {
            // ...
          },
          slider: {
            // ...
          }
        }
      },
      components: {
        'vueSlider': window['vue-slider-component'],
        'vueMaskedInput': window['VueTheMask']
      },
      computed: {
        can_proceed_to_final_form: function() {
          return !(this.number_of_weeks == 0 || (this.has_mentor && this.mentor_id != null));
        }
      },
      watch: {
        number_of_weeks: function(val) { this.setPrice(); },
        coupon: function(val) { this.setPrice(); },
        price: function(val) {
          this.weekly_price = (this.price / this.number_of_weeks).toFixed(1);
        }
      },
      methods: {
        setPrice: function() {
          // ...
        },
        getStripeToken: function(event) {
          // ...
        },
        showFinalForm: function(event) {
          // ...
        },
        toggleViews: function(event) {
          // ...
        }
      },
      created: function () {
        this.setPrice();
      }
    })

In the template we indicate where to output these properties and which method to request with which action. For example:

<a href="#"
   class="payment__button button button--centered"
   v-bind:class="[can_proceed_to_final_form ? '' : 'button--disabled']"
   v-if="!final_form_visible"
   v-on:click.prevent="showFinalForm">
  <%= t ".enroll_for", price: "{{price}}" %>
</a>
<! --- >
<template v-if="final_form_visible">
  <p class="payment__notice payment__notice--centered">
    <%= raw t '.to_continue' %>
  </p>
  <%= render "login_form" %>
  <%= link_to t('.cancel'), "#",
              class: "payment__cancel-link",
              "v-if" => "final_form_visible",
              "v-on:click.prevent" => "final_form_visible = false" %>
</template>

The elements hide by themselves and are shown according to the component properties and binding events to the elements (clicking the button) is occuring right in the template.

It’s not so convenient to reference the components without the webpacker, but it’s not a big deal

Ideally, Vue.js components are stored each in a separate.vue file. The template, code and even CSS are also stored here. Leaving the webpacker behind, we (presumably) have lost an ability to do so. Luckily, Vue.js offers some alternative ways to define the component. X-Templates way suited us and our system with the assets pipeline perfectly:

<script type="text/x-template" id="payment-form-template">
  <div class="payment"
       v-bind:class="[upgrade_notice_visible ? 'payment--large' : '']">
<! -- >
</script>

Vue.js is very handy to incorporate part by part

I’ve already mentioned it a little, but I’d better note that once again: Vue.js doesn’t make you rewrite all the code. We took just one component, a payment form, and used Vue.js. All the rest wasn’t changed.

There are millions of plug-ins for Vue.js

I was afraid not to find the same amount of ready-made solutions for Vue.js, as I had for jQuery. I shouldn’t have to. It turned out that there were a lot of components for the slider, for the masked input. Having searched the Internet, I also found the components for the datepicker, for the tables and whatnot.

Better tomorrow for the mkdev.me front-end

Let’s sum it up.

I managed to rewrite the most complicated front-end component of our project using Vue.js in no time. As a result, the code got simpler, clearer and more supportable. Vue.js is a great, simple and fast tool, which doesn’t make you to learn new architecture. The experience is the same as it was with jQuery: you just code following the simplest rules and it goes pretty well.

Sure enough, over the time jQuery shoots you in the feet, and maybe Vue.js will, too. But for now it successfully healed the wounds inflicted by its predecessor.

Of course, we haven’t mastered Vue.js yet. We have also planned a couple of dozens of minor fixes and improvements for our payment form code. Then we’re going to get rid of jQuery step by step and move to Vue.js components completely. Maybe we will even implement Webpack in the end.

Another bonus from Vue.js is that our mkdev developers are interested in examining it, working with it and implementing it. And this is a new page in the mkdev front-end development.