22 augustus 2018
29 april 2026
Leestijd 12 min
Snel schaalbare applicaties ontwikkelen met VueJS en Firebase (deel 2)
<span id="hs_cos_wrapper_name" class="hs_cos_wrapper hs_cos_wrapper_meta_field hs_cos_wrapper_type_text" style="" data-hs-cos-general-type="meta_field" data-hs-cos-type="text" >Snel schaalbare applicaties ontwikkelen met VueJS en Firebase (deel 2)</span>
Share this via:

In dit tweede deel van deze blogpostserie zal ik mijn ervaringen delen over het maken van een notitie-app. We behandelen het maken, bijwerken en verwijderen van notities en het toevoegen van authenticatie. Deel één van deze blogpostserie vind je hier, waarin we onze Firebase database en Vue-applicatie hebben opgezet om notities weer te geven.

Wat zijn we aan het maken?

creating fast en scalable applications with VuaJS and firebase

Voortbouwend op ons vorige werk, zullen we onze notities app afmaken.
Afhankelijk van je vaardigheidsniveau, zullen de volgende stappen je ongeveer 20 tot 45 minuten kosten om te voltooien.

Stap 1: Notities toevoegen

In onze vorige blogpost hebben we al leesnotities gemaakt, maar daarvoor moesten we ze handmatig toevoegen in onze Firestore-database. Laten we nu de functionaliteit creëren waarmee we rechtstreeks via de interface van onze applicatie notities kunnen toevoegen. In Notes.vue:

