Improving interactivity for legacy front-end applications using Vue.js

Introduction

Who am I?

  • Full stack programmer
  • Got started in backend, more recently a javascript enthusiast
  • Work at Position Development!

Thanks!

  • My co-worker Libby motivated me to do this talk!
  • Thanks to GothamSass for having me!

Motivation

Magazine Subscription Page

Examples of interactions required:

  • Show different price based on "print" or "digital"
  • Show higher price if subscription is shipped internationally
  • Hide address fields if subscription is "digital" only
  • Show discounted cost if URL contains discount code

I need to add:

  • Ability to select a subscription term (1, 2, 3 yr)
  • When a user chooses the "International" shipping option, they see a dropdown to choose their country in the address section

...to code that looks like:

  function amount_calc() {
    var c = $(".type-choice input:checked");
    var shipping = 0;
    var digital_desc = "";
    if (c.attr("data-ship") === "1") {
      $(".shipping-choice").removeClass("inactive");
      $(".shipping-choice input").removeAttr("disabled");
      if (!is_gift()) {
        $(".shipping-information").show();
      }
      var sc = $(".shipping-choice input:checked");
      shipping = Number(sc.attr("data-amount"));
      digital_desc = " (" + sc.parent().find(".desc").text() + ")";
    } else {
      $(".shipping-choice").addClass("inactive");
      $(".shipping-choice input").attr("disabled", "disabled");
      $(".shipping-information").hide();
    }
    var discount = 0;

    function format_money(d) {
      var pad = "";
      if (d % 100 < 10) {
        pad = "0";
      }
      return Math.floor(d / 100) + "." + pad + (d % 100);
    }

    if ($(".coupon-info").length != 0) {
      if (c.attr("data-ship") === "1") {
        var d = $(".coupon-info").attr("data-" + c.attr("data-type") + "-" + sc.attr("data-code"));
      } else {
        var d = $(".coupon-info").attr("data-" + c.attr("data-type"));
      }

      if ($(".coupon-info").attr("data-gift-only") === "true") {
        if (is_gift()) {
          var gift_passed = true;
        } else {
          var gift_passed = false;
        }
      } else {
        var gift_passed = true;
      }

      if (typeof d !== "undefined" && d !== "0" && gift_passed) {
        discount = Number(d)
        $(".coupon-amount").text(" - $" + format_money(d) + " Off");
      } else {
        $(".coupon-amount").text(" - You can't use this coupon with that selection.");
      }
    }

    var cents = Number(c.attr("data-amount")) + shipping - discount;
    var amount = format_money(cents);
    $(".total .amount").text(amount);
                

What do I do?

The existing jQuery is hard to understand/modify

I'm worried about the next set of requirements we'll need to accomodate

I don't have a lot of extra time to spend on this simple request

I love React but I can't use it here

  • Don't have bundling or babel set up for javascript
  • The divs are already rendered by the server
  • I don't have a JSON API...

Vue!

First Steps

  • Chose to check out Vue based on State of JS Survey
  • Spent an afternoon prototyping and got surprisingly far!
source: http://stateofjs.com/2016/frontend/

Code Examples!

Hello World

                    
                        

                        
{{ display_text }}
                    new Vue({
  el: '#my-component',
  data: {
    display_text: 'Hello World!'
  }
}) 
                

A Simple Toggle

                    
                        

                        
{{ display_text }}
                    new Vue({
  el: '#my-component',
  data: {
    display_text: 'Hello World!',
    show_display: true
  }
}) 
                

Displaying Computed Data

                    
                        

                        
Subtotal: {{ subtotal }}
                    new Vue({
  el: '#my-component',
  data: {
    expedite_shipping: false,
    add_donation: false
  },
  computed: {
    shipping_subtotal: function() {
      if (this.expedite_shipping) {
        return 5
      } else {
        return 1
      }
    },
    donation_subtotal: function() {
      if (this.add_donation) {
        return 10
      } else {
        return 0
      }
    },
    subtotal: function() {
      return 10 + this.shipping_subtotal + this.donation_subtotal
    }
  }
})
                

Communicating with the Backend

                    

                        
Subtotal: {{ subtotal }}
                    new Vue({
  el: '#my-component',
  data: {
    expedite_shipping: false, add_donation: false,
    shipping_cost: {reg: 0, exp: 0}, donation_cost: 0, base_cost: 0
  },
  mounted: function() {
    this.shipping_cost = {
      reg: parseInt(this.$refs.shipping_toggle.dataset.reg),
      exp: parseInt(this.$refs.shipping_toggle.dataset.exp)
    };
    this.donation_cost = parseInt(this.$refs.donation_toggle.dataset.amount);
    this.base_cost = parseInt(this.$refs.base_cost.dataset.amount);
  },
  computed: {
    shipping_subtotal: function() {
      return this.expedite_shipping ? this.shipping_cost.exp : this.shipping_cost.reg
    },
    donation_subtotal: function() {
      return this.add_donation ? this.donation_cost : 0
    },
    subtotal: function() {
      return this.base_cost + this.shipping_subtotal + this.donation_subtotal;
    }
  }
})
                    
                

CodePens!

"The Pattern"

  1. Somewhere (in 1 place!), we have the data
  2. User input can change the data
  3. Rendering of DOM is automatic and derived from the data

What's Next?

  • Vue all the (legacy|jQuery) things
  • Empower designers we work with to create more interactive designs!
  • Try out SPA structure for new projects with Vue (!!)

Questions?