Using Ionic 4 Components in Your Vue.js Apps

Paul Halliday

Vue.js is gaining popularity for all the right reasons, and the future looks bright for Vue and the combination of Progressive Web Applications. Ionic is a web framework at the forefront of combining mobile-first UX and although it’s currently powered by Angular 5.x, the team’s new Stencil compiler allows Vue.js to use the same Web Components with Ionic 4.

Ionic 4 is currently in alpha and should not be used in production, but nevertheless, this is a great thought experiment and dives deep into the benefits of Stencil and what Web Components mean for framework agnostic UI components.

To get started, create a new Vue project with the Vue CLI:

# Install the Vue CLI
$ npm install vue-cli

# Create a new Vue project
$ vue init webpack-simple vue-ionic

# Change directory
$ cd vue-ionic

# Add vue-router and axios
$ npm install vue-router axios

# Run development server
$ npm run dev

REST API

We can make a local REST API with json-server which allows us to easily GET and POST todos to our server. Install this globally on your machine if you haven’t got it already:

$ npm install json-server -g

The server can be started with a JSON serialized database. Create a file named db.json inside of the root of your project:

db.json

{
  "todos": [
    {
      "id": 1,
      "name": "Make awesome applications"
    },
    {
      "id": 2,
      "name": "Play squash"
    },
    {
      "id": 3,
      "name": "Deadlift"
    },
    {
      "id": 4,
      "name": "Squat"
    }
  ]
}

Start the API by running the following in the terminal:

$ json-server db.json --watch --port 3001

Now that we have an API up and running, let’s add Ionic!

Adding Ionic

To add an alpha version of Ionic inside of our project, adding the following script to our index.html file:

<script src="https://unpkg.com/@ionic/core@0.0.2-30/dist/ionic.js"></script>

You can find the current version of Ionic core here: https://www.npmjs.com/package/@ionic/core

At the same time, ensure you’ve got the appropriate responsive meta tag:

<meta name="viewport" content="width=device-width, initial-scale=1.0">

Routes

We can then set up two routes that allow us to either view todo items or add todo items:

App.vue

import Vue from 'vue'
import VueRouter from 'vue-router';

import TodoList from './components/TodoList';
import AddTodoItem from './components/AddTodoItem';

Vue.use(VueRouter);

const routes = [
  { path: '', redirect: '/todos'},
  { path: '/todos', component: TodoList },
  { path: '/todos/add', component: AddTodoItem}
]

export default new VueRouter({ routes })

Afterward, add the router configuration to our main Vue instance inside of main.js:

main.js

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

import router from './router'

new Vue({
  el: '#app',
  router,
  render: h => h(App)
})

Ionic Web Components

As Ionic 4.x is built using Web Components, we’ll need to tell Vue that the ion- components aren’t Vue components. Inside of main.js, add the following configuration object that ignores all Ionic elements:

main.js

Vue.config.ignoredElements = [/^ion-/]

Inside of App.vue we can now add our <router-view> and enclose this in an <ion-app> component. This is used to enclose all of our Ionic components:

App.vue

<template>
  <ion-app>
    <router-view></router-view>
  </ion-app>
</template>

Todo List

To create the todo list page that we registered inside of router.js, create a file named TodoList.vue inside of the src/components folder.

Inside of our created hook, we’re capturing the todos from our API and assigning them to the todos array.

TodoList.vue

import axios from 'axios';

export default {
  data() {
    return {
      todos: []
    }
  },
  methods: {
    addTodo() {
      this.$router.push({path: '/todos/add'})
    }
  },
  created() {
    axios.get('http://localhost:3001/todos')
      .then(res => this.todos = res.data)
  }
}

We can then add a template which will certainly look similar to anyone that’s used Ionic in the past:

<template>
  <ion-page>
    <ion-header>

      <ion-toolbar class="toolbar-md-primary">
        <ion-title>TodoList</ion-title>
      </ion-toolbar>

    </ion-header>
    <ion-content class="content">

      <ion-list>
        <ion-item v-for="todo in todos" :key="todo.id">
          
        </ion-item>
      </ion-list>

      <ion-fab-button class="todo-fab" @click="addTodo">
        <ion-icon name="add"></ion-icon>
      </ion-fab-button>
    </ion-content>
  </ion-page>
</template>

We wrap our component inside of an ion-page element. This then allows us to define a header and toolbar which gives us the ability to display a navigation bar.

Next, we can display the list inside of an ion-content element and iterate over each item inside of our todos array as an ion-item.

To style our fab button and add some padding. We’ll be using both classes across both of our components so let’s add a style.css file and the appropriate classes:

style.css

.todo-fab {
  position: fixed;
  bottom: 25px;
  right: 15px;
  font-size: 30px;
}

.content {
  padding: 10px 10px 10px 0px
}

This gives us the following result(s)

List of todo items

When the user clicks the FAB button, they’ll be navigated to the Add Todo page. Let’s create that at components/AddTodoItem.vue:

AddTodoItem.vue

<template>
  <ion-page>
    <ion-header class="toolbar-md-primary">

      <ion-toolbar>
        <ion-title>Add Item</ion-title>
      </ion-toolbar>

    </ion-header>
    <ion-content class="content">

      <ion-item>
        <ion-input :value="name" ref="newTodoItem" @input="updateTodoName" placeholder="Todo Name"></ion-input>
      </ion-item>

      <ion-fab-button class="todo-fab" @click="addTodo">
        <ion-icon name="checkmark"></ion-icon>
      </ion-fab-button>

    </ion-content>
  </ion-page>
</template>

Our markup looks quite similar, but this time we’re using an ion-input. To get the value of the new todo item we’re using a ref and an input event.

import axios from 'axios';

export default {
  data() {
    return {
      name: ''
    }
  },
  methods: {
    addTodo() {
      const newTodo = { name: this.name }
      axios.post('http://localhost:3001/todos', newTodo)
        .then(res => {
          this.$router.push({path: '/todos'})
        })
    },
    updateTodoName() {
      this.name = this.$refs.newTodoItem.value
    }
  }
}

The code for this page is quite similar too, we’re adding a new Todo item to our API using axios whenever someone clicks the FAB button.

Here's the results

Adding a todo item

  Tweet It
✖ Clear

🕵 Search Results

🔎 Searching...