SOFTWAREONTWIKKELINGCUSTOM SOFTWAREFIREBASEVUEJS
10/08/2018 • Dorien Jorissen

Snel schaalbare applicaties ontwikkelen met VueJS en Firebase (deel één)

Tegenwoordig vinden we heel veel van de functionaliteit die vroeger in de backend werd afgehandeld nu in de frontend. Ik laat je als frontend designer zien hoe je een simpele SPA (Single Page Application) kunt creëren zonder gedoe met onderhoud in de backend.

Wat gaan we bouwen?

In deze demo gebruiken wij: VueJS CLI, VueRouter, ElementUI and Google Firebase

We gaan een eenvoudige CRUD-applicatie (Create, Read, Update, Delete) maken in de vorm van een notitieapp. We gebruiken voor deze demo: VueJS CLI, VueRouter, ElementUI en Google Firebase.

Houd er rekening mee dat dit deel één is van een blogreeks. We gaan in dit deel aan de slag met het instellen van Firebase, het lokaal installeren van VueJS via de opdrachtregelinterface, het creëren van de benodigde weergaven en het instellen van de routering. Onze databaseverbinding wordt beperkt tot 'alleen-lezen'. Het maken, bijwerken en verwijderen van notities via onze app wordt binnenkort behandeld in deel twee van deze blogreeks.

Waarom VueJS?

Er zijn voor dit project talloze geschikte raamwerken en technologieën mogelijk en de keuze voor VueJS is dan ook persoonlijk. Dit zijn de redenen waarom ik voor VueJS heb gekozen en niet voor andere raamwerken:

  • VueJS maakt gebruik van HTML-sjablonen en is daardoor veel leesbaarder dan bijvoorbeeld JSX in React.
  • Frontend-ontwikkelaars die meer gericht zijn op design dan backend-ontwikkeling hebben minder moeite met het leertraject en dat is ideaal voor het creatieve team bij ACA.
  • Single-file componenten (HTML/CSS/JS) zijn perfect opnieuw te gebruiken.
  • Het heeft geïntegreerd statusbeheer (vuex) en een geïntegreerde router (vue-router) in plaats van te vertrouwen op externe bibliotheken zoals redux.
  • Het is licht en gemakkelijk te integreren met bestaande projecten. Angular op zichzelf is gigantisch en het leerproces verloopt moeizaam. Ik vind de aanpak van React gewoon veel te rommelig.

Er zijn echter ook enkele compromissen waarmee je rekening moet houden:

  • Het is in het Westen minder goed aangepast. VueJS is gemaakt door Evan You, een voormalige Google-engineer. Evan heeft Chinese roots en VueJS heeft een groot marktaandeel in China. Het kan zijn dat je Chinese documentatie tegenkomt als je zoekt naar invoegtoepassingen van anderen.
  • Er zijn in Europa minder VueJS-banen of -ontwikkelaars beschikbaar, maar de kennis over het raamwerk neemt wel toe.
  • Het wordt (nog) niet ondersteund of overgenomen door een multinational, mocht je dat belangrijk vinden. Er zijn echter wel heel veel Github-bijdragen van mensen die VueJS helemaal geweldig vinden. Het stond afgelopen jaar bij Github op nummer 1 als project met de meeste sterren.

Waarom Firebase?

Firebase is een geweldige service die ruimte biedt om je te richten op wat echt belangrijk is: het realiseren van optimale gebruikerservaringen. Je hoeft met Firebase geen servers te beheren of API's te schrijven. Je kunt Firebase gebruiken als host, verificatiemiddel, API en als gegevensarchief. Je kunt het aanpassen aan veel van je behoeften en het groeit probleemloos mee met je project.

Het gratis 'Spark'-abonnement biedt tot wel 100 gelijktijdige verbindingen, 1GB aan opslag en een downloadbeperking van 10GB. Voldoende voor een klein project, start-ups of voor indie-ontwikkelaars die hun idee(ën) willen valideren.

Ontwikkelen van de applicatie

Afhankelijk van je vaardigheidsniveau, kun je de volgende stappen binnen circa 30 tot 60 minuten voltooien

Stap 1: Onze Firebase-omgeving instellen

Ga naar https://firebase.google.com/ en meld je aan met je Google-account. Ga vervolgens naar de Firebase-console en maak een nieuw project aan. Mijn project heet ‘FireNotes’.

het opzetten van een firebase omgeving

We maken gebruik van de Firestore-database, dus ga naar het tabblad 'Database' in de zijbalk onder de sectie 'Develop' en schakel databasegebruik in testmodus* in zoals hieronder afgebeeld.

Firebase security warning

* Belangrijke opmerking: hiermee geef je iedereen toegang tot jouw database. Ben je van plan om een project te lanceren met Firebase? Duik dan dieper in de documentatie over database-machtigingen en stel alles correct in.

We kunnen vervolgens notities toevoegen aan onze database. Voeg een nieuwe collectie toe met de naam 'notes'. Dit is de bovenliggende collectie waar al onze notities worden ondergebracht. Vul de collectie met enkele dummy-notities zoals in onderstaand screenshot, met een auto-ID, datum van bericht, titel en content:

notities toevoegen aan de Firebase database

Stap 2: Ons VueJS project instellen

Niet heel ingewikkeld tot nu toe, toch? Laten we nu eens kijken naar onze applicatie. Open je console en navigeer naar je map met projecten. Ervan uitgaande dat je NPM al hebt geïnstalleerd, kun je Vue CLI installeren met behulp van het volgende commando:

npm install -g @vue/cli

Start je project daarna met het volgende commando:

vue create firenotes

Je kunt vervolgens kiezen uit een standaard of een handmatige voorinstelling. Laten we in dit geval beginnen met de standaardoptie met NPM. Ga na het installeren naar de map 'Firenotes' en start je webserver op met het volgende commando:

npm run serve

Navigeer naar het opgegeven IP-adres (doorgaans localhost:8080) en je krijgt het standaard 'hello world'-scherm van Vue te zien:

Hello world in Vue.js

Stap 3: De hoofdindeling maken

Laten we nu bepalen hoe onze applicatie er uit moet komen zien. We gaan in deze blog niet nader in op de vormgeving, dus ik gebruik de ElementUI-kit om het tempo op te voeren.

# In your ~/projects/appname folder
npm install element-ui -S 

Ga naar /src/main.js en voeg de ElementUI-kit toe en registreer deze in onze VueJS-applicatie direct onder 'import Vue' zoals hieronder weergegeven:

import Vue from 'vue'
 
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)

We kunnen nu ElementUI-componenten gebruiken in onze sjablonen. Ik heb het Vue-logo vervangen door mijn eigen logo (download hier) en ik heb het App.vue-sjabloon als volgt aangepast:

<template>
  <div id="app">
    <el-container>
      <el-header>
        <img class="logo" src="./assets/logo.png" />
      </el-header>
      <el-container>
        <el-aside width="300px">
          <el-menu
            default-active="home"
            class="el-menu-vertical-demo">
            <el-menu-item index="home">
              <i class="el-icon-menu"></i>
              <span>Home</span>
            </el-menu-item>
            <el-menu-item index="notes">
              <i class="el-icon-document"></i>
              <span>Notes</span>
            </el-menu-item>
          </el-menu>
        </el-aside>
        <el-main>
          <h1>Welcome to FireNotes</h1>
          <p>A simple CRUD (Create, Read, Update, Delete) application in the form of a notes app.</p>
          <p>For this application we are using: VueJS CLI, VueRouter, ElementUI and Google Firebase.</p>
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>
 
<script>
export default {
  name: 'App'
}
</script>
 
<style>
  html, body { margin: 0; }
  #app {
    font-family: 'Avenir', Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    color: #2c3e50;
  }
  .el-header { border-bottom: 1px solid #e6e6e6; display: flex; align-items: center; width: 100%; }
  .el-header button { float: right; }
  .el-menu-item { border-bottom: 1px solid #e6e6e6; }
  .logo { max-width: 50%; max-height: 50%; margin-right: auto; }
</style>

Zodat het er als volgt komt uit te zien:

ElementUI components in onze Vue.JS templates

Stap 4: Onze routes instellen

Dat is allemaal goed verlopen! We kunnen nu de vue-router installeren, dat is de officiële VueJS-router. Deze integreert diep met de kern van je VueJS-applicatie, zodat het maken van een SPA een fluitje van een cent wordt. We kunnen met vue-router navigeren naar onze pagina met notities, die we later zullen instellen.

# In your ~/projects/appname folder
npm install vue-router

De vue-router moet net zoals ElementUI geïmporteerd worden en moet worden geregistreerd in onze VueJS-app. Ga naar main.js en vervang het door de volgende code:

import Vue from 'vue'
 
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
 
import VueRouter from 'vue-router'
Vue.use(VueRouter)
 
import App from '@/App'
import HelloWorld from '@/components/HelloWorld'
import Notes from '@/components/Notes'
 
const routes = [
  {
    path: '*',
    redirect: '/home'
  },
  {
    path: '/',
    redirect: '/home'
  },
  {
    path: '/home',
    name: 'HelloWorld',
    component: HelloWorld
  },
  {
    path: '/notes',
    name: 'Notes',
    component: Notes
  },
]
 
const router = new VueRouter({
  routes
})
 
new Vue({
  el: '#app',
  router: router,
  render: h => h(App),
  components: { App }
})

Wat is hier gebeurd? We hebben de URL's van onze twee navigatie-items gespecificeerd en we hebben ze toegewezen aan twee verschillende componenten. We hebben ook regels toegevoegd om bij het laden automatisch naar de startpagina te gaan of als er door een URL een niet-geregistreerde route wordt geactiveerd. We hebben de routes tot slot geregistreerd via 'new VueRouter', die nu is vastgelegd in de kern van onze applicatie zodra deze wordt doorgegeven aan onze 'new Vue'-instantie.

Het opslaan zal hoogstwaarschijnlijk fouten opleveren, omdat we nog niets aan het component 'notes' hebben gedaan. We maken een eenvoudig Notes.vue-bestand in de map met componenten.

<template>
  <div class="notes">
    Notes will go here
  </div>
</template>
 
<script>
export default {
  name: 'Notes'
}
</script>

We moeten vervolgens zorgen dat onze app de juiste componenten weergeeft op basis van deze gespecificeerde routes. Dit vereist enkele aanpassingen aan ons App.vue-bestand aan zowel het navigatie- als het contentpaneel (el-main).

<template>
  <div id="app">
    <el-container>
      <el-header>
        <img class="logo" src="./assets/logo.png" />
      </el-header>
      <el-container>
        <el-aside width="300px">
          <el-menu
            default-active="home"
            :router="true"
            class="el-menu-vertical-demo">
            <el-menu-item index="home">
              <i class="el-icon-menu"></i>
              <span>Home</span>
            </el-menu-item>
            <el-menu-item index="notes">
              <i class="el-icon-document"></i>
              <span>Notes</span>
            </el-menu-item>
          </el-menu>
        </el-aside>
        <el-main>
          <router-view/>
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>
 
<script>
export default {
  name: 'App'
}
</script>
 
<style>
  html, body { margin: 0; }
  #app {
    font-family: 'Avenir', Helvetica, Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    color: #2c3e50;
  }
  .el-header { border-bottom: 1px solid #e6e6e6; display: flex; align-items: center; width: 100%; }
  .el-header button { float: right; }
  .el-menu-item { border-bottom: 1px solid #e6e6e6; }
  .logo { max-width: 50%; max-height: 50%; margin-right: auto; }
</style>

We hebben hier router="true" toegevoegd aan het component, zodat de indexen als 'path' kunnen worden gebruikt om de actie te routeren. We hebben de inleidende kop en paragrafen verplaatst naar HelloWorld.vue (dat zou jij ook moeten doen ;-)) en vervangen door <router-view/>. De router is nu volledig verantwoordelijk voor welk component er in dat paneel wordt getoond. Geweldig!

We zullen Notes.vue nu vervolgens zo aanpassen dat er daadwerkelijk notities worden weergegeven. Alle content is nu nog statisch. We zullen de notitietabel later vullen met gegevens uit onze Firebase-database.

<template>
  <div class="notes">
    <h1>
      Notes
      <el-button type="primary" size="medium"><i class="el-icon-circle-plus"></i> Add note</el-button>
    </h1>
    <el-table
      :data="tableData"
      border>
      <el-table-column type="expand">
        <template slot-scope="props">
          <p>{{ props.row.details }}</p>
        </template>
      </el-table-column>
      <el-table-column
        label="Note title">
        <template slot-scope="props">
          {{ props.row.name }}
        </template>
      </el-table-column>
      <el-table-column
        label="Date added / modified"
        prop="date">
      </el-table-column>
      <el-table-column
        fixed="right"
        label=""
        width="90">
        <template slot-scope="scope">
          <el-button type="info" size="small" icon="el-icon-edit" circle></el-button>
          <el-button type="danger" size="small" icon="el-icon-delete" circle style="margin-left: 5px;"></el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>
 
<script>
export default {
  name: 'Notes',
  data() {
    return {
      tableData: [{
        date: '2018-07-03',
        name: 'Lorem ipsum dolor sit amet',
        details: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.'
      }, {
        date: '2018-07-02',
        name: 'Consectetur adipiscing',
        details: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industrys standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book.'
      }]
    };
  }
}
</script>
 
<style>
h1 { margin-top: 0; display: flex; align-items: center; width: 100%; }
.el-button { margin-left: auto; }
.el-collapse-item__header { font-size: 16px; }
.el-table { font-size: 14px; font-weight: 700; }
.el-table__expanded-cell p { font-size: 16px; font-weight: 400; }
.el-table th { background: #fafafa; }
.el-table th > .cell { color: #333; }
</style>

Zodat het er als volgt komt uit te zien:

we tweak Notes.vue to actually display notes

Stap 5: Firebase koppelen aan ons project

Fantastisch! Alles is nu aanwezig om de statische applicatie te veranderen in een applicatie die wordt aangestuurd door Firestore-data. We zullen de afhankelijkheden installeren om verbinding te maken met Firebase:

# In your ~/projects/appname folder
npm install firebase

Voeg daarna het volgende toe aan het bestand main.js:

import firebase from 'firebase'
import 'firebase/firestore'
firebase.initializeApp({
  apiKey: '',
  projectId: ''
})
export const db = firebase.firestore()
const settings = { timestampsInSnapshots: true }
db.settings(settings)

Je moet hier nog steeds ‘apiKey’ en ‘projectId’ gebruiken, die je via het tandwielpictogram kunt vinden onder 'Project settings':

Firebase Project settings screenshot

Ga daarna naar het component Notes.vue en leeg daar de statische array met notities en vul deze met de notities die we eerder in onze Firestore-database hebben gemaakt. We hebben ook een 'empty-text' gedefinieerd in de tabel, die wordt getoond als de gegevens in onze tabel worden geladen.

<template>
  <div class="notes">
    <h1>
      Notes
      <el-button type="primary" size="medium"><i class="el-icon-circle-plus"></i> Add note</el-button>
    </h1>
    <el-table
      :data="tableData"
      empty-text="Loading, or no records to be shown."
      border>
      <el-table-column type="expand">
        <template slot-scope="props">
          <p>{{ props.row.content }}</p>
        </template>
      </el-table-column>
      <el-table-column
        label="Note title">
        <template slot-scope="props">
          {{ props.row.title }}
        </template>
      </el-table-column>
      <el-table-column
        label="Date added / modified"
        prop="date">
      </el-table-column>
      <el-table-column
        fixed="right"
        label=""
        width="90">
        <template slot-scope="scope">
          <el-button type="info" size="small" icon="el-icon-edit" circle></el-button>
          <el-button type="danger" size="small" icon="el-icon-delete" circle style="margin-left: 5px;"></el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>
 
<script>
import { db } from '@/main'
export default {
  name: 'Notes',
  data() {
    return {
      tableData: []
    }
  },
  created () {
    db.collection('notes').get().then(querySnapshot => {
      querySnapshot.forEach(doc => {
        const data = {
          'id': doc.id,
          'date': doc.data().date,
          'title': doc.data().title,
          'content': doc.data().content
        }
        this.tableData.push(data)
      })
    })
  }
}
</script>
 
<style>
h1 { margin-top: 0; display: flex; align-items: center; width: 100%; }
.el-button { margin-left: auto; }
.el-collapse-item__header { font-size: 16px; }
.el-table { font-size: 14px; font-weight: 700; }
.el-table__expanded-cell p { font-size: 16px; font-weight: 400; }
.el-table th { background: #fafafa; }
.el-table th > .cell { color: #333; }
</style>

Hierdoor worden de notities, die we eerder handmatig aan onze Firestore-database hebben toegevoegd, zichtbaar in onze applicatie die daardoor minder statisch lijkt. Het resultaat is top!

great success gif

Wat zijn de volgende stappen?

We gaan in de volgende blog over dit onderwerp dieper in op de resterende functionaliteiten: het maken, bijwerken en verwijderen van notities in onze Firebase-database, volledig aangetuurd door onze VueJS-app. De vraag die werd gesteld over login-functionaliteit hebben we als bonus toegevoegd!

Vragen of opmerkingen?

Lukt het niet om het aan de gang te krijgen? Meer informatie nodig over bepaalde delen van de code? Laat het ons gerust weten. Laat een reactie achter en we zullen zo snel mogelijk reageren.

Dorien Jorissen