「ElectronJSベースのプロジェクト」ElectronJSの機能に沿ったユーザーインターフェース（UI）の作成

高性能なキーバリューデータベースであるRedisは、ビッグデータアプリケーションに欠かせない要素となっています。

*本ブログは英語版からの翻訳です。オリジナルは[こちら](https://www.alibabacloud.com/blog/invoice-application-front-end-using-electronjs_582438)からご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。*

###電子テンプレートの作成
まずはElectronテンプレートの基本的なバージョンから始めてみましょう。これは、Official Electron RepositoryからフォークされたテンプレートのGitHubリンクです。試してみたい方は、以下のリンクにアクセスしてコードをクローンしてみてください: [https://github.com/saichandu415/electron-quick-start](https://github.com/saichandu415/electron-quick-start?spm=a2c65.11461447.0.0.50cc7759MyNikO)

以下は、コードをクローンした後に表示されるファイル構造です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/1d6a1f43-44a8-09e1-10d9-82f66ad04a4a.png)


各ファイルが何を担当しているのかを簡単に説明します。

**.gitignore**
多くの人がこのファイルを当たり前のように受け取っていますが、このファイルの本当の目的は、コードをコミット / git リポジトリにプッシュしている間、ファイルを無視することです。これにより、不要なファイルがリポジトリにプッシュされないようにします。

**LICENSE.md**
これは、オープンソースとしてリリースするコードのライセンス構造の概要を示します。すべてのライセンスをチェックして、自分に合ったものを選ぶようにしましょう。

**README.md**
これはリポジトリを開いたときに表示されるフロントページです。マークダウンはドキュメントを書くための特別なフォーマットで、内容を読みやすくするための十分な書式オプションがあり、非常に簡単です。

**Index.html**
プレゼンテーションのロジックを保存するファイルです。HTML と CSS を使用したすべての UI デザインはここにあります。あなたが従うUIアーキテクチャに基づいて、異なるファイルやアプリケーション全体のための1つのHTMLページを管理することが起こるかもしれません。また、タグが動作するようにするためには、いくつかの変更が必要です。

**main.js**
これはElectronアプリケーションの心臓部であり、IPCMainプロセスからIPCRendererプロセスへの完全な通信で構成されています。このファイルがどのように動作するかについては、コードを説明するときに詳しく説明します。このファイルはすべてのプロセス間通信を担当しています。

**Package.json**
このファイルは、Node.jsの経験がある人には馴染みのあるファイルでしょう。これと同等のものは、pom.xmlのようなjavaでも見つけることができます。

**Renderer.js**
これはプロセス間通信のレンダラー部分です。複数のrenderer.jsを持つことができますが、イベントの連鎖を維持したり追跡したりするのが非常に難しいので、複数のmain.jsを持つことはお勧めできません。

###ElectronJSを使用したデスクトップ請求書発行アプリケーション
ElectronJSをベースに請求書発行アプリケーションを作成しています。アプリケーションのUIを作成することから始めます。

**UIとUX**
私はBoomerang UI Kit ([https://github.com/webpixels/boomerang-ui-kit](https://github.com/webpixels/boomerang-free-bootstrap-ui-kit?spm=a2c65.11461447.0.0.50cc7759QDgmoa))を選びました。これは、レスポンシブで美しいユーザーインターフェースを簡単に開発する方法を提供してくれるbootstrapフレームワークの上に作成されています。このキットには、あらかじめ定義されたコードスニペットがたくさん用意されているので、簡単に始めることができます。

今日ご紹介するコードスニペットは、以下のレポから取得したものです。

[https://github.com/saichandu415/Electron-Invoice-ApsaraDB-MongoDB](https://github.com/saichandu415/Electron-Invoice-ApsaraDB-MongoDB?spm=a2c65.11461447.0.0.50cc7759Djxd8z)

コードをクローンすると、"Electron_Invoice "と "Invoice_Backend "があるはずです。Electron_Invoiceは、この記事で説明しているフロントエンドのコード全体で構成されています。

```
<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'>
```

基本的なBootstrapフレームワークの要素をスタイリングするために必要なCSSの構成要素をすべて含んだ「theme.css」をリンクしています。CSS のリンクは、Web 開発で行うように、簡単にできます。
**body**タグの最後に移動すると、いくつかのスクリプトがあります。そのうちの一つが

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

ここではrenderer.jsを注入しています。renderer.jsでは、完全なDOMとNPM(Node Package Manager)モジュールにアクセスできます。


```
<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>
```

これは、依存関係が適切に統合されるのを妨げるので、理解しておく必要がある最も重要なコードです。.jsの依存関係を追加し始める前に、「スクリプトの依存関係を開始する」というコメントの上にスクリプトを追加する必要があることに注意してください。そうしないと、JQueryなどのモジュールや他のライブラリが利用できなくなります。

Electronではすべてのライブラリがモジュールとして利用できるようになっているはずです。Starting & Ending script dependencies」のスクリプトの間にインポートを挟むことで 全てのライブラリが正常にインポートされ、利用可能になります。

使用しているインポートは以下の通りです。

**Jquery, Popper, Bootstrap** - Boomerang UIの相互依存性のためのもので、UIキットはその上に構築されています。

**Font Awesome** - フォントとして統合された完全にスケーラブルなアイコン。

**Page plugins** - HTMLの一部として使用している既存の要素に追加機能を作成するために使用します。

**Theme** - このJavaScriptコードは、アニメーション、補間などを担当するtheme.cssにバインドされています。

**ChartJS** - 分析結果をグラフィカルに表示するためにChartJSを使用しています。このライブラリはオープンソースであり、カスタマイズが容易な多くのチャートを提供しています。

**MustacheJS** - 実行時に簡単に動的にhtmlを作成するためにMustacheを使用しています。このライブラリは動的なhtmlを生成し、リストを使って値を置き換える作業を大幅に簡略化してくれます。

他のコードスニペットについて説明する前に、最終製品がどのようになるか見てみましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/a1deb72c-c0c4-f959-4df7-50f9b60c86cb.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/95bc9028-75a0-e05b-b7eb-7548ee741eb4.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/0b660f13-2d42-c002-86af-cd58490900c1.png)

販売の見える化

現在デザインしている2つの画面です。

- New Invoice
これは、ユーザーが新しい請求書を作成するためのフォームを持っているウィンドウです。
- Dashboard
ダッシュボードは、ユーザーの売上分析、総注文数、総売上高をグラフィカルな方法で見ることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/d6496b8b-b17f-4476-6885-10e7c258ac81.png)


上記のUI部分は、以下のコードで実現しています。

```
<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>
```

我々は、単一のページのUIアーキテクチャに従っており、1つのhtmlファイルで一度に全体のページを作成し、我々はそれが必要であることを示しています。

myTab "には新規請求書とダッシュボードのタブの完全なリストが表示されています。HTMLタグに適用されているクラスは無視しても良いかもしれません。

他の<div>の "tab-pane "は "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">
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/c843377a-deea-0912-f0c4-34df06c6b71c.png)

これはフォームで使用したコンポーネントの一つで、アイコンにはタグが作成され、先ほど追加したフォント awesome で利用可能になります。

**インプット**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/6c50c3eb-7d82-0a63-5a75-8448e13b8d6e.png)

