JavaScriptのデスクトップアプリケーション。 パート2

この記事は、記事「JavaScriptのデスクトップアプリケーション」の続きです パート1」 前のパートでは、次のことを確認しました。



この記事では、パスワードを保存するためのアプリケーションの作成を検討します。 アプリケーションは比較的単純で、ほとんどの部分は実際のプロトタイプです。 ただし、必要に応じて時間がある場合は、変更して日常業務に完全に使用できます。





パスワード保存アプリケーションの基礎



ご存知のように、開発は純粋なJavaScriptとさまざまなフレームワークの両方を使用して実行できますが、その多くは非常に多く、その種類に迷うことがあり、長い間選択することができません 。 アプリケーション開発では、パターンが特に人気があり、その名前はMVで始まります( MVCMVVMMVP )。 同様のパターンを使用するフレームワークの1つは、アプリケーションを開発するときに使用するAngular JSです。 それに慣れていない場合は、ドキュメント( チュートリアルAPI )を読むことをお勧めします。ロシア語のマニュアルで基本情報を学ぶこともできます。



アプリケーションは何になりますか? すべてのデータはテーブルに表示され、ログインが表示され、パスワードの代わりにアスタリスクが表示されます。 ユーザーは、新しいログイン/パスワードを追加したり、不要になったエントリを削除したりできます。 さらに、編集を提供する必要があります。



アプリケーションの基本機能を実装します。 これを行うには、ソースコードを見つけるフォルダーを作成し、その中にpackage.jsonも配置します(これを行う方法については、パート1を参照してください)。



次のディレクトリで構成される基本的なフォルダー構造を作成します。



さらに、 index.htmlファイルをプロジェクトルートに追加します。これは、アプリケーションへのエントリポイントになります。 基本的なマークアップを作成します。

基本的なマークアップ
<html ng-app="main"> <head> <meta charset="utf-8"> <title>Password keeper</title> <link rel="stylesheet" type="text/css" href="css/index.css"> </head> <body> <table> <thead> <tr> <td></td> <td>Login</td> <td>Password</td> <td></td> </tr> </thead> <tbody> <tr> <td></td> <td></td> <td></td> <td><a></a></td> </tr> </tbody> <tfoot> <tr> <td></td> <td><a></a></td> <td></td> <td></td> </tr> </tfoot> </table> <script type="text/javascript" src="lib/angular.min.js"></script> </body> </html>
      
      









アプリケーションは非常にシンプルであるため、コントローラーを作成し、その中にアプリケーションのすべてのメインロジックを配置します(ロジックが大きくなるにつれて、 Serviceフォルダーを追加し、その中にサービスを配置する必要があります。 「薄い」)。 コントローラーをmain 、コントローラーファイルをmain.ctrl.jsと呼びます したがって、コントローラーのブランク:



 (function () { 'use strict'; angular .module('main', []) .controller('MainCtrl', [MainCtrl]); function MainCtrl() { this.data = []; return this; } })();
      
      







プロトタイプのユーザー名/パスワードを含むデータは、 データ配列に配置されます 。 編集の実装を簡素化するには、独自のEditableText要素を作成し、ディレクティブとして配置します。 この要素は次のように機能します。要素はテキストとして表示され、要素をクリックするとテキストがテキスト入力フィールドに変わり、フォーカスが失われると要素がテキストとして再び表示されます。 これを行うには、 Viewフォルダー内にeditableText.htmlディレクティブのマークアップファイルを作成します。



 <input ng-model="value"> <span ng-click="edit()">{{value}}</span>
      
      







そして、 ディレクティブフォルダー内で、 editableText.jsファイルを作成します。

