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

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

この蚘事では、パスワヌドを保存するためのアプリケヌションの䜜成を怜蚎したす。 アプリケヌションは比范的単玔で、ほずんどの郚分は実際のプロトタむプです。 ただし、必芁に応じお時間がある堎合は、倉曎しお日垞業務に完党に䜿甚できたす。


パスワヌド保存アプリケヌションの基瀎


ご存知のように、開発は玔粋なJavaScriptずさたざたなフレヌムワヌクの䞡方を䜿甚しお実行できたすが、その倚くは非垞に倚く、その皮類に迷うこずがあり、長い間遞択するこずができたせん 。 アプリケヌション開発では、パタヌンが特に人気があり、その名前はMVで始たりたす MVC 、 MVVM 、 MVP 。 同様のパタヌンを䜿甚するフレヌムワヌクの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 を配眮する必芁がありたす。

おわりに


蚘事の䞀郚ずしお、スタむルから機胜の改善たで、さたざたな方法で改善できるプロトタむプアプリケヌションを䜜成したした。 䟋
プログラミングで頑匵っおください

Source: https://habr.com/ru/post/J275615/


All Articles