```
<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>
```

この特定のコードは、テーブルヘッダーとそれに対するボタンと一緒に入力のセットを作成します。

緑色のボタンは、項目の詳細を提供するエントリを作成し、次のエントリを見ることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/4aa6ba97-a3de-e195-fc32-7b10e523158a.png)

アイテム詳細のカード入力は、以下のコードで作成されます。

```
<!-- 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 -->
```

このテンプレートは動的に生成され、以下の関数を使用して実行時に以下の表に追加されます。

```
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);

  }
```

クリックすると、"addItemCard() "が呼び出され、データがItemsObjにプッシュされます(後にサーバーにデータをプッシュするために使用されます)。また、Mustacheライブラリを使用してカードテンプレートを作成し、テーブルに追加します。

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

**Reset**
「リセット」ボタンを押すと、フォーム内のデータがリセットされ、新しい請求書フォームのようになります。

**Submit**
これは、データベースへのデータの挿入の世話をするAPIコールを行うもう一つの機能です。

```
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");
      }
    });
  }
```

これは、ECSインスタンスで動作している上記URIへのPOSTコールで、MongoDBインスタンスへのデータの挿入を担当しています。バックエンドについては近日中に詳しく説明します。

**ダッシュボード**
ダッシュボードは以下のコンポーネントで構成されています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/647921/01f42536-32d8-9929-ec39-1cd42a3d715c.png)

上記のコンポーネントは、以下のコードで作成しています。

```
<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>
```

上記のコードでは、MongoDBから最近更新された時間とともに、アイコン、ヘッダー、ボタンを持つカードを作成しています。

また、売上をそれぞれの日付でグラフィカルに表示するために chart.js を使用しています。スクリプトセクションの

タグを参考にしてください。非常にわかりやすいはずです。
データを取得して分析するために使用した機能については、コード内で

```
  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();
      }
    });
```

fetchAndLoadValues()関数は、ECSインスタンス上のAPIにAJAX(Asyncronous JavascriptとXML)呼び出しを行い、値を取得し、以下の操作を行います。

- セールバリューリフレッシュタイムの値を更新
- 総受注件数更新時間の値を更新
- 現在の総売却額
- その日の全注文の合計注文数
- グラフ用の総売却額と過去5日間の日付の配列の作成


###次は何をするのか？
今回は、このアプリケーションで使用するバックエンドサービスをどのように作成したのか、また、アプリケーションでどのような操作ができるのかを説明します。もしまだElectronの利点について読んでいないのであれば、このチュートリアルシリーズの前回の記事をぜひチェックしてみてください。

*アリババクラウドは日本に２つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
[アリババクラウドジャパン公式ページ](https://www.alibabacloud.com/ja)*