編集可能なテキストディレクティブコード
 (function () { 'use strict'; angular .module('main') .directive('editableText', [editableText]); function editableText() { var directive = { restrict: 'E', scope: { value: "=" }, templateUrl: 'view/editableText.html', link: function ( $scope, element, attrs ) { //     input   var inputElement = angular.element( element.children()[0] ); element.addClass( 'editable-text' ); // ,     ,    //     $scope.edit = function () { element.addClass( 'active' ); inputElement[0].focus(); }; //   , ..     inputElement.prop( 'onblur', function() { element.removeClass( 'active' ); }); } }; return directive; } })();
      
      









このディレクティブには、 index.css内に配置できるいくつかのスタイルも必要です



 .editable-text span { cursor: pointer; } .editable-text input { display: none; } .editable-text.active span { display: none; } .editable-text.active input { display: inline-block; }
      
      







ディレクティブの使用方法は次のとおりです。



 <editable-text value="variable"></editable-text>
      
      







ログインの場合、すべてが順調です。テキストまたはテキストフィールドのいずれかを表示しますが、パスワードは表示すべきではありません。 次のように、暗号フィールドをディレクティブのスコープに追加します。



 scope: { value: "=", crypto: "=" }
      
      







また、ディレクティブのマークアップも変更します。



 <input ng-model="value"> <span ng-click="edit()">{{crypto?'***':value}}</span>
      
      







さらに、index.htmlにスクリプトを追加することを忘れないでください:



 <script type="text/javascript" src="lib/angular.min.js"></script> <script type="text/javascript" src="controller/main.ctrl.js"></script> <script type="text/javascript" src="directive/editableText.js"></script>
      
      







機能を追加します。 次のようにコントローラーを変更します。

コントローラーコード
 function MainCtrl() { var self = this; this.data = []; this.remove = remove; this.copy = copy; this.add = add; return this; function remove(ind){ self.data.splice(ind, 1); }; function copy(ind){ //   }; function add(){ self.data.push({login: "login", password: "password"}); }; }
      
      









さらに、マークアップの変更が必要です。

最終マークアップ
 <body ng-controller="MainCtrl as ctrl"> <table> <thead> <tr> <td></td> <td>Login</td> <td>Password</td> <td></td> </tr> </thead> <tbody> <tr ng-repeat="record in ctrl.data track by $index"> <td><a ng-click="ctrl.copy($index)">{{$index}}</a></td> <td><editable-text value="record.login"></editable-text></td> <td><editable-text value="record.password" crypto="true"></editable-text></td> <td><a ng-click="ctrl.remove($index)"></a></td> </tr> </tbody> <tfoot> <tr> <td></td> <td><a ng-click="ctrl.add()"></a></td> <td></td> <td></td> </tr> </tfoot> </table> <script type="text/javascript" src="lib/angular.min.js"></script> <script type="text/javascript" src="controller/main.ctrl.js"></script> <script type="text/javascript" src="directive/editableText.js"></script> </body>
      
      









この段階で、スタイリングを行うことができます。 単純なスタイルの例( index.cssにスタイルを追加しますが、スタイルが多数ある場合は、スタイルをファイルに分割したり、 LESSなどのプリプロセッサを使用することもできます):

アプリケーションスタイリングの例
 table { border-collapse: collapse; margin: auto; width: calc(100% - 40px); } table, table thead, table tfoot, table tbody tr td:first-child, table tbody tr td:nth-child(2), table tbody tr td:nth-child(3), table thead tr td:nth-child(2), table thead tr td:nth-child(3) { border: 1px solid #000; } table td { padding: 5px; } table thead { background: #EEE; } table tbody tr td:first-child { background: #CCC; } table tbody tr td:nth-child(2) { background: #777; color: #FFF; } table tbody tr td:nth-child(3) { background: #555; color: #FFF; } table thead tr td:nth-child(2),table thead tr td:nth-child(3) { text-align: center; } table a { font-size: smaller; cursor: pointer; }
      
      









アプリケーションは次のとおりです。







クリップボードを操作する



そのため、アプリケーションの基礎は用意されていますが、主な目的をまだ実装していません。パスワードをコピーすることはできません(むしろ、コピーすることはできますが、非常に不便です)。 開始するには、NW.jsでクリップボードを操作することを検討してください

クリップボードという特別なオブジェクトがあります 。これは WindowsのクリップボードとGTKの抽象化として、またペーストボード(Mac)の抽象化として使用されます。 執筆時点では、テキストのみの書き込みと読み取りのサポートがあります。

オブジェクトを操作するには、使い慣れたnw.guiモジュールが必要です。



 var gui = require('nw.gui'); var clipboard = gui.Clipboard.get();
      
      







独自のオブジェクトを作成することはできず、システムオブジェクトのみを取得できることに注意してください。 次の3つの方法がサポートされています。



これで、アプリケーションの機能を終了でき、コントローラーは次のようになります。

コントローラーコード
 function MainCtrl() { var self = this; var gui = require('nw.gui'); var clipboard = gui.Clipboard.get(); this.data = []; this.remove = remove; this.copy = copy; this.add = add; return this; function remove(ind){ self.data.splice(ind, 1); }; function copy(ind){ clipboard.set(self.data[ind].password); }; function add(){ self.data.push({login: "login", password: "password"}); }; }
      
      









パスワード保存



アプリケーションの起動後、ユーザーはいくつかのパスワードを保存し、アプリケーションを閉じました。 翌日、パスワードがなくなったことがわかりました。 問題は、それらを閉じるときに削除された通常のローカル変数に保存したことです。

3番目の部分では、NW.jsがデータベースでどのように機能するかを見ていきますが、今のところはlocalStorageにパスワードを保存します 。 機能の作成を開始する前に(使用しているアプリケーションは単なるプロトタイプですが)、セキュリティに注意する必要があります。 これを行うには、パスワードを平文で保存しないでください。

暗号化/復号化用のさまざまなJavaScriptライブラリがあります。 そのようなライブラリの1つにcrypto-jsがあります。 node.jsのモジュールとしてインストールします。 ライブラリは多数の標準をサポートしており、その完全なリストはドキュメントに記載されています。 同時に、すべてのモジュールと個別のモジュールの両方を接続できます。



 //   ,         CryptoJS.HmacSHA1 var CryptoJS = require("crypto-js"); //   ,  AES var AES = require("crypto-js/aes");
      
      







メッセージを暗号化するには、暗号化方式が使用されます。



 var ciphertext = CryptoJS.AES.encrypt('', ' ');
      
      







復号化はもう少し複雑です:



 var bytes = CryptoJS.AES.decrypt(ciphertext.toString(), ' '); var plaintext = bytes.toString(CryptoJS.enc.Utf8);
      
      







アプリケーションを変更して、アプリケーションを閉じるときにパスワードを保存し、起動時にロードできるようにします。

crypto.svcサービスを作成し、 サービスフォルダーに配置します(このフォルダーをまだ作成していない場合は、アプリケーションルートに作成します)。

サービスコード
 (function () { 'use strict'; angular .module('main') .factory('CryptoService', [CryptoService]); function CryptoService() { var CryptoJS = require("crypto-js"); var secretKey = "secretKey"; var service = { encrypt: encrypt, decrypt: decrypt }; return service; function encrypt(data) { return CryptoJS.AES.encrypt(JSON.stringify(data), secretKey); } function decrypt(text) { var bytes = CryptoJS.AES.decrypt(text.toString(), secretKey); var decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8)); return decryptedData; } } })();
      
      









サービスを使用するには、コントローラーをアップグレードします。

コントローラーコード
 (function () { 'use strict'; angular .module('main', []) .controller('MainCtrl', ['$scope', 'CryptoService', MainCtrl]); function MainCtrl($scope, CryptoService) { var self = this; var gui = require('nw.gui'); var clipboard = gui.Clipboard.get(); var localStorageKey = "loginPasswordData" this.data = []; this.remove = remove; this.copy = copy; this.add = add; load(); $scope.$watch("ctrl.data", save, true); return this; function remove(ind){ self.data.splice(ind, 1); }; function copy(ind){ clipboard.set(self.data[ind].password); }; function add(){ self.data.push({login: "login", password: "password"}); }; function load(){ var text = localStorage.getItem(localStorageKey); if(text) { self.data = CryptoService.decrypt(text); } } function save(){ if(self.data) { localStorage.setItem(localStorageKey, CryptoService.encrypt(self.data)); } } } })();
      
      









サービスの接続に加えて、 AngularJSに既に存在する$ scopeサービスも必要でした。 $ watchメソッドを使用して、データが変更された瞬間を追跡し、それらを時間内に保存します(3番目の引数がtrueであるため、変更は配列だけでなく、挿入/削除だけでなく、変更も追跡されます配列要素、つまり個々の配列要素のユーザー名またはパスワードの変更)。 ビューを開くと、データの読み込みが行われます。



トレイに向ける



アプリケーションの基礎は用意されていますが、ご存知のように、このようなプログラムはシステムトレイに最小化されているため、多くの開いているウィンドウでユーザーが過負荷にならないようになっています。

NW.jsに導入されたもう1つの抽象化は、トレイです。Windowsのシステムトレイ アイコン 、GTKのステータスアイコン 、OSXのステータスアイテム 。 このオブジェクトは、既知のguiモジュールを使用して作成されます。



 var gui = require("nw.gui"); var tray = new gui.Tray({ title: 'Tray', icon: 'img/icon.png' });
      
      





このオブジェクトを操作するときは、変数のスコープに注意する必要があります。関数内で変数を作成すると、 GCによってすぐに削除されます。 オブジェクトを作成するとき、例で行ったように、すぐにプロパティを作成できます。または、少し後でこれを処理できます。 このオブジェクトには、次のプロパティを定義できます。



アプリケーションでトレイを使用するには、マークアップ要素を作成する必要があります 。最も明白なオプションはボタンbuttonです。 次に、 クリックイベントをサブスクライブし、 windowオブジェクトのメソッドを使用する必要があります。これについては、これから説明します。



ウィンドウでの作業



ウィンドウを操作するには、既存のウィンドウを取得するか、新しいウィンドウを作成する必要があります。 したがって、アプリケーションが表示されている現在のウィンドウを取得するには、次のコマンドを実行する必要があります。



 var win = gui.Window.get();
      
      





そして、新しいウィンドウを作成するには、ページが配置されているアドレス、このウィンドウで開くアドレス、および開くためのパラメーターを指定する必要があります(これらのパラメーターは、マニフェストの作成時に指定したものに対応します。一連の記事の最初の部分を参照)。



 var win = gui.Window.open ('https://myurl', { position: 'center', width: 901, height: 127 });
      
      





パラメータに特別なプロパティフォーカスを渡すこともできます。trueを指定すると、新しく作成されたウィンドウはすぐにフォーカスを受け取ります。そうでない場合、フォーカスは現在のウィンドウに残ります。

新しいウィンドウを作成し、作成後にそれを使用したい場合は、対応するイベントにサブスクライブする必要があります。



 win.on ('loaded', function(){ //   document  ,       var document = win.window.document; //      ... });
      
      







この例からわかるように、ウィンドウのプロパティの1つはウィンドウオブジェクトです。このオブジェクトから、ドキュメントを含む残りの要素を取得できます。 このプロパティに加えて、ウィンドウは他の多くの機能もサポートしています。

これらのプロパティは読み取るだけでなく、変更することもできます。 それらに加えて、読み取り専用プロパティもあります(それらはすべて論理的であり、trueまたはfalseになります)



オブジェクトは、プロパティに加えて、多数のメソッドをサポートしています。 主な方法を以下に示し、便宜上グループ化しています。

ウィンドウの位置とサイズを変更する方法:



フォーカスと可視性を操作する方法:



ウィンドウを閉じる、最小化/拡張を制御する方法:



状態を管理する方法:



ウィンドウのサイズを変更する機能を制御する方法



したがって、 トレイおよびウィンドウオブジェクトに精通しているので、 トレイに最小化する機能を記述します。 これを行うには、ボタンまたはリンクなどの要素をマークアップに追加します(前述のとおり)。

 <a ng-click="ctrl.toTray()"> </a>
      
      





そして、コントローラーを次のように変更します。

コントローラーコード
 (function () { 'use strict'; angular .module('main', []) .controller('MainCtrl', ['$scope', 'CryptoService', MainCtrl]); function MainCtrl($scope, CryptoService) { var self = this; var localStorageKey = "loginPasswordData" this.data = []; var gui = require('nw.gui'); var clipboard = gui.Clipboard.get(); var win = gui.Window.get(); var tray = new gui.Tray({ title: 'Tray', icon: 'img/test.png' }); tray.on("click", restoreFromTray); this.remove = remove; this.copy = copy; this.add = add; this.toTray = toTray; load(); $scope.$watch("ctrl.data", save, true); return this; function remove(ind){ self.data.splice(ind, 1); }; function copy(ind){ clipboard.set(self.data[ind].password); }; function add(){ self.data.push({login: "login", password: "password"}); }; function load(){ var text = localStorage.getItem(localStorageKey); if(text) { self.data = CryptoService.decrypt(text); } } function save(){ if(self.data) { localStorage.setItem(localStorageKey, CryptoService.encrypt(self.data)); } } function toTray(){ win.minimize(); win.setShowInTaskbar(false); } function restoreFromTray(){ win.restore(); win.setShowInTaskbar(true); } } })();
      
      







また、この例が機能するには、 imgフォルダーを作成し、そこにトレイアイコン(この例ではimg / test.png )を配置する必要があります。



おわりに



記事の一部として、スタイルから機能の改善まで、さまざまな方法で改善できるプロトタイプアプリケーションを作成しました。 例:

プログラミングで頑張ってください!



All Articles