Image

progressive-web-apps-vue-3

5 min read
Last update: December 19, 2021

Saving Data to IndexedDB

Now that we have our database and object store setup, it’s time for us to get to integrating it with our Vue 3 app!

Setup basic database scaffold

export default {
// Other code excluded for brevity
methods: {
async saveTodo(todo) {
this.database = await this.getDatabase()

return new Promise((resolve, reject) => {

})
}
}
}

Initiate a readwrite transaction with the database

Unlike the previous lesson where we requested readonly permissions from the database, we need to request a transaction for readwrite permissions in order to be able to “write” data to the database. This is critical, because without the correct permissions, nothing will work as expected.

export default {
// Other code excluded for brevity
methods: {
async saveTodo(todo) {
this.database = await this.getDatabase()

return new Promise((resolve, reject) => {
let transaction = this.database.transaction('todos', 'readwrite')
})
}
}
}

Add an item to the object store

Now that we have the right permissions, we can access and save our store using the same objectStore as the last lesson. And then, in order to save data, we only need a single method named put. While this may seem insignificant at first, this is incredibly powerful because the put method allows us create a new item in the object store if it doesn’t exist. However, if the item already exists, it will just go ahead and update the correct item as long as the id matches!

export default {
// Other code excluded for brevity
methods: {
async saveTodo(todo) {
this.database = await this.getDatabase()

return new Promise((resolve, reject) => {
const transaction = this.database.transaction('todos', 'readwrite')

// Access the todos object store
const store = transaction.objectStore('todos')

// Add the item to the store
store.put(todo)
})
}
}
}
export default {
// Other code excluded for brevity
methods: {
async saveTodo(todo) {
this.database = await this.getDatabase()

return new Promise((resolve, reject) => {
const transaction = this.database.transaction('todos', 'readwrite')
const store = transaction.objectStore('todos')

// Add the item to the store
store.put(todo)
})
}
}
}

Wrapping up our promises

export default {
// Other code excluded for brevity
methods: {
async saveTodo(todo) {
this.database = await this.getDatabase()

return new Promise((resolve, reject) => {
const transaction = this.database.transaction('todos', 'readwrite')
const store = transaction.objectStore('todos')

// Add the item to the store
store.put(todo)

// Resolve the promise if the transaction succeeds
transaction.oncomplete = () => {
resolve('Item successfully saved.')
}

// Reject the promise if the transaction fails
transaction.onerror = event => {
reject(event)
}
})
}
}
}

Alright! With this, we are now ready to integrate it into our TodoMVC functionality.

Integrate saveTodo into new task creation

With our newly created method, it’s time for us to integrate it into our workflow for adding a new task. If we look at our code, we will find that the code responsible for adding new tasks is called addTodo. And scanning the code, we’ll see that at the bottom, this is where we update our todos key in our data store, so this is a perfect place to use our new saveTodo method.

addTodo() {
const value = this.newTodo && this.newTodo.trim()
const todoItem = {
id: this.todos.length + 1,
title: value,
completed: false
}

if (!value) {
return
}

// When it's time to update our todos data
this.todos.push(todoItem)
// This is a good time to save our data as well
this.saveTodo(todoItem)
// Before resetting the new todo item
this.newTodo = ''
}

To verify that this works, let’s go ahead and add a couple of items and refresh the page. If everything works, nothing should have changed! And to double check, we can open up the Application tab in the DevTools to verify that our data indeed has been updated in IndexedDB. What a major milestone! 🎉

Integrate saveTodo into edited task update

With our well earned success, let’s take that lesson learned and build on it by learning how to update data within the object store. And there are a few scenarios that cover this.

  • When a user edits the name of an item
  • When a user toggles whether an item is complete or not
  • When a user toggles all items to be complete or not

When a user edits an item

doneEdit(todo) {
if (!this.editedTodo) {
return
}
this.editedTodo = null
todo.title = todo.title.trim()

this.saveTodo({
...todo,
title: todo.title
})

if (!todo.title) {
this.removeTodo(todo)
}
},

When a user toggles whether an item is complete or not

updateTodo(todo) {
this.todos.find(item => item === todo).completed = !todo.completed
this.saveTodo({
...todo
})
}

When a user toggles all items to be complete or not

allDone: {
get() {
return this.remaining === 0
},
set(value) {
this.todos.forEach(todo => {
todo.completed = value
this.saveTodo({
...todo
})
})
}
}

Next Steps

Congratulations! In this lesson, you’ve learned how to change permissions on the transactions we request to the database in order to add data to our object store. You are now able to save and persist data across user sessions in a browser. And as a result, you’ve also unlocked offline databases with PWAs!