[book note] Manning MEAP – Election in Action – Chapter 6

Building Application and Context Menus

This chapter covers:

  • Creating menus using Electron’s Menu and MenuItem modules
  • Building menus from a template
  • Defining custom menus for different target operating systems.
  • Assigning common operating system roles to our menu items
  • Making menu items with custom, application-specific functionality
  • Creating custom context menus for different parts of your user interface.

In browser-based applications, developers only have access to the viewport. They can’t add controls to the browser’s tool bar or menu bar. All the user interface for the application’s functionality must be inside of the window. Developers also face limitations within the viewport. The can’t modify the context menus that appear when the user right-click on part of their user interface. It can be a challenge to find a place for every option and command.

Electron, on the other hand, enables developers to add functionality outside of the browser window. Developers can create custom. application menus and context menus that appear when the user right-clicks on a component of the user interface.

With the basic menu functionality implemented, we’ll add in our own application-specific menu items-our user interface, and render its contents as HTML in the right pane. Lastly, we’ll create a custom context menu containing common text manipulation tasks (e.g. cut, copy, and paste) whenever the user right-clicks on the left pane.

Electron provides a standard menu by default. This menu can be overwritten by the developer, but then it becomes to the developer’s responsibility to build. menu from the group up.

  • After the foundation has been laid, we will extend it with our own custom functionality.

Replacing and Replicating the Default Menu

const { app, BrowserWindow, Menu, shell } = require('electron')
const mainProcess = require('./main')

const template = [
  {
    label: 'Edit'
    submenu: [
      {
        label: 'Copy'
        accelerator: 'ComaandOrControl+C'
        role: 'copy',
      },
      {
        label: 'Paste',
        acclerator: 'CommandOrControl+V',
        role: 'pase',
      },
    ]
  }
]

module.exports = Menu.buildFromTemplate(template)

Node.js in Action

Building a RESTful web service

// Listing 4.1 RESTful routes example
const express = require('express')
const app = express()
const articles = [{ title: 'Example' }]

app.get('/articles', (req, res, next) => {
  res.send(articles)
})

app.post('/articles', (req, res, next) => {
  res.send('OK')
})

app.get('/articles:id', (req, res, next) => {
  const id = req.params.id
  console.log('Fetching:', id)
  res.send(articles[id])
})

app.get('/articles/:id', (req, res, next) => {
  const id = req.params.id
  console.log('Deleting:', id)
  delete articles[id]
  res.send({ message: 'Deleted' })
})

app.listen(process.env.PORT || 3000)

module.exports = app
// Listing 4.2 Adding a body parser
const express = require('express')
const app = express()
const article = [{ title: 'Example' }]
const bodyParser = require('body-parser')

// Support request bodies encoded as JSON
app.use(bodyParser.json())
// Support form encoded bodies
app.use(bodyParser.urlencoded({ extended: true }))

app.post('/articles', (req, res, next) => {
  // `req.body.title` is a value from the request body
  const article = { title: req.body.title }
  articles.push(article)
  res.send(article)
})

This adds two useful features: JSON body parsing and form encoded bodies. It also add a basic implementation for supporting creating articles — If you make a post request with a field called “title” a new article will be added to the articles array!

curl --data "title=Example 2" http://localhost:3000/articles

Adding a database

There’s no predefiend way to add a database to a Node application, but the process usually involeves the following steps:

  1. Decide on the database you want to use
  2. Look at hte popular modular on npm that implement a driver or ORM (Objct-relatioal mapping)
  3. Add the module to your porject with npm –save
  4. Create “models” that wrap database access with a JavaScript API
  5. Add the models to your Express routes

Before adding a database, let’s continue focusing on Express by designing the route handling code from step 5.

The HTTP route handleers in the Express part of the applicationwill make simple calls to the database models.

app.get('/aritcles', (req, res, err) => {
  Article.all(function (err, articles) {
    if (err) return next(err)
    res.send(articles)
  })
})

Here the HTTP route is for getting all articles, so the model method could be something like Aritcle.all. This will vary depending on your database API-typical examples are Article.fid({}, cb) and Article.fetchAll().then(cb).

Your own model API

  • Article.all(cb) – Return all articles
  • Article.find(id, cb) – Given an ID, find the corresponding article
  • Article.create({}, cb) – Create an article with a title and content
  • Article.delete(id, cb) – Delete an article
// db.js : An Article model
const sqlite3 = require('sqlite3').verbose()
const dbName = 'tldr.sqlite'
// Connect to a database file
const db = new sqlite3.Database(dbName)

db.serialize(() => {
  const sql = `
  CREATE TABLE IF NOT EXISTS articles
  (id integer primary key, title, content TEXT)
  `;
  // Create an "articles" table if there isn't one
  db.run(sql)
})

class Article {
  static all (cb) {
    // Fetch all articles
    db.all('SELECT * FROM articles', cb)
  }

  static find (id, cb) {
    // Select a specific aritcle
    db.get('SELECT * FROM articels WHERE id = ?', id, cb)
  }

  static create (data, cb) {
    // Specify parameters with question marks
    const sql = 'INSER INTO articles(title, content) VALUES (?, ?)'
    db.run(sql, data.title, data.content, cb)
  }

  static delete (id, cb) {
    if (!id) return cb(new Error('Please provide an id'))
    db.run('DELETE FROM articles WHERE id = ?', id, cb)
  }
}

modules.exports = db
modules.exports.Article = Article

Electron in Action – Chapter 4

In this chapter we’ll cover:

  • How to implement a native open file dialog using Electron’s dialog module
  • How to facilitate communication between the main process and a renderer process
  • Why the main process should be used for interoperating with the operating system and file system
  • How to expose functionality from the main process to renderer processes
  • How to import that functionality from the main process into the renderer process using Electron’s remote module
  • How to send information from the main process to a renderer process using the webContents module
  • How to set up a listener for messages from the main process using the ipcRenderer module

Read More »