Re-writing dcidr.org with Vue 2


I just finished re-writing my dcidr.org app again, this time with vue 2.

dcidr.org

It's my favorite pet project for learning new programming languages and libraries because:

I've re-built this app many different ways over the years: server-side web apps, command-line tools, mobile apps, spreadsheets, JS apps, etc. The latest generation was using Knockout circa 2014 so this time I decided to try out Vue with ES2015.

Why Vue?

Vue prides itself on having a path from simple use cases to more complex ones. All you really need to do in order to start using Vue is something like this:


<html>
    <body>
        <div id="app">{{ message }}</div>

        <script src="https://unpkg.com/vue"></script>
        <script>
            var app = new Vue({
                el: '#app',
                data: {
                    message: 'Hello Vue!'
                }
            });
        </script>
    </body>
</html>

When it's time to bring in a router or state management, you can use official plugins/libraries that work well with Vue or choose a 3rd party library. React and Knockout are same way (merely view libraries) and it's up to the developer to bring in whatever else they need. This is very valuable because sometimes I just need to add some basic interactivity to server-rendered pages and other times when I need a full-blown SPA.

Vue is very similar to React in that is uses a virtual DOM, is component-centric, and promotes unidirrectional data flow. The biggest difference is how it does data-binding and how templates work which we will dig into.

App Overview & Structure

The app has these key features:

I structured it like so:

dcidr.org diagram

Nothing innovative here! The central state manager has one Decision object. Components in the app trigger mutation events which the store listens for. The store synchronously updates its decision object and the updated state is passed down to all components who are listening.

The Router

I like delcarative routers and this one is very full-featured but my needs were simple. This is what my router looked like:


// imports omitted 
Vue.use(VueRouter);

const router = new VueRouter({
  routes : [
    { path: ROUTES.HOME, component: WelcomeComponent },
    { path: ROUTES.OPTIONS, component: OptionsComponent },
    { path: ROUTES.CRITERIA, component: CriteriaComponent },
    { path: ROUTES.OPTION_COMPARISONS, component: OptionComparisonListComponent },
    { path: ROUTES.CRITERIA_COMPARISONS, component: CriteriaComparisonListComponent },
    { path: ROUTES.REPORT, component: ReportComponent },
    { path: ROUTES.SAVE, component: SavePromptComponent },
    { path: ROUTES.ARCHIVE, component: ArchivedDecisionsComponent},
    { path: '*', redirect: '/' }  // default route
  ]
});

export default router;

My main app view model was just a container for the current route and the HTML for it was just like so:


<div id="app" v-cloak>
    <transition name="fade" mode="out-in" appear>
        <router-view></router-view>
    </transition> 
</div>

That <transition> element is vue's built-in transition support which I found to be extremely well-designed and documented. Simply wrap any element which would be dynamically added/removed via Vue (e.g. has a v-if binding) and vue will automatically provide support for CSS transitions and animations.

State Management

I probably could have found a way to re-write dcidr without central state management but my previous version used a message bus technique already so it was a natural fit. Vuex does state management very similarly to redux where components trigger synchronous mutations on a central state object which get propogated to listeners. Vuex provides "actions" which allows you do work with asynchronous operations, which I didn't need.

Vuex offers some helpful time-savers like getters which are a kind of shorthand for creating computed poperties based on your state.

Here's a sample of what my store looked like.


// imports omitted 

Vue.use(Vuex);

const store = new Vuex.Store({
  state: {
    decision: null
  },
  mutations: {
    [MUTATIONS.NEW_DECISION] (state) {
      state.decision = new Decision();
    },
    [MUTATIONS.ADD_OPTION] (state, name) {
      state.decision.addOption(name);
    },
  },
  getters: {
    gates: state => {
      if(!state.decision) return null;
      return state.decision.gates;
    }
  }
});

export default store;

Data-Binding

Every property on your ViewModel you want to bind to must be defined in the .data property. Vue proxies these properties and allows you to optionally define some basic validations. You can also define .computed properties as functions. Here is an example:


const ViewModel = Vue.extend({
  data : {
    firstName: '',
    lastName: ''   
  },
  computed: {
      fullName: function(){
          return `${this.firstName} ${this.lastName}`;
      }
  }
});

Every property in .data is available on the root of the ViewModel object, as is every function in the .computed object. Notice how in the fullName() computed that it's referencing this.firstName instead of this.data.firstName.

This is very MVVM-ish if you ask me.

Vue components are slightly different in that .data is a factory function instead of an object and components can receive props (declarative bindings from parent components) which are treated as a one-way top-down data flow like in React.

Templates

View templates are essentially HTML which gets converted to a render function by the Vue engine (at design time or run time, your choice). You can write this HTML in whatever way works best for you. If your team wants to keep the component logic and templates in the same file, they can use single-file components or simply use string literals in the Vue component JS. If they want to keep the HTML separate from the JS, like me, they can use HTML references (el: '#app') or string literals sourced from html files and imported via webpack's html-loader.

Here are some quick examples of the Vue template syntax to get a taste.


<!-- handlebars are for text content, you can use filters in the expressions too -->
<div>{{ something | capitalize }}</div>

<!-- v-bind directive is for HTML attributes, shorthand notation is :href -->
<a v-bind.href="something">click me</a>

<!-- v-on directive is for events, shorthand notation is @click -->
<button v-on.click="myMethod">click me</button>

Each component you create becomes a "custom element" (e.g. <my-widget>). Components can also have one or more <slot> elements in their template for the purposes of content distribution, otherwise and infamously known in the Angular 1.x world as transclusion. Here is a quick example...


<!-- my-component template -->
<section>
    <slot>
</section>

<!-- my-app template -->
<my-component>
    <strong>Hello there!</strong>
</my-component>

<!-- rendered -->
<section>
    <strong>Hello there!</strong>
</section>

Tradeoffs & Conclusion

The key strengths for Vue IMO were:

The key downsides for me were:

Overall I enjoyed using Vue. I like some of the design choices (MVVM, component-based, "progressive", minimal lock-in) and the overall quality of the library. The dcidr code base is much better as a result and I'm confident I will understand it a year from now when I go to re-write it again. :)

TechBash 2016 After-Action Report


I just left the first ever TechBash software development conference at the Kalahari water park resort in the Pocono mountains. It was a community-run event with about 150 attendees and the sessions mostly focused on Microsoft tech.

open source talk

Logistics

The resort and conference center was great (as expected). The waterpark is huge and once they finish the second phase of construction, it will be the largest in North America.

water park

Sessions

You can check out the session content on GitHub. Here are notes on some of the sessions I attended.

Keynote: A few of my favorite things

Pete Brown from MSFT demonstrated a bunch of the latest IoT stuff all the the theme of 80's nostalgia. He capped it off with a homage to Stranger Things by blinking some lights like they did on the show.

internet of stranger things

Developing for Mixed Reality with HoloLens

Overview of the HoloLens product and developer experience. I got to try one out very briefly and it was very impressive. The holographic field of view is small so you need to move your head around a lot. At $3,000 I think it will be much more successful in industrial and marketing (i.e. showroom) applications then as a consumer device.

hololens

Instrumenting apps with the ELK stack

The ELK (or Elastic) stack is ElasticSearch, LogStash, and Kibana. Together they help solve the problem of collecting log files from many servers and turning that raw data into actionable information. The flow was log4net -> LogStash (collects and processed logs) -> ElasticSearch (JSON document search engine) -> Kibana (dashboard and ad-hoc query app). All these tools work very well together.

Other sessions (that went well)

Conclusion

Overall TechBash was an afforable and fun community developer event. The organizers (who did a great job) announced that TechBash 2017 will go down October 4-6. I expect more people to attend next year as word gets out.

My feedback for the organizers was that they should ask speakers to indicate the depth / requisite experience for their talks. I found myself in way too many introductory talks than I expected.

The laid-back, friendly vibe of the conference wast the best part. Will I be there next year? I hope so.

Group Shot

How To Clean Up Data With R


Working with data almost always means working with bad data: the wrong format, missing values, inconsistent units of measure, typos, you name it. I've heard it said many times that data scientists, the high priests of machine learning and statistics, spend a majority of their time cleaning up bad data sets.

We software developers can use all sorts of general purpose programming languages to solve pretty much any data transformation problem. However, the average business person pretty much has Excel, elbow grease, and maybe some macros if they're crafty. As the volume of data in our world continues to explode, the ability for business people to independently deal with it becomes increasingly valuable.

At CodeMash I saw a great talk by Matthew Renze about "Exploratory Data Analysis with R" (he also has a corresponding Pluralsight course on the topic which I highly recommend). The whole time I was watching this talk I was thinking "all business people should learn this right now!"

R is a special-purpose programming language for statistics. It has lots of great capabilities and libraries for fancy things like linear regression, data visualization, machine learning, k-means clustering and so on. It also happens to be pretty great for basic data manipulation tasks. Here's a simple example based on Renze's talk...

The Bad Data

Let's say we work at a large mobile carrier and have some sales data to clean up:

Fixed Data

The problems are...

The Script

Here's how you can solve all these problems with a basic R script.

    
# set working directory
setwd("C:/code/R/blog")

# load data
phones <- read.csv(
  file = "phones.bad.csv",
  header = TRUE,
  na.strings=c("")
)

# PROBLEM: omit rows with missing data
phones <- na.omit(phones)

# PROBLEM: remove "hrs" from the Battery since it's bad for math
# fix it by converting it to a character type
battery <- as.character(phones$Battery)
# then removing the "hrs"
battery <- sub(" hrs", "", battery)
# then converting back to an int
phones$Battery <- as.integer(battery)

# PROBLEM: inconsistant units of measure
# write a function to normalize to millions
convertRevenureToMillions <- function(rev)
{
  stringRev <- as.character(rev)
  revNumStrOnly <- gsub("[$|K|M]", "", stringRev)
  numericRev <- as.numeric(revNumStrOnly)
  if(grepl("M", rev)) {
    numericRev
  }else if(grepl("K", rev)){
    numericRev * 0.001
  }
}
# then apply it
phones$Revenue <- sapply(phones$Revenue, convertRevenureToMillions)
# let's be a good citizen and rename the Revenue column
names(phones)[2] <- "RevenueMillions"

# ALL DONE!
write.csv(
  phones, 
  "phones.fixed.csv",  
  row.names=FALSE
)
    

The Output

And presto, here's your fixed data...

Fixed Data

Conclusion

R can be a very useful tool for cleaning up data because it's optimized for working with data in vector (array) and frame (table) structures. Reading and writing data is straightforward, as is applying functions to data sets. I didn't touch on any of the analytical features, which is R is primarily known for.

R is built for statisticians by statisticians so it has a bit of a learning curve but if you use Excel frequently to deal with bad data you should seriously consider checking it out.

[ Archive · Home ]