Tutorial

Intro to Electron.js - Part 2: Todo App

Published on August 24, 2019
    author

    Joshua Hall

    Intro to Electron.js - Part 2: Todo App

    Over in the first part of this introduction to Electron.js, we just got Electron setup with our bundling scripts and a local window. With the foundations out of the way, we can get started with the more interesting functionality of our app.

    Passing Data Between Client and Server

    Since we’re not setup with a database yet, the todo functionality is almost the same as any vanilla JavaScript todo app. The only difference being we’ll first use ipcRenderer.send to send an event to our server with our data, which we can listen for with ipcMain.on, then do whatever we need to with the data (like save it to our database), and send it back to the client to be rendered if successful.

    script.js
    const electron = require('electron')
    const { ipcRenderer } = electron
    
    const form = document.querySelector('form')
    const item = document.querySelector('input')
    const list = document.querySelector('ul')
    
    // Render Items to Screen
    const render = item => {
      const li = document.createElement('li')
      li.innerHTML = item
      list.appendChild(li)
    }
    
    // Get All Items After Starting 
    window.addEventListener('load', () => ipcRenderer.send('loadAll'))
    ipcRenderer.on('loaded', (e, items) => items.forEach(item => render(item.item)))
    
    // Send Item to the server and clear the form
    form.addEventListener('submit', e => {
      e.preventDefault()
      ipcRenderer.send('addItem', { item: item.value })
      form.reset()
    })
    

    Next we just need to start a new database with Datastore and use some mongoose-like methods when we catch our events from the client.

    Instead of a method on ipcMain, as you may expect, we need to use webcontents.send on the window we want it applied to. Later this will also allow us to send events from our menu without destructuring anything from electron.

    NeDB gives us many of the same options as Mongoose does for manipulating and querying our data. I strongly recommend looking at the docs since there are some differences between Nedb and Mongoose.

    The main methods that we need just take an object with the properties you want the return item to match, and a callback function for when it’s complete.

    • insert Takes the new item as an object and adds it to the database.
    • remove Takes the query, and object of options, and removes from the database.
    • find Takes the query and returns the objects.
    • update Takes the query and an object of properties you want updated.
    app.js
    const db = new Datastore({
      filename: './items.db',
      autoload: true
    })
    
    // Get all items from db and send them to the client
    ipcMain.on('loadAll', () => db.find({}, (err, items) => mainWindow.webContents.send('loaded', items)))
    
    // Saves item and returns it to client
    ipcMain.on('addItem', (e, item) => {
      db.insert(item, err => {
        if (err) throw new Error(err)
      })
    
      mainWindow.webContents.send('added', item)
    })
    

    Now we can start adding some more interesting functionality into our menu bar. For every object in our array we have a few options to customize it.

    • label Set the name shown on the menu bar
    • submenu Sets and array of objects as a sub directory, these can go on infinitely.
    • click Sets an onClick handler, takes in itself and the current focused window.
    • accelerator Sets any keyboard shortcuts, we can use process.platform to check what OS is being used, since Mac and Windows have a few different keys.
    • type Set menu item as one of electrons preset formats; normal, separator, checkbox, and radio.

    Along with our file menu, we’re going to add the option to toggle the dev tools to help debug our application.

    MenuBar.js
    const menuBar = [
      {
        label: 'file',
        submenu: [
          {
            label: 'Clear All',
            accelerator: process.platform == 'darwin' ? 'Command+C' : 'Ctrl+C',
            click(item, currentWindow) { currentWindow.webContents.send('clearAll') }
          }
        ]
      }, {
        label: 'DevTools',
        accelerator: process.platform == 'darwin' ? 'Command+I' : 'Ctrl+I',
        click(item, mainWindow) { mainWindow.toggleDevTools() }
      }
    ]
    

    With the cleared event being sent, we can clear our ul.

    script.js
    // Catches ClearAll from menu, sends the event to server to clear the db.
    ipcRenderer.on('clearAll', () => ipcRenderer.send('clearAll'))
    ipcRenderer.on('cleared', () => list.innerHTML = '')
    
    app.js
    // Clears database and send event to client if successful
    ipcMain.on('clearAll', () => {
      // Without multi being set to true only the first matching item with be removed.
      db.remove({}, { multi: true }, (err) => {
        if (err) throw new Error(err)
        mainWindow.webContents.send('cleared')
      })
    })
    

    Bundling

    Before we can bundle our new app we need some icons to go with it, but they need to be in the right formats for their operating system. icns for Mac and Linux and ico for Windows. I recommend using Cloud Convert to do this.

    When they’re all in the correct locations you can just run their script from our package.json file and a new release-builds folder should be created. In your OS’s folder you can find your new application ready to run natively on your machine.

    $ npm run package-mac
    $ npm run package-win
    $ npm run package-linux
    

    Conclusion

    While this may have been a bit of a lengthy process, I hope that you found this helpful in understanding the fundamentals of building your own desktop apps using Electron. You can find the full repo for this example here.

    Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

    Learn more about our products

    About the authors
    Default avatar
    Joshua Hall

    author

    While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

    Still looking for an answer?

    Ask a questionSearch for more help

    Was this helpful?
     
    Leave a comment
    

    This textbox defaults to using Markdown to format your answer.

    You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

    Try DigitalOcean for free

    Click below to sign up and get $200 of credit to try our products over 60 days!

    Sign up

    Join the Tech Talk
    Success! Thank you! Please check your email for further details.

    Please complete your information!

    Become a contributor for community

    Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

    DigitalOcean Documentation

    Full documentation for every DigitalOcean product.

    Resources for startups and SMBs

    The Wave has everything you need to know about building a business, from raising funding to marketing your product.

    Get our newsletter

    Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

    New accounts only. By submitting your email you agree to our Privacy Policy

    The developer cloud

    Scale up as you grow — whether you're running one virtual machine or ten thousand.

    Get started for free

    Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

    *This promotional offer applies to new accounts only.