DEV Community

Bilal Haidar for This Dot

Posted on • Updated on

Vue.js for Angular Developers

When writing, I focus on Angular and full-stack development, with Angular for the frontend.

Recently, I had the opportunity to write a proof-of-concept app using Vue.js to create a basic Todo app using Vue.js and the Vue CLI.

Let's take a look, go over the app, and explore Vue.js. We will also compare some of the concepts and techniques in Vue.js to those in the Angular world.

In brief, whether you are developing Angular or Vue apps, there are some basic concepts and techniques, related to architecturing or structuring your applications, that can be applied to both. For instance, the Smart/Presentational components is universal and applies to both worlds.

Now, the tools differ for sure. Take for example Vuex, which is the Vue.js library for client-side state management. You may compare that to NGRX or any other Angular based state management library.

The major point of this article is that, whatever framework you use to develop your apps, the concepts are yours, and you can take them with you whether you are developing in Vue or Angular.

Here’s a sneak peek at the final product we will build together today:

The source code of this article is available at this GitHub repo.
Let’s start coding the Todo app!

Create Vue app

To start, make sure you install the latest version of the Vue CLI by running this command:
yarn global add @vue/cli

The command downloads and installs the latest bits of the Vue CLI on your machine.

After that, I will create a new Vue.js app using the Vue CLI UI instead of the command line.

Run the following command: vue ui

This command opens a new browser, as shown here:

The home page of the Vue UI allows you to manage your existing projects, create new ones, and import existing projects.

Click on the Create tab, and you will see something similar to this:

Change the system path to the directory where you want to create a new project. In my case, I’ve chosen the path D:\Projects

Locate and click on the Create a new project here button. A new page opens as shown below:

On this page you specify the following:

  • Project folder: todo-app
  • Project manager: yarn
  • Git repository: On

Then, click the Next button to go to the page where you select a preset.

You can choose the Default preset to create the app with default features and options. I’ve chosen the Manual option so that I can customize my app generation.

Locate and click on the Next button to go to the page where you select what features you wish to include in the app.

For this app, I've chosen to use the following features:

  • Babel
  • Vuex (for state management)
  • CSS Preprocessors
  • Linter/Formatter

Once done, locate and click on the Next button to go to the page where you have to configure the CSS preprocessors since you’ve added this feature.

In my case, I will pick the Sass/SCSS (with node-sass) for the CSS Preprocessor.

As for linting and formatting, I’ve decided to use ESLint + Prettier to combine both amazing worlds together!

Finally, I want my project to get linted whenever I save a file.

Click the Create Project button to start generating your project files.

NB: Before the project file generation process starts, a prompt will appear asking if you'd like to save your current settings as a preset. This saves all the preferences and configurations for future use if you choose. For now, I will just continue without saving it as a preset.

Once the generation process ends, you get the following project created:

Public folder contains the index.html file that will be used to run your app.
Src folder contains all of your Vue components and any assets like CSS or images.
The rest are configuration files.

Now that the application is generated, let’s start building the app source code.

Anatomy of a Vue.js app

Let’s explore the main.ts file and check how a Vue.js app is started:

import Vue from 'vue'
import App from './App.vue'
import store from './store'

new Vue({
  store,
  render: h => h(App)
}).$mount('#app')
Enter fullscreen mode Exit fullscreen mode

The file creates a new instance of the main Vue component passing it an object parameter containing configuration settings.

The first argument of the input parameter is the store object that is imported from ./store file.

The store object is injected at this level so that the store object is available to all children components.

The render() method returns the app.vue root component to render for this app.