<template> <div class="notes"><h1> Notities <el-button type="primary" size="medium" @click="addDialogVisible = true"><i class="el-icon-circle-plus"></i> Notitie toevoegen</el-button> </h1> <el-table :data="tableData" empty-text="Bezig met laden, of geen records te tonen." border> <el-table-column type="expand"><template slot-scope="props"><p></p></template> </el-table-column> <el-table-column label="Note title"> <template slot-scope="props"></template> </el-table-column> <el-table-column label="Datum toegevoegd/gewijzigd"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> <el-dialog title="Notitie toevoegen" :visible.sync="addDialogVisible"width="30%"><el-form ref="addNoteRuleForm" :model="addNoteRuleForm" :rules="rules"><el-form-item label="Note title" prop="title"><el-input type="text" placeholder="Note title" v-model="addNoteRuleForm.title"></el-input> </el-form-item> <el-form-item label="Note content" prop="content"><el-input type="textarea" placeholder="Note content" v-model="addNoteRuleForm.content"></el-input> </el-form-item> </el-form> <span slot="footer" class="dialog-footer"><el-button @click="addDialogVisible = false">Cancel</el-button> <el-button type="primary" @click="addNoteForm('addNoteRuleForm'); addDialogVisible = false;">Bevestig</el-button> </span> </el-dialog> </div> </template> <script> import { db } from '@/main' export default {naam: 'Notities', data() { return { tableData: [], addDialogVisible: false, addNoteRuleForm: {titel: '', inhoud: ''}, regels: { title: [ { required: true, message: "Note title can't be empty", trigger: 'blur' }] , content: [ { required: true, message: "Notitie-inhoud mag niet leeg zijn", trigger: 'blur' } }, created () { db.collection('notities').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) }), methods: { addNoteForm (formName) {var self = this; this.$refs[formName].validate((valid) => { if (valid) {console.log('Form valid'); } } } anders {console.log('error submit!!')}} </script>CSS CSS-beheerprogramma's.
CSS

De bovenstaande code laat zien dat we een@click="addDialogVisible = true" handler hebben toegevoegd aan onze knop 'Notitie toevoegen' en deze hebben geregistreerd in onze 'data'-haak zodat deze standaard false is.

We hebben ook het dialoogvenster zelf toegevoegd dat wordt weergegeven op basis van deze 'addDialogVisible'-waarde. In dit dialoogvenster hebben we een ElementUI-formulier toegevoegd met titel- en inhoudsvelden die enkele validatieregels hebben, ook gespecificeerd in onze 'data hook'.

Als we nu op de knop drukken om de notitie toe te voegen, geven we de refs van onze formname door, die een Vue-object retourneert met de velden die moeten worden gevalideerd.

Vervolgens voegen we onze database query toe. Wijzig de methode 'addNoteForm' als volgt:

 addNoteForm (formName) {var self = this; this.$refs[formName].validate((valid) => { if (valid) {var today = new Date().toLocaleString('en-GB'); db.collection('notes').add({' title': this.addNoteRuleForm.title, 'content': this.addNoteRuleForm.content, 'date': today }).then(function (docRef) { self.$message( {type: 'succes', message: 'Notitie succesvol toegevoegd' });}).catch(function (error) {console.error('Fout bij toevoegen document: ', error) }) } else {console.log('fout bij verzenden!!') })
CSS

Nadat je dit hebt getest door een geldig formulier in te dienen, zie je de succesmelding. Maar er is nog steeds een probleem met het tonen van de nieuw toegevoegde notitie in onze tabel. Door de pagina te verversen, wordt hij wel getoond.

De reden hiervoor is dat we de database-aanroep om de notities op te halen hebben uitgevoerd via de 'created' haak. Dit zal één keer gebeuren als we de pagina openen, maar niet nadat we onze database hebben bijgewerkt. Laten we dat wat flexibeler maken door de get query naar een methode te verplaatsen:

created () {this.getNotes()}, methods: { getNotes () { db.collection('notes').orderBy('date').get().then(querySnapshot => {this.tableData = []; querySnapshot.forEach(doc => { const data = {' id': doc.id,' date': doc.data().date,' title': doc.data().title,' content': doc.data().content }this.tableData.push(data);})}, addNoteForm (formName) {var self = this; this.$refs[formName].validate((valid) => { if (valid) {var today = new Date().toLocaleString('en-GB'); db.collection('notes').add({' title': this.addNoteRuleForm.title, 'content': this.addNoteRuleForm.content, 'date': today }).then(function (docRef) {self.getNotes(); self.$message( {type: 'succes', message: 'Notitie succesvol toegevoegd' });}).catch(function (error) {console.error('Fout bij toevoegen document: ', error)      }) } anders {console.log('error submit!!') } }
CSS

Als je goed kijkt, heb ik ook een orderBy('date') toegevoegd aan de query. Als we nu nog een noot toevoegen, wordt onze tabel foutloos bijgewerkt en wordt de laatste noot toegevoegd aan de onderste rij. Als je dit andersom wilt, verander dan orderBy('date') in orderBy('date', 'desc').

Stap 2: Notities verwijderen

Het verwijderen van een item is vrij eenvoudig, maar moet vanuit het oogpunt van gebruikerservaring ook niet te eenvoudig zijn. Daarom heb ik besloten om het verwijderen af te handelen na een bevestigingsvenster van ElementUI.

Voeg eerst een @click handler toe aan het verwijder icoon waar we de ID van de notitie die we willen verwijderen naartoe sturen:

<template slot-scope="props"><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;" @click="deleteNote(props.row.id)"></el-button> </template>
CSS

Merk op dat ik ook de slot-scope="scope" heb veranderd in slot-scope="props" in de template tag om overeen te komen met de rest van de props die worden doorgegeven aan de tabel.

Laten we nu de deleteNote methode registreren:

 deleteNote (noteId) {var self = this; this.$confirm('Hiermee wordt de notitie permanent verwijderd. Doorgaan?", "Waarschuwing", { confirmButtonText: 'OK', cancelButtonText: 'Annuleren', type: 'waarschuwing'}).then() => { db.collection('notes').doc(noteId).delete().then(function() {self.getNotes(); self.$message( {type: 'succes', message: 'Verwijderen voltooid'}); }).catch(function(error) {console.error("Fout bij verwijderen notitie: ", error);}); }).catch() => {console.log("Verwijderen geannuleerd"); });CSS.
CSS

En voilà! Het verwijderen van notities is nu volledig afgedekt.

Stap 3: Notities bewerken

Voeg net als bij het verwijderen een bewerkingshandler toe aan de bewerkingsknop, samen met een 'editDialogVisible'-waarde zoals we die hebben toegevoegd voor de 'addDialog'.

<template slot-scope="props"><el-button type="info" size="small" icon="el-icon-edit" circle @click="editNote(props.row.id); editDialogVisible = true"></el-button> <el-button type="danger" size="small" icon="el-icon-delete" circle style="margin-left: 5px;" @click="deleteNote(props.row.id)"></el-button> </template>
CSS

Vervolgens moeten we ook het dialoogvenster maken waarin de bewerking plaatsvindt:

   <el-dialog title="Notitie bewerken": visible.sync="editDialogVisible"width="30%"><el-form ref="editNoteRuleForm" :model="editNoteRuleForm" :rules="rules"><el-form-item label="Notitie titel" prop="title"><el-input type="text" placeholder="Notitie titel" v-model="editNoteRuleForm.title" value="editNoteRuleForm.title"></el-input> </el-form-item> <el-form-item label="Note content" prop="content"><el-input type="textarea" placeholder="Note content" v-model="editNoteRuleForm.content" value="editNoteRuleForm.content"></el-input> </el-form-item> </el-form> <span slot="footer" class="dialog-footer"><el-button @click="editDialogVisible = false">Cancel</el-button> <el-button type="primary" @click="saveEditedNote('editNoteRuleForm', editNoteRuleForm); editDialogVisible = false;">Bevestig</el-button> </span> </el-dialog>
CSS

Onze data hook (we hebben editDialogVisible en editNoteRuleForm toegevoegd) moet dan worden bijgewerkt tot:

 data() { return { tableData: [], addDialogVisible: false, addNoteRuleForm: { title: '', content: ''}, editDialogVisible: false, editNoteRuleForm: { id: '', title: '', content: ''}, regels: { title: [ { required: true, message: "Note title can't be empty", trigger: 'blur' }] , content: [ { required: true, message: "Opmerking inhoud kan niet leeg zijn", trigger: 'blur' } }
CSS

Daarnaast moeten we ook een editNote methode toevoegen die de gegevens ophaalt op basis van de notitie ID en de waarde van de editDialog velden invult:

  editNote (noteId) {var self = this;this.editNoteRuleForm.title = '';this.editNoteRuleForm.content = ''; db.collection('notes').doc(noteId).get().then(function(doc) { if (doc.exists) {self.editNoteRuleForm.id = doc.id;self.editNoteRuleForm.title = doc.data().title;self.editNoteRuleForm.content = doc.data().content; } else {console.log("No such document!"); }).catch(function(error) {console.log("Error getting document:", error);}); }
CSS

Tot zover is het ophalen van de notitie in ons bewerkingsvenster behandeld, maar we hebben nog steeds de methode saveEditedNote nodig:

  saveEditedNote (note, noteObj) {var self = this;var today = new Date().toLocaleString('en-GB'); this.$refs[note].validate((valid) => { if (valid) { db.collection('notes').doc(noteObj.id).set({ 'title': noteObj.title, 'content': noteObj.content, 'date': today }).then(function() {self.getNotes(); self.$message( {type: 'succes', message: 'Notitie succesvol bewerkt' });}) .catch(function(error) {console.error("Fout bij het schrijven van document: ", error); });}) }
CSS

Hier stellen we onze database record in op de nieuwe waarde gespecificeerd door onze gevalideerde formuliervelden, een bijgewerkte datum wordt ook doorgegeven. Bij succes wordt er een 'Note successfully edited' bericht getoond.

Dat was het! Onze missie om een basis CRUD-applicatie te maken op basis van VueJS en Firebase is een feit!

 CRUD application based on VueJS and Firebase is a fact

Bonusstap: Authenticatie

Iemand vroeg me naar de authenticatiefunctionaliteit in onze vorige blogpost, dus ik zal het hebben over het maken van een basisauthenticatie voor onze app, gebaseerd op de authenticatie-API die Firebase biedt.

Ga naar je Firebase console en navigeer naar de link 'Authenticatie' in de zijbalk onder 'Ontwikkelen'. Schakel nu de eenvoudige Email/Paswoord Authenticatie in.

firebase overview

Nu kunnen we beginnen met het bouwen van de login- en registratiepagina's. Dat zijn in principe gewoon formulieren zoals we eerder hebben gedaan. Het enige verschil hier is dat we de methodesfirebase.auth().createUserWithEmailAndPassword enfirebase.auth().signInWithEmailAndPassword gebruiken.

Inloggen.vue:

<template> <div class="login"> <el-container> <el-main> <el-row :gutter="20"> <el-col :span="12"> <h1>Login bij uw account</h1> <el-form ref="form" :model="form"> <el-form-item label="E-mailadres"> <el-input type="email" placeholder="E-mail" v-model="form.email"></el-input> </el-form-item> <el-form-item label="Password"> <el-input type="password" placeholder="Password" v-model="form.password"></el-input> </el-form-item> <el-form-item> <el-button type="primary" v-on:click="login">Login</el-button> </el-form-item> <el-form-item> Geen account? <router-link to="/signup">Maak er een aan</router-link> </el-form-item> </el-form> </el-col> </el-row> </el-main> </el-container> </div> </template> <script> import firebase from 'firebase' export default {naam: 'login', data: functie () {retour {formulier: { email: '', wachtwoord: '' } }, methoden: { login: functie () { firebase.auth().signInWithEmailAndPassword(this.form.email, this.form.password).then( (gebruiker) => { this.$router.replace('/home') this.$notify({ title: 'Geweldig!', message: 'Je bent nu ingelogd.', type: 'succes' })}, (err) => { this.$message.error( {boodschap: 'Oeps. ' + err.message } ) } }</script>
CSS

Aanmelden.vue:

<template> <div class="sign-up"> <el-container> <el-main> <el-row :gutter="20"> <el-col :span="12"> <h1>Aanmelden voor een FireNotes account</h1> <el-form ref="form" :model="form"> <el-form-item label="E-mailadres"> <el-input type="email" placeholder="E-mail" v-model="form.email"></el-input> </el-form-item> <el-form-item label="Password"> <el-input type="password" placeholder="Password" v-model="form.password"></el-input> </el-form-item> <el-form-item> <el-button type="primary" v-on:click="signUp">Aanmelden</el-button> </el-form-item> <el-form-item> Heeft u al een account? <router-link to="/login">Login</router-link> </el-form-item> </el-form> </el-col> </el-row> </el-main> </el-container> </div> </template> <script> import firebase from 'firebase' export default {naam: 'SignUp', data: function () { return { form: { email: '', wachtwoord: '' } }, methods: { signUp: functie () { firebase.auth().createUserWithEmailAndPassword(this.form.email, this.form.password).then( (user) => { this.$router.replace('/home') this.$notify({ title: 'Geweldig!', message: 'Uw account is aangemaakt.', type: 'succes' })}, (err) => { this.$message.error( {boodschap: 'Oeps. ' + err.message } ) } }</script>
CSS

We kunnen deze weergaven nog steeds niet zien omdat we onze routes nog moeten bijwerken. Ga naar main.js en vervang deze door het volgende (vergeet je API-sleutel en Project ID niet):

import Vue from 'vue' import ElementUI from 'element-ui' import 'element-ui/lib/theme-chalk/index.css' Vue.use(ElementUI) import firebase from 'firebase' import 'firebase/firestore' firebase.initializeApp({ apiKey: 'yourApiKey', projectId: 'yourProjectId'}) export const db = firebase.firestore() const settings = { timestampsInSnapshots: true }db.settings(settings) import VueRouter from 'vue-router' Vue.use(VueRouter) import App from '@/App' import HelloWorld from '@/components/HelloWorld' import Notes from '@/components/Notes' import Login from '@/components/Login' import SignUp from '@/components/SignUp' const routes = [ { path: '*', redirect: '/login'}, { path: '/', redirect: '/login'}, { path: '/login', name: 'Login', component: Login }, { path: '/signup', name: 'SignUp', component: SignUp }, { pad: '/home', naam: 'HelloWorld', component: HelloWorld, meta: { requiresAuth: true }}, { path: '/notes', name: 'Notes', component: Notes, meta: { requiresAuth: true }} const router = nieuwe VueRouter({routes}) router.beforeEach((to, from, next) => {const currentUser = firebase.auth().currentUser const requireAuth = to.matched.some(record => record.meta.requiresAuth)if (requiresAuth && !currentUser) next('/login')else if (!requiresAuth && currentUser) next('/home')else next()})/* eslint-disable no-new */firebase.auth().onAuthStateChanged(function (user) { new Vue({ el: '#app', router: router, render: h => h(App), components: { App }})})
CSS

Hier hebben we een aantal routes toegevoegd voor inloggen en aanmelden, en meta: { requiresAuth: true; } toegevoegd aan de pagina's waarop we beperkte toegang willen. We controleren met Firebase of de gebruiker is geauthenticeerd in de functie router.beforeEach, voordat we hem registreren in onze app. Als de auth-status verandert, wordt de controle opnieuw uitgevoerd.

Nu werkt het dus vrij goed, maar we willen de navigatie in de zijbalk verbergen als de gebruiker niet is ingelogd, dus we hebben de aangemaakte haak en de v-if op <el-aside> toegevoegd. We hebben ook een uitloglink toegevoegd aan de navigatie. Ons App.vue bestand ziet er nu zo uit:

<template> <div id="app"> <el-container> <el-header> <img class="logo" src="./assets/logo.png" /> </el-header> <el-container> <el-aside width="300px" v-if="user"> <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-item index="/logout" v-on:click="logout"> <i class="el-icon-setting"></i> <span>Logout</span> </el-menu-item> </el-menu> </el-aside> <el-main> <router-view/> </el-main> </el-container> </el-container> </div> </template> <script> import firebase from 'firebase' export default {naam: 'App', created () {this.user = firebase.auth().currentUser || false;}, methods: { uitloggen: functie () { firebase.auth().signOut().then() => {this.$router.replace('/login')}) }</script> <style> html, body { margin: 0; } body { font-family: 'Avenir', Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grijswaarden; 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>
CSS

En daar hebben we het, een toepassing voor notities op één pagina waar je een account kunt aanmaken, kunt inloggen en notities kunt maken, lezen, bijwerken en verwijderen.

Voel je vrij om deze code / kennis aan te passen of op voort te bouwen om je eigen VueJS / Firebase projecten te maken. Laat me weten hoe je het hebt gedaan!

Vragen, opmerkingen?

Krijg je het niet aan de praat? Heb je meer uitleg nodig over bepaalde stukjes code? We zijn een en al oor. Laat het ons weten en we nemen zo snel mogelijk contact met je op.

 

Dorien Jorissen
Dorien Jorissen
Solution Engineer, ACA Group
Contact us

Want to dive deeper into this topic?

Get in touch with our experts today. They are happy to help!

ACA mug mok koffie tas
Contact us

Want to dive deeper into this topic?

Get in touch with our experts today. They are happy to help!

ACA mug mok koffie tas
Contact us

Want to dive deeper into this topic?

Get in touch with our experts today. They are happy to help!

ACA mug mok koffie tas
Contact us

Want to dive deeper into this topic?

Get in touch with our experts today. They are happy to help!

ACA mug mok koffie tas