Invoice Application Front-end Using ElectronJS

简介: In part two of this three-part tutorial, we will explore how to create the front-end of a fully functional invoice application using ElectronJS.

By Sai Sarath Chandra, Alibaba Cloud Tech Share Author and Alibaba Cloud MVP

In our previous article, we discussed in detail about:

  • What ElectronJS is
  • Why you should opt for ElectronJS
  • Benefits and limitations of ElectronJS

We also briefly talked about how Electron handles a simple use case.

In this article, we will setup an ElectronJS based project, creating a user interface (UI) alongside ElectronJS functionalities. We will also go into detail as we create the functionality which forms the building blocks for any Electron application.

We will be using the Boomerang UI Kit, which is built on Bootstrap Framework. Bootstrap provides nice and customizable components, which can almost be used in any web development. The reason I choose Boomerang UI Kit is more of a personal choice. You can choose any other framework to work with this tutorial as we are more focusing only on the ElectronJS functionalities rather than the Frontend frameworks.

Creating an Electron Template

Let’s start with the basic version of the Electron Template. This is the GitHub link of the template forked from the Official Electron Repository. If you would like to experiment, then go ahead and clone the code by visiting this link: https://github.com/saichandu415/electron-quick-start

Below is the file structure you should see after cloning the code.

1

Let's talk briefly about what each file is responsible for

.gitignore
Many have taken this file for granted, but the real purpose of the file is to ignore the files while committing / pushing the code to the git repository. This will make sure unnecessary files are not pushed into the repository.

LICENSE.md
This outlines the Licensing structure of the code you are releasing as an open source. Make sure you check all the licenses and pick what suits best for you

README.md
This is the front page you see when the repository is opened. The markdown is a special format of writing the documentation which is very easy with enough formatting options to make the content readable.

Index.html
The file where we should keep our presentation logic. All the UI design with HTML and CSS should go here. It might happen we maintain different files or a single html page for the whole application based on the UI architecture you follow. There are also couple of changes you need to make to make sure your tag works, we will discuss that shortly.

main.js
This is the heart of the Electron application, this consists of the complete communication from the IPCMain process to the IPCRenderer process. We will see in detail how it works when we discuss the code. This file is responsible for all the Inter Process Communication.

Package.json
This file should be familiar to you if you have experience with Node.js else this nothing but a file with a list of dependencies for production and development. The equivalent can also be found in java like pom.xml.

Renderer.js
This is the renderer part of the Inter Process Communication. Note that you can have multiple renderer.js but not advisable to have multiple main.js as it very difficult to maintain and follow the chain of events.

Desktop Invoicing Application using ElectronJS

We are creating the Invoicing Application based on ElectronJS. We start by creating the UI for the application

UI & UX
I chose Boomerang UI Kit (https://github.com/webpixels/boomerang-ui-kit) . This is created on top of bootstrap framework which provide easy way to develop responsive & beautiful User Interface. There are so many pre-defined code snippets which helps you get started.

The whole code snippets I show you today is taken from this repo below

https://github.com/saichandu415/Electron-Invoice-ApsaraDB-MongoDB

Clone the code, and you should find "Electron_Invoice" and "Invoice_Backend". Electron_Invoice consists of the whole frontend code we are discussing in this article.

<link href="https://fonts.googleapis.com/css?family=Nunito:400,600,700,800|Roboto:400,500,700" rel="stylesheet">
<!-- Theme CSS -->
<link type="text/css" href="./assets/css/theme.css" rel="stylesheet">
<!-- Font Awesome -->
<link href='http://fonts.googleapis.com/css?family=Grand+Hotel' rel='stylesheet' type='text/css'>

We are linking theme.css, which contains all the needed CSS constructs for styling the elements of the basic Bootstrap framework. Linking CSS is straight forward as we do in web development

If we navigate to the end of the </body> tag you will find several scripts. One of which is

<script>
// You can also require other files to run in this process
require('./renderer.js');
</script>

Here we are injecting renderer.js, In renderer.js you can access complete DOM and NPM (Node Package Manager) modules.

<script>
if (typeof module === 'object') {
window.module = module;
module = undefined;
}
</script>
<!-- Start Script dependencies -->
<!-- Core -->
<script src="./assets/vendor/jquery/jquery.min.js"></script>
<script src="./assets/vendor/popper/popper.min.js"></script>
<script src="./assets/js/bootstrap/bootstrap.min.js"></script>
<!-- FontAwesome 5 -->
<script src="./assets/vendor/fontawesome/js/fontawesome-all.min.js" defer></script>
<!-- Page plugins -->
<script src="./assets/vendor/bootstrap-select/js/bootstrap-select.min.js"></script>
<script src="./assets/vendor/bootstrap-tagsinput/bootstrap-tagsinput.min.js"></script>
<script src="./assets/vendor/input-mask/input-mask.min.js"></script>
<script src="./assets/vendor/nouislider/js/nouislider.min.js"></script>
<script src="./assets/vendor/textarea-autosize/textarea-autosize.min.js"></script>
<!-- Theme JS -->
<script src="./assets/js/theme.js"></script>
<!-- Chart JS Plugin -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.min.js"></script>
<!-- Mustache JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.3.0/mustache.js"></script>
<!-- Ending Script dependencies -->
<script>
if (window.module) module = window.module;
</script>

This is the most important piece of code you need to understand as this hinders the dependencies to integrate properly. Note that before you start adding any .js dependencies, we need to add the script above the comment "starting script dependencies". If not, modules like JQuery and other libraries will not be available.

In Electron all the libraries should be available as modules. By enclosing the imports between the scripts of "Starting & Ending script dependencies". All are imported successfully and made available.

The imports we are using include:

Jquery, Popper, Bootstrap – for Boomerang UI interdependencies as the UI Kit is built on top of that.

Font Awesome – for fully scalable icons integrated as font.

Page plugins – These are used to creates an additional functionality to the existing elements we are using as part of html.

Theme – This JavaScript code is more bound with the theme.css which is also responsible for the animations, interpolations, etc...

ChartJS – We are using Chart JS for the showing the analytical analysis in the graphical form. This library is open-source and it provides a lot of charts which are easy to customize.

MustacheJS – We used Mustache to create html dynamically at runtime with ease. This library greatly simplifies the work of generating the dynamic html and replacing the values using a list.

Before I discuss any other code snippet, let's see how our end product would look like.

2

3

4

Sales Visualization

These are the two screens we are designing now:

  • New Invoice
    This is the window where we have a form to make user create a new invoice.
  • Dashboard
    Dashboard is where we see the user sales analysis, total orders and total sales in a graphical way.

5

The above UI Part is achieved by the following code:

<div class='navigationBar'>
        <ul class="nav nav-pills nav-fill flex-column flex-sm-row" id="myTab" role="tablist">
          <li class="nav-item">
            <a class="nav-link mb-sm-3 active" id="home-tab" data-toggle="tab" href="#newInvoice" role="tab" aria-controls="home" aria-selected="true">New Invoice</a>
          </li>
          <li class="nav-item">
            <a class="nav-link mb-sm-3" id="profile-tab" data-toggle="tab" href="#dashboard" role="tab" aria-controls="profile" aria-selected="false">DashBoard</a>
          </li>
        </ul>
        <div class="tab-content" id="myTabContent">
<div class="tab-pane fade show active" id="newInvoice" role="tabpanel" aria-labelledby="home-tab">
** Content of Home **
</div>
          <div class="tab-pane fade" id="dashboard" role="tabpanel" aria-labelledby="profile-tab">
** Content of DashBoard **
</div>

We follow the single page UI architecture wherein we will create the whole page at once in a single html file and we show that is needed.

If you see the “myTab” holds the complete list of tabs – New Invoice and Dashboard. You might ignore the Classes applied to the HTML Tags, they’re necessary else the tabs might not have the same presentation format.

The other <div>’s with “tab-pane” hold the content related to both “New Invoice” & “Dashboard”.

<div class="input-group-prepend">
   <span class="input-group-text">
       <i class="fas fa-user"></i>
   </span>
</div>
<input type="text" class="form-control" id="customerName" placeholder="Customer Name">

6

This is one of the component that we used in the form, the icon will be created the tag and they are available with font awesome which we added earlier.

Input

7

<table class="table">
              <thead class="thead-dark">
                <tr>
                  <th scope="col" class='centerme'>Item Details</th>
                  <th scope="col" class='centerme'>Qty</th>
                  <th scope="col" class='centerme'>Rate</th>
                  <th scope="col" class='centerme'>Amount</th>
                  <th scope="col"></th>

                </tr>
              </thead>
              <tbody>
                <tr>
                  <td>
                    <div class="form-group">
                      <input type="text" id="itemDetails" class="form-control border-0" placeholder="Item Details">
                    </div>
                  </td>
                  <td>
                    <div class="form-group">
                      <input type="number" id="quantity" class="form-control border-0" placeholder="Quantity">
                    </div>
                  </td>
                  <td>
                    <div class="form-group">
                      <input type="number" id="rate" class="form-control border-0" placeholder="Rate">
                    </div>
                  </td>
                  <td>
                    <div class="form-group">
                      <input type="number" id="amount" class="form-control border-0" placeholder="Amount" readonly>
                    </div>
                  </td>
                  <td class=''>
                    <button type="button" onclick="addItemCard()" class="btn btn-success btn-icon-only rounded-circle">
                      <span class="btn-inner--icon">
                        <i class="fas fa-check"></i>
                      </span>
                    </button>
                  </td>
                </tr>
              </tbody>
            </table>

This particular piece of code creates the Table header & set of inputs along with the button against it.

The green button creates an entry with the Item details provided, then you can see the following entry

8

The card input with the Item Details are created with the following code

<!-- Template here Starts -->
<script id="tutorial-template" type="text/template">
  <tr class="bg-white">
    <th scope="row">
      <div class="media align-items-center">
        <span class="avatar avatar-lg bg-pink mr-3">{{itemShortName}}</span>
        <div class="media-body">
          <h6 class="h5 font-weight-normal mb-0">{{ItemName}}</h6>
          <span class="font-weight-normal text-muted">Quantity :
            <b>{{qty}}</b>
          </span>
        </div>
      </div>
    </th>
    <td>Rate/pc : {{rate}}</td>
    <td>Amount : {{amount}}</td>
  </tr>
  <tr class="table-divider"></tr>
</script>
<!-- Template here ends -->

This template is generated dynamically and added to the below table at runtime using the below function

function addItemCard() {
    var data = {};

    data.itemShortName = ($("#itemDetails").val()).substring(0, 2).toUpperCase();
    data.ItemName = $("#itemDetails").val();
    data.qty = $("#quantity").val();
    data.rate = $("#rate").val();
    data.amount = $("#amount").val();

    if (!itemsObj.customerName) {
      itemsObj.customerName = $("#customerName").val();
    }

    if (!itemsObj.invoiceNumber) {
      itemsObj.invoiceNumber = $("#invoiceNumber").val();
    }
    if (!itemsObj.invoiceDate) {
      itemsObj.invoiceDate = $("#invoiceDate").val();
    }
    if (!itemsObj.dueDate) {
      itemsObj.dueDate = $("#dueDate").val();
    }
    itemsObj.itemsData.push(data);
    var template = $("#tutorial-template").html();
    var html = Mustache.render(template, data);
    $("#cardsList").append(html);

  }

Upon clicking, “addItemCard()” will be invoked and the data is pushed into the ItemsObj (Which will be used later to push data into the server). It also creates the card template using the Mustache library and appends it to the table.

<table class="table table-hover table-cards align-items-center">
              <tbody id="cardsList">
                <!-- Dynamically generated template goes here -->
              </tbody>
            </table>

Reset
We have the “Reset” button which resets the complete data in the form to like a new Invoice form.

Submit
This is one more functionality where we will make an API call which takes care of inserting the data to the database.

function submitData() {
    console.log('submit Clicked');
    var itemsArr = itemsObj.itemsData;
    var totalAmount = 0;
    console.log(itemsArr);

    for (var i = 0; i < itemsArr.length; i++) {
      totalAmount = totalAmount + parseFloat(itemsArr[i].amount);
    }
    console.log(totalAmount);
    itemsObj.totalAmount = totalAmount;
    console.log(JSON.stringify(itemsObj));

    $.ajax({
      type: 'POST',
      data: JSON.stringify(itemsObj),
      contentType: 'application/json',
      url: 'http://149.129.130.26:443/data/invoice',
      success: function (data) {
        console.log('success');
        console.log(JSON.stringify(data));
        $("#modal_5").modal("show");
      }
    });
  }

This is the POST Call to the above-mentioned URI, which is operating in the ECS Instance and takes care of inserting data into the MongoDB instance. We will discuss more about the backend soon.

Dashboard
The dashboard is made of below components

9

The above component is created using the following code:

<div class="card">
                  <div class="card-header">
                    <div class="row align-items-center">
                      <div class="col-8">
                        <h4 class="heading h5 mb-0">Today Orders</h4>
                      </div>
                      <div class="col-4">
                        <div class="card-icon-actions text-right">
                          <a href="#" class="favorite" data-toggle="tooltip" data-original-title="Save as favorite">
                            <i class="fas fa-star"></i>
                          </a>
                          <a href="#" class="love active" data-toggle="tooltip" data-original-title="Love this">
                            <i class="fas fa-heart"></i>
                          </a>
                        </div>
                      </div>
                    </div>
                  </div>
                  <div class="card-body">
                    <p class="card-text" id="saleCount">Count : 0</p>
                  </div>
                  <div class="card-footer">
                    <div class="row align-items-center">
                      <div class="col-6">
                        <a href="#" class="btn btn-sm btn-primary" onclick="fetchAndLoadValues()">Refresh Now</a>
                      </div>
                      <div class="col-6 text-right">
                        <span class="text-muted" id="saleCountUpdateValue">2 hrs ago</span>
                      </div>
                    </div>
                  </div>

The above code creates the Card with the icons, header and button along with the recent refreshed time from MongoDB.

We have also used the chart.js for the Graphical representation of the sales with the respective dates. You can see that in the script section under the

tag for reference. It should be very obvious.

We will discuss the functionality we have used to fetch data and analyze in the code

  function fetchAndLoadValues() {
    var ajaxResult = {};
    $.ajax({
      url: "http://149.129.130.26:443/data/dashboard", success: function (result) {
        console.log(result);
        ajaxResult = result;
        dataVariable.data.labels = ajaxResult.datesArr;
        dataVariable.data.datasets[0].data = ajaxResult.salesArr;
        $('#saleValueUpdateTime').text(timeNow());
        $('#saleCountUpdateValue').text(timeNow());
        $('#saleValue').text("sale Value : $ " + (ajaxResult.salesArr)[0]);
        $('#saleCount').text("Count : " + (ajaxResult.ordersArr)[0]);
        loadLineChart();
      }
    });

The “fetchAndLoadValues()” function will make an AJAX (Asyncronous Javascript and XML) call to the API on ECS instance and fetches the values and do the below operations

  • Update the value of Sale Value refresh Time
  • Update the value of Total Orders Count update time
  • The total sale Value of the current day
  • Total order count of all the orders on that day
  • Creating an array of total sale value & dates of the previous 5 days for the graph

What's Next?

We will be discussing the how we created the backend services used for this application and what operations we can perform with our application. If you haven't read about the benefits of Electron, then you should definitely check out the previous article from this tutorial series.

目录
相关文章
|
网络协议 MySQL 关系型数据库
auto,string.back(),string.pop_back() 不能用?使devc++拥有c++11的功能
auto,string.back(),string.pop_back() 不能用?使devc++拥有c++11的功能
314 0
auto,string.back(),string.pop_back() 不能用?使devc++拥有c++11的功能
|
Swift iOS开发
Xcode10 NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END
前言 升级成 Xcode 10 之后每次 New File 看到 .h 基本都能看到 NS_ASSUME_NONNULL_BEGIN 和 NS_ASSUME_NONNULL_END 成对出现在 @interface 与 @end 上下, 包裹住它, 这两对关键字并非新特性, 只是 Xcode 10 之后系统默认实现了, 应该是考虑到与 Swift 混编, 为了更好兼容其 optional 与 non-optional。
1844 0
|
缓存 NoSQL Redis
BITCOUNT key [start end]
统计字符串被设置为1的bit数. 一般情况下,给定的整个字符串都会被进行计数,通过指定额外的 start 或 end 参数,可以让计数只在特定的位上进行。 start 和 end 参数的设置和 GETRANGE 命令类似,都可以使用负数值:比如 -1 表示最后一个位,而 -2 表示倒数第二个位,以此类推。
1348 0
|
Web App开发 前端开发 JavaScript
|
JavaScript 前端开发 数据安全/隐私保护