Finally, the $mount(#app) method is called with a DOM element so that Vue renders the root component in this DOM element inside the index.html page and activates the app.⁰

<div id="app"></div>
Enter fullscreen mode Exit fullscreen mode

Let’s explore the store we will be using for this app.

Vuex - State Management Simplified

At the core of Vuex is the Store object to help with state management for your Vue apps:

The store is:

  • Reactive by nature. Any change that occurs inside the store is automatically reflected inside the Vue components.
  • You cannot directly mutate the store’s state. You do that by committing a mutation to update the store’s state via a pure-function without any side-effects.

To trigger a mutation you dispatch an action.

Let’s look at how the Todo app store looks like. You can check the whole store implementation on this GitHub repo.

The store.js file starts by registering Vuex as a plugin to the application so you can access the store’s state later on:
Vue.use(Vuex)

The code then creates a new instance of the Vuex.Store() object passing an object parameter to configure the state, mutations, actions, and getters of the store.

The state is defined as follows:

state: {
    todos: []
  },
Enter fullscreen mode Exit fullscreen mode

The state in this app is tracking the list of todo items.

Let's discuss a single mutation in this store:

ADD_TODO(state, todo) {
      state.todos = [
        { body: todo, completed: false, id: generateRandom(1, 1000) },
        ...state.todos
      ]
    }
Enter fullscreen mode Exit fullscreen mode

A mutation accepts, as input parameters, the existing state object in addition to the todo item to be added to the store’s state.

You can check the rest of mutations in the source code of the store referenced above.

In NGRX - Reactive State for Angular, all the mutations are grouped inside a Reducer. This reducer holds all the mutations and based on the action dispatched, a single mutation is executed.

Let’s continue with the actions of a store.

addTodo({ commit }, todo) {
  commit('ADD_TODO', todo)
}
Enter fullscreen mode Exit fullscreen mode

An action method receives the store object as an input together with the action payload. In the code, the addTodo() action extracts just the commit method from the store object. It then calls the commit() method, passing as parameters, the name of the mutation together with the todo item, to save in the store’s state. So, dispatching an action commits a new mutation to update the store’s state.

Similar to NGRX an action triggers the Reducer to execute a mutation on the store.

Finally, the getters defined on the store object are convenience methods added to allow components to reactively read data from the store’s state. Whenever the store’s state changes, the components listening to the getters will be notified and updated automatically.

Here’s an example getter used in the app:

pendingTodos: state => state.todos.filter(todo => !todo.completed)
Enter fullscreen mode Exit fullscreen mode

pendingTodos getter, given a state, filters out all completed todo items and returns only those that are still pending.

Vuex Getters are equivalent to Selectors in NGRX.

Now that we know how the Vuex Store works, let’s move on and start building the UI and functionality of the todo app.

Building the Todo app

Let’s start at the App.vue root component of the app:

The component defines a main header for the home page:

<header>
      <div class="navbar navbar-dark bg-dark box-shadow">
        <div class="container d-flex justify-content-between">
          <TodoBrand />
        </div>
      </div>
</header>
Enter fullscreen mode Exit fullscreen mode

The app is using [Bootstrap 4] for the layout, and I’ve added the following two lines to add support for Bootstrap in the app.

Locate the public\index.html and add the following to the <head> of the document:

 <link href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"
    integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
  <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.0/css/all.css"
    integrity="sha384-lZN37f5QGtY3VHgisS14W3ExzMWZxybE1SJSEsQp9S+oqd12jhcu+A56Ebc1zFSJ" crossorigin="anonymous">
Enter fullscreen mode Exit fullscreen mode

The head of the App.vue component defines a custom TodoBrand Vue.js component to render the brand section of the page.

TodoBrand component

Create a new Vue.js component under the path src\components\TodoBrand.vue with the following content:

This renders a Todo SVG icon together with the Todo header text.

Now you import the TodoBrand component inside the root App.vue component:

import TodoBrand from '@/components/TodoBrand.vue'

After that you need to inform App.vue about this new component by adding it to the list of components managed by App.vue as follows:

components: {
   TodoBrand
}
Enter fullscreen mode Exit fullscreen mode

The body of the App.vue component has 3 main sections

The header text section showing the number of pending todo items in the store’s state.

<h4>
    You have
    <strong>{{ pendingTodos.length }}</strong> pending todo(s)
</h4>
Enter fullscreen mode Exit fullscreen mode

The snippet above uses a one-way data binding to bind the pendingTodos computed variable defined inside the code of the component as follows:

computed: {
    pendingTodos() {
      return this.$store.getters.pendingTodos
    }
  }
Enter fullscreen mode Exit fullscreen mode

A computed property in Vue.js is reevaluated every time one of the fields used inside a computed property has changed.

In this example, whenever the this.$store.getters.pendingTodos results change, this computed variable is reevaluated.

Doesn't this remind you of RxJS in Angular? The reactivity nature of both technologies?

TodoAdd component

The App.vue then renders the TodoAdd component that, in turn, displays an input field with a button to allow the user to add a new todo item.

<TodoAdd @add-todo="addTodo" />

Create this component inside the src\components folder, and then import it to the App.vue component, and finally add it to the list of components managed by this root component.

The TodoAdd component exposes a single output event the add-todo event. This event is triggered by the TodoAdd component whenever the user adds a new todo item.

You would ask yourself why am I handling this event at the App.vue component rather than inside the TodoAdd component itself?

I am following the Smart/Presentational concept whereby the Presentational TodoAdd component handles the DOM events of the input field, and button, and emits a new event to the Smart App.vue component. The event handler at the App.vue component level handles this event by communicating directly to the store.

All presentational components have no access to the store object itself. Everything is delegated to the smart components.

Let's explore the TodoAdd component.

This component uses an input field with a button to allow the user to add a new todo item.

To add a new todo item, you can either enter your todo item text and hit [enter] or click the [+] button. Both ways are handled by the component.

The input field uses a 2-way data-binding option via the v-model.

data() {
    return {
      newTodo: ''
    }
  }
Enter fullscreen mode Exit fullscreen mode

The newTodo variable is defined under the data() method on the App.vue component.

v-model reminds me of the 2-way data binding [(ngModel)] in Angular!

The input field also binds the keyup.enter event to execute the addTodo() method whenever the user hits [enter] inside the input field.

The addTodo() method is defined inside the methods object property of the App.vue component as follows:

addTodo() {
      if (this.newTodo) {
        this.$emit('add-todo', this.newTodo)
        this.newTodo = ''
      }
}
Enter fullscreen mode Exit fullscreen mode

The method emits the new todo item text, entered by the user, to the outside components.

The $emit method is defined by Vue, and is globally accessible by all Vue components to emit events to all consumers of this component.

$emit() reminds me of the EventEmitter in Angular app!

Emitting an event involves specifying the name of the event add-todo, and the payload of the event.

Then back in the App.vue, you would listen to this event by means of binding to the event name as follows:

<TodoAdd @add-todo="addTodo" />

The addTodo() method, defined inside App.vue component, handles this event by dispatching a new action on the store object as follows:

addTodo(todo) {
      this.$store.dispatch('addTodo', todo)
 }
Enter fullscreen mode Exit fullscreen mode

You dispatch a new action on the store by calling $store.dispatch() method, and passing it 2 arguments:

  • The name of the event to match that define in the store.js file.
  • The payload of the event. In this case, it is the todo item text entered by the user.

The call above is handled by Vuex and is redirected to execute the action addTodo defined by the store.js file.

TodoList component

The final component used inside the App.vue is the TodoList component.

<TodoList
      :todos="todos"
      @change-completed="changeCompleted"
      @edit-todo="editTodo"
      @delete-todo="deleteTodo"
/>
Enter fullscreen mode Exit fullscreen mode

This component is responsible to render a list of todo items. It accepts as input an array of todo items and exposes 3 output events:

  • change-completed event
  • edit-todo event
  • delete-todo event

In addition to displaying todo items it also allows the user to:

  • Mark a todo item as completed
  • Edit the text of an existing todo item
  • Delete an existing todo item

App.vue component imports this component, and adds it to the list of components it manages.

Let's explore this component together:

The component makes use of the TodoItem component to render every single todo item. We will explore this component in depth shortly.

The component uses the v-for directive to loop over the todo records, and render a single instance of TodoItem per todo item.

It is very important to specify the :key="item.id" for performance enhancement reasons.

v-for directive is equivalent to *ngFor directive in Angular.

The component handles the events exposed by TodoItem component and then re-emits the same events to the parent or outer component, the App.vue root component in this case.

The events are handled by means of 3 methods defined inside the methods object property on the TodoList component.

TodoItem component

Now let’s explore the final component in this app, the TodoItem component.

For each and every todo item, the component renders the following:

  • A checkbox field to mark the todo item as completed (on/off)
  • An input field to display the todo item text and allow the user to edit the text
  • A button to allow the user to delete the todo item

The text of todo item is coming from the input property todo defined by the TodoItem component inside the props object property.

props in Vue reminds me of @Input() in Angular

The component subscribes to the change event of the checkbox field by executing the changeCompleted() event handler method, defined inside the methods object property of the TodoItem component. The event handler emits the change-completed event to the outer components to signal a change in the completed state of a specific todo item.

$emit() in Vue reminds me of @Output() in Angular

The component subscribes to the change event of the input field by executing the editTodo() event handler method defined inside the methods object property of the TodoItem component. The event handler emits the edit-todo event to the outer components to signal an edit in the text of the todo item. It also provides a payload for this change to be eventually reflected on the store’ stat.

The component subscribes to the click.prevent event of the delete button by executing the deleteTodo() event handler method defined inside the methods object property of the TodoItem component. The event handler prompts the user for a confirmation of deletion, before emitting the delete-todo event to the outer components, to signal deletion of the todo item, and eventually reflect that to the store’s state.

That's it! If you visit the App.vue root component again, you will understand how the component is composed of the TodoBrand, TodoAdd, TodoList, and TodoItem components.

Finally, to serve the application, run the following command:
yarn serve

Once the app starts, you can access it by navigating your browser to http://localhost:8080, and playing with the todo app.

Conclusion

I am pretty sure you would agree that the concepts in both Vue and Angular are close. Only the details of how to use this or that feature differ, and you can learn them in no time.

One more feature that I've touched on in this article is the Vue Router module that allows you to define routes in your application. Once again, it resembles that of the Angular Router library.

The Vue.js framework has an extensive documentation website that’s easy to follow, and is always up to date. You can check it here.

This post was written by Bilal Haidar, a mentor with This Dot.

You can follow him on Twitter at @bhaidar.

Need JavaScript consulting, mentoring, or training help? Check out our list of services at This Dot Labs.

Latest comments (0)