Build a Shopping List App with Electron #3: Final Menu Template Steps & Add functionality

in #utopian-io7 years ago (edited)

3.png

What Will I Learn?

In this tutorial we gonna continue the main menu template after the overwrite the default template that we did before. and We will added that this feature to Mac OS and I will explain What should we use to apply this. then I will explain how to create interactive functions to achieve the purpose of the application.

  • Main Menu Template Support for Mac OS
  • Add Developer Tools
  • Add functionality by Using Vanilla JavaScript
  • Prepare add item functionality insinde addWindow.html & index.js

Requirements

  • Any Modern OS (Windows - Mac - Linux)
  • Text Editor (VSCode eg.)
  • Node.js (8.9.4) or above

Difficulty

  • Intermediate

Menu Template Support for Mac OS

if you started the app from mac you properly seeing electron by default not file. and basically to fix that we gonna push an empty object to mainMenuBar Array.

The following an example for the empty object:

const mainMenuBar =  [
    {}, //an empty object
    {
      label: 'File',
      submenu:[
        {
          label:'Add Item',
          click(){
            createAddWindow();
          }
        },
        {
          label:'Clear Items',
          click(){
            mainWindow.webContents.send('item:clear');
          }
        },
        {
          label: 'Quit',
          accelerator:process.platform == 'darwin' ? 'Command+Q' : 'Ctrl+Q',
          click(){
            app.quit();
          }
        }
      ]
    }
  ];

An issue will appear you will got a little space before file label that on other platforms like windows.
Sketch0.png

To fix that issue we gonna check to see if we are on a mac.
so if we are on a mac its gonna push an empty object in that case as we haved used before process.platform to define Mac OS as darwin

if(process.platform == 'darwin'){
    mainMenuBar.unshift({});
  }

Add Developer Tools

If you're not in a production mode you probably need a developer tools as well as the ability to reload the application that we have created or even edited.

So I will create if statement to check if we are not on production mode

  if(process.env.NODE_ENV !== 'production'){

  }

Then I gonna push to mainMenuBar Array the developer tools label as well as submenu

First I will create the developer tools label like the following in object:

  if(process.env.NODE_ENV !== 'production'){
    mainMenuBar.push({
      label: 'Developer Tools'
    });
  }

Then I will add a submenu that has Toggle DevTools ability

  if(process.env.NODE_ENV !== 'production'){
    mainMenuBar.push({
      label: 'Developer Tools',
      submenu:[
        {
          label: 'Toggle DevTools',
      ]
    });
  }

Then I gonna use click function that we need to pass in item, focusedWindow
then we need to make dev tools to popup on that on in our main window to show up.
and that by using focusedWindow.toggleDevTools(); Then I will add an accelerator for a hot key as Command + I on Mac OS and for Window, Linux we gonna use CTRL + I and then we gonna add a reload option and we gonna put all that inside submenu array as objects and we gonna use the comma as well.

  if(process.env.NODE_ENV !== 'production'){
    mainMenuBar.push({
      label: 'Developer Tools',
      submenu:[
        {
          role: 'reload'
        },
        {
          label: 'Toggle DevTools',
          accelerator:process.platform == 'darwin' ? 'Command+I' : 'Ctrl+I',
          click(item, focusedWindow){
            focusedWindow.toggleDevTools();
          }
        }
      ]
    });
  }

Let's test it by using npm start in terminal or command prompt.

image.png

It Works!

Add functionality by Using Vanilla JavaScript

Now we are ready to start the functionality of adding an item to the list so we gonna start off with the add window HTML that's where we are going to working on for a little bit.

Also we are gonna use some vanilla JavaScript to catch the item when some one puts in some text.
then we gonna use something called IPC renderer which is going to be used to basically send an event with a payload from our HTML file to the index.js
We gonna send that item here then we gonna take that item and send it to the main window.
so we can list it on the main window.

First Open addWindow.html then create script tags
then add variable electron that will require electron also create a variable called {ipcRenderer} and set that to electron.

The following is an example:

  <script>
    const electron = require('electron');
    const {ipcRenderer} = electron;
  </script>

Then we will need to catch that item. so we gonna use some Vanilla JavaScript
we are actually gonna using quit a bit from DOM. by using the document then querySelector as method to catch item

  <script>
    const electron = require('electron');
    const {ipcRenderer} = electron;

    const form = document.querySelector('item`);

  </script>

Then we gonna listen for a submit event and then we will create a submitForm function for that parameter inside AddEventListener and that's gonna get passed in an event parameter

  <script>
    const electron = require('electron');
    const {ipcRenderer} = electron;

    const form = document.querySelector('form');
    form.addEventListener('submit', submitForm);

    function submitForm(e){

    }
  </script>

Now we you try to submit a form its gonna automatically just try to submit to a file as it would normally so we need to stop that from happening so we need to take that event parameter our event object and call prevent default. Then create a console.log for a test.

The following is an example:

  <script>
    const electron = require('electron');
    const {ipcRenderer} = electron;

    const form = document.querySelector('form');
    form.addEventListener('submit', submitForm);

    function submitForm(e){
        e.preventDefault();
        console.log('Works');
    }
  </script>

Now Let's test it by using npm start from terminal or command prompt.

1.png

Now we will need to catch the text inside input that has an ID of item and we gonna set that value or text inside a new variable called item.

and then by using ipcRenderer we gonna use send method to send it after the catch of the item. it works like socket.io if you've an experience with that if you're sending it from the client to the sever you are just kind of giving it a name and then you are sending the data along with it then we gonna catch on the index.js side

  <script>
    const electron = require('electron');
    const {ipcRenderer} = electron;

    const form = document.querySelector('form');
    form.addEventListener('submit', submitForm);

    function submitForm(e){
        e.preventDefault();
      const item = document.querySelector('#item').value;
      ipcRenderer.send('item:add', item);
    }
  </script>

Now save it. then go to the index.js file
and to catch this we need to bring something called IPC main
so up inside the file as we gonna doing our destructing and we gonna add in ipcMain
that inside the variable of objects that we have already created before.

The Following is an example:

const {
    app,
    BrowserWindow,
    Menu,
    ipcMain
} = electron;

Then go under create AddWindow that we have created for garbage collection handle.
right above the mainMenuBar.

so just Create an ipcMain.on() then add the item whatever we gave that name which is item add so on that happens we gonna call a function and that function gonna passed in an event and the item. whatever we sent and what we want to do is send it to the main window coming from the add window. then close addWindow

The Following is an example:

ipcMain.on('item:add', function(e, item){
  mainWindow.webContents.send('item:add', item);
  addWindow.close(); 
});

and to make sure its coming from addWindow to mainWindow we gonna use a console.log() as we did before.

ipcMain.on('item:add', function(e, item){
    console.log(item);
  mainWindow.webContents.send('item:add', item);
  addWindow.close(); 
});

Now Let's test it by using npm start from terminal or command prompt.

image.png

Now we have finished that part of our series.

Here is the final result for index.js

const electron = require('electron');
const url = require('url');
const path = require('path');

const {app, BrowserWindow, Menu, ipcMain} = electron;

let mainWindow;
let addWindow;

app.on('ready', function () {
    mainWindow = new BrowserWindow({});
    mainWindow.loadURL(url.format({
        pathname: path.join(__dirname, 'mainWindow.html'),
        protocol: 'file:',
        slashes: true
    }));


    const mainMenu = Menu.buildFromTemplate(mainMenuBar);

    Menu.setApplicationMenu(mainMenu);


    mainWindow.on('closed', function () {
        app.quit();
    });

});


ipcMain.on('item:add', function(e, item){
    console.log(item);
    mainWindow.webContents.send('item:add', item);
    addWindow.close(); 
  });
  


  
// Create menu template
const mainMenuBar =  [
    // Each object is a dropdown
    {
      label: 'File',
      submenu:[
        {
          label:'Add Item',
          click(){
            createAddWindow();
          }
        },
        {
          label:'Clear Items',
          click(){
            mainWindow.webContents.send('item:clear');
          }
        },
        {
          label: 'Quit',
          accelerator:process.platform == 'darwin' ? 'Command+Q' : 'Ctrl+Q',
          click(){
            app.quit();
          }
        }
      ]
    }
  ];


function createAddWindow() {
    addWindow = new BrowserWindow({
        width: 300,
        height: 200,
        title: 'Add Shopping List Item'
    });
    addWindow.loadURL(url.format({
        pathname: path.join(__dirname, 'addWindow.html'),
        protocol: 'file:',
        slashes: true
    }));
    addWindow.on('close', function () {
        addWindow = null;
    });
}

// If OSX, add empty object to menu
if(process.platform == 'darwin'){
    mainMenuBar.unshift({});
  }
  
  // Add developer tools option if in dev
  if(process.env.NODE_ENV !== 'production'){
    mainMenuBar.push({
      label: 'Developer Tools',
      submenu:[
        {
          role: 'reload'
        },
        {
          label: 'Toggle DevTools',
          accelerator:process.platform == 'darwin' ? 'Command+I' : 'Ctrl+I',
          click(item, focusedWindow){
            focusedWindow.toggleDevTools();
          }
        }
      ]
    });
  }

and for addWindow.html


<!DOCTYPE html>
<html lang="en">

<head>
  <title>Add Shopping Item</title>
</head>

<body>
    <div class="container">
      <form>
        <div>
          <label>Enter Item</label>
          <input type="text" id="item" autofocus>
        </div>
        <button class="waves-effect waves-light btn" type="submit">Add Item</button>
      </form>
  </div>

  <script>
    const electron = require('electron');
    const {ipcRenderer} = electron;

    const form = document.querySelector('form');
    form.addEventListener('submit', submitForm);

    function submitForm(e){
        e.preventDefault();
      const item = document.querySelector('#item').value;
      ipcRenderer.send('item:add', item);
    }
  </script>

</body>

</html>

What's Next?

We will continue the functionality of add an item then we will create a functionality to clear items then in other parts we will create a modern look for our application by using some HTML/CSS frameworks.

Previous Tutorials


All Images/GIF has been created by @jinzo for an open source project.



Posted on Utopian.io - Rewarding Open Source Contributors

Sort:  

Thank you for the contribution. It has been approved.

You can contact us on Discord.
[utopian-moderator]

Another awesome tutorial

Very Informative.
Thanks.

Hey @jinzo I am @utopian-io. I have just upvoted you!

Achievements

  • Seems like you contribute quite often. AMAZING!

Suggestions

  • Contribute more often to get higher and higher rewards. I wish to see you often!
  • Work on your followers to increase the votes/rewards. I follow what humans do and my vote is mainly based on that. Good luck!

Get Noticed!

  • Did you know project owners can manually vote with their own voting power or by voting power delegated to their projects? Ask the project owner to review your contributions!

Community-Driven Witness!

I am the first and only Steem Community-Driven Witness. Participate on Discord. Lets GROW TOGETHER!

mooncryption-utopian-witness-gif

Up-vote this comment to grow my power and help Open Source contributions like this one. Want to chat? Join me on Discord https://discord.gg/Pc8HG9x