bekwam courses

Vuex Demo

February 2, 2019

This article demonstrates Vuex which is a state management library used by Vue.js. If each component in an application manages its own data, there's often inconsistency among shared items. This leads to bugs. Vuex gives the developer the ability to factor out common data and to provide governance. The result is an application that has a single source for data and enforces data access rules.

The code here is my re-creation of the example in the Intro to Vuex video at Vuemastery.com.

This video shows a web page with form controls at the top for adding a Todo item. When the Add button is pressed, the text in the input control is added to the bottom of the page in a list.

At the start of the video, the Vue extension shows a root Vue instance containing two components: TodoForm and Todos. While it's not difficult to imagine a Javascript function attached to the Add button that appends an HTML <li> element directly to the DOM, this article is going to highlight an important separation. Apps of any significant size need to be broken down into smaller units to help with development and maintenance. Vue components provide functions and a standard for doing this. Each component has a well-defined role and that role can be tested and debugged independently as it is assembled into the larger whole.

A Vuex store provides additional separation. Like the components, Vuex has a well-defined role to manage data. Vue components allow you to manage data too (data and methods), but sharing data can get a little tricky even with props. The Vuex store lets the components "outsource" the data management to a shared component. This is sometimes called factoring or refactoring. Think of pulling out a common "3" from the expression 3x+3 to produce 3(x+1). I'm pulling out the data and methods from each component and putting them into the Vuex store.

This UML class model shows the four parts of the application: Vue app instance, TodoForm component, Todos component, and Vuex Store. If you're not familiar with UML, this particular diagram called a Class Model shows the static relationships among these parts. The arrows indicate a "knows of" relationship and not a data flow. A big trend in app architecture is to maintain one-way relationships which helps avoid side effects rippling throughout the application. This is adopted by having TodoForm and Todos reference Store, but Store does not reference either component. Also, Store and the app instance are not related whatsoever.

UML Class Diagram
App Instance, Components, and Vuex Relationships

HTML

The index.html file loads the Javascript and stylesheet. It contains a single div "app" which will be the el target of the Vue instance.

index.html


<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Todo Demo</title>
<link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
	<div id="app"></div>
	<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
	<script src="https://unpkg.com/vuex"></script>
	<script src="main.js"></script>
</body>
</html>

Javascript

The Vue app instance contains a template field that is a container for the two Vue components. The components are registered. There is not mention of "Store" or "store" in this declaration and this helps strengthen the app design through information hiding. (If an app instance or component doesn't know about another part, it won't misuse it.)

main.js


new Vue({
    el: '#app',
    template: `
      <div id="wrapper">
        <h1>Todo Demo</h1>
        <TodoForm />
        <hr>
        <Todos />
      </div>
    `,
    components: { TodoForm, Todos }
  })

The TodoForm component contains the input text control and button which will initiate an add operation. There is a local variable data declared which, through Vue data binding, will be updated with the contents of the text control. The add method will use this value as an argument to a Vue Store commit operation. Notice the reference to the store which is a convenient Store integration provided in Vue.js.

main.js (continued)


const TodoForm = {
  template: `
  <fieldset>
    &l;label for="todoItem">New Todo</label>
    <input type="text" v-model="todoItem" id="todoItem">
    <button v-on:click="add">Todo>/button>
  </fieldset>
  `,
  store,
  data() {
    return {
      todoItem: ''
    }
  },
  methods: {
    add() {
      this.$store.commit('addItem', this.todoItem)
    } 
  }
}

TodoForm's add calls commit rather than a direct manipulation of the Store's data. This gives the framework the opportunity to broadcast updates to interested parties among other things.

The Todos component is an initially empty <ul>. As new items are added in the TodosForm, the Vue store will be modified. That will trigger an Store update and broadcast the changes to Todos. TodosForm does not directly interact with Todos. This means that additional components can be added to tap into changes coming from TodosForm. For example, if there were a lot of Todos, I could add a TodosCounter component to display the number added at the top of the list in case there are items scrolled off the screen.

main.js (continued)


const Todos = {
  template: `
    <div>
      <h2>List of Todos</h2>
      <ul>
        <li v-for="item in todos">{{ item }}</li>
      </ul>
    </div>`,
  store,
  computed: {
    todos() {
      return this.$store.state.todos
    }
  }
}

Finally, there is the Vuex Store object. This is managing a single String[] "todos". Vuex is handling all of the complexity in sharing the updates to interested components and the only responsibility of the developer is to do a standard push() in the mutation.

main.js (continued)


const store = new Vuex.Store({
  state: {
    todos: []
  },
  mutations: {
    addItem(state, item) {
      state.todos.push( item )
    }
  }
})

There is a stylesheet used in the video. It's not important to the functionality of the Vue components and Vuex Store.

styles.css

    
#wrapper {
    width: 960px;
    margin: 0 auto;
}

fieldset div {
    display:  flex;
    align-items: center;
    padding: 1em;
}

label {
    order: 1;
    width: 10em;
    padding-right: 0.5em;
}

input, select {
    order: 2;
    flex: 1 1 auto;
}
    

In such a small program, it might not seem important to divide it into smaller pieces. However, as your program grows larger, smaller units are essential to keeping development and maintenance on track. Small units with cohesive roles mean that troubleshooting can be very focused. It gives you some assurance that assembling these smaller units into the larger program will be successful. Vuex factors out shared data into a common component and gives the app a single version of the data while controlling access using consistent rules.


Headshot of Carl Walker

By Carl Walker

President and Principal Consultant of Bekwam, Inc