
写真:映画「ベストシューター」のトム・クルーズ
この記事では、シングルページHTMLアプリケーションがJavaScriptを介してクラウドMongoDBと対話する方法について説明します。 MongoDB-as-a-Serviceとして、
Mongolabを使用します。 500 MBのボリュームで展開されたMongoDBのコストはわずか0米ドルです。
ToDoリストを作成するために、
バックエンドは必要ありません。 REST APIを介してMongolabとやり取りし、サードパーティのJavaScriptフレームワークの助けを借りずに、クライアント部分でそのためのラッパーを作成します。
記事のナビゲーション
1. Mongolabに登録してAPIキーを取得する2.ブラウザとMongoDBを通信する際のデータセキュリティ3.そのような決定の範囲4.ビジネスに取り掛かろう5.アプリケーションコードを逆アセンブルします6.完成したプロジェクトのデモ1. Mongolabに登録してAPIキーを取得する
ステップ1-登録

登録は簡単で、拘束力のある支払いカードを必要としません。 Mongolabは非常に便利なサービスです。 当社では、Webアプリケーションの開発中にサンドボックスとして使用します。
ステップ2-ユーザーメニューに移動します

画面の右側に、ユーザーメニューへのリンクがあります。 このメニューでは、大切なAPIキーが私たちを待っています。
ステップ3-APIキーを取得する

APIキーを受け取っ
たら 、
Mongolab REST APIを使用できます2.ブラウザとMongoDBを通信する際のデータセキュリティ

写真:トム・クルーズは笑う
あなたに警告したい-記事は本質的に純粋に教育的です。 ブラウザからクラウドデータベースとの通信は致命的なエラーになる可能性があります。 開発者のコンソールを開くだけで、攻撃者がデータベースに簡単にアクセスできることは明らかだと思います。 データベースの読み取り専用ユーザーを使用すると、クラウドMongoDBにあるすべてのデータに重要性とプライバシーがまったくない場合にのみ、この問題が解決します。
3.そのような決定の範囲
このアプローチに基づいて、あなたと私はあなたのコンピューターに保存できるtodoリストアプリケーションを作成し、1つのhtmlとjavascriptを使用してAndroid / iOS / Windows Phone / Windows 8.1用のアプリケーションを書くことができます。
4.ビジネスに取り掛かろう
Todoアプリケーションを書くのにちょうど15分かかり、この記事を書くのに2時間を費やしました(+コードについてコメントします)。 カラースキームは
Googleから取得しました 。これは、LESSの
親切な人によって慎重に取り出されました。 私がやったことは、githubにアップロードしたので、貴重な時間を無駄にすることなくクラウドベースで作業することを評価できます。 リンクは記事の最後にあります。
XMLHttpRequestを介してREST APIと通信します。 Web開発の現代の世界は、jQueryやAngularなどのソリューションに非常に自信を持って焦点を当てています。それらはどこでもどこでもポップされています。 多くの場合、落ち着いてそれらなしで取得できます。
new XMLHttpRequest ()
オブジェクトは、メインの
openメソッドと
sendメソッド(接続を開いてデータを送信する)およびメインの
onreadystatechangeイベントを持つjsオブジェクトに関連付けられた一種のストリームです。 RESTと通信するには、
Content-Typeヘッダーを設定する必要があり
ます:application / json; charset = UTF-8 、このために
setRequestHeaderメソッドを使用し
ます 。
簡単なRESTアプリケーションは次のようになります。
var api = new XMLHttpRequest(); api.onreadystatechange = function () { if (this.readyState != 4 || this.status != 200) return; console.log(this.responseText); };
メソッドをラップできませんか?
var api = new XMLHttpRequest(); api.call = function (method, resource, data, callback) { this.onreadystatechange = function () { if (this.readyState != 4 || this.status != 200) return; return (callback instanceof Function) ? callback(JSON.parse(this.responseText)) : null; }; this.open(method, 'https://api.mongolab.com/api/1/' + resource + '?apiKey=XXX'); this.setRequestHeader('Content-Type', 'application/json;charset=UTF-8'); this.send(data ? JSON.stringify(data) : null); }; api.call('GET', 'databases', null, function (databases) { console.log(databases); });
デモコレクションにタイトル「test」で新しいエントリを作成します
var test = { title: 'test' }; api.call('POST', 'databases/mydb/demo', test, function (result) { test = result;
同期ストリームの問題
api変数は1つのスレッドにすぎないため、次のコードは誤りです。
api.call('POST', 'databases/mydb/demo', test1); api.call('POST', 'databases/mydb/demo', test2);
同期をバイパスするには、最初のPOSTと2番目のスレッドに2つの別個のスレッドが必要です。 毎回呼び出しメソッドを記述しないために、「疑似クラス」
MongoRESTRequestをアセンブルする決定に
至ります。実際には、呼び出しメソッドが用意された新しいXMLHttpRequestオブジェクトを返す関数になります。
var MongoRESTRequest = function () { var api = new XMLHttpRequest(); api.call = function (method, resource, data, callback) { this.onreadystatechange = function () { if (this.readyState != 4 || this.status != 200) return; return (callback instanceof Function) ? callback(JSON.parse(this.responseText)) : null; }; this.open(method, 'https://api.mongolab.com/api/1/' + resource + '?apiKey=XXX'); this.setRequestHeader('Content-Type', 'application/json;charset=UTF-8'); this.send(data ? JSON.stringify(data) : null); }; return api; }; var api1 = new MongoRESTRequest(); var api2 = new MongoRESTRequest(); api1.call('POST', 'databases/mydb/demo', test1); api2.call('POST', 'databases/mydb/demo', test2);
これで、このコードは正しく実行されます。
MongoRESTRequestの変更を続けると、おおよそ以下のアプリケーションのソースコードで説明されるオプションになります。
テンプレートエンジンなしでできることについて少し:
通常、平均的なjQueryファンのコードには次のようなものがあります。
$('#myDiv').html('<div class="red"></div>');
ここで、余分な93.6kb(圧縮された運用jQuery 1.11.2)を接続せずに、実際にどうあるべきかを見てみましょう。
var myDiv = document.getElementById('myDiv'); var newDiv = document.createElement('div');
わかりました、わかりました、もちろん、これは次のように実行できることをすべて知っています。
document.getElementById('myDiv').innerHTML = '<div class="red"></div>';
VanillaでDOMを操作することについてもう少し:
マップを使用してリストを作成します(ReactJS-way):
var myList = document.getElementById('myList'); var items = ['', '', '']; items.map(function (item) { var itemElement = document.createElement('li'); itemElement.appendChild(document.createTextNode(item)); myList.appendChild(itemElement); });
出力には(
jsFiddleで遊ぶための
リンク )があります:
<ul id="myList"> <li></li> <li></li> <li></li> </ul>
このJavaScriptの機能の利点は、オブジェクトを完全に操作できることです。
var myList = document.getElementById('myList'); var items = [{id: 1, name: ''}, {id: 2, name: ''}, {id: 3, name: ''}]; items.map(function (item) { var itemElement = document.createElement('li'); itemElement.appendChild(document.createTextNode(item.name)); itemElement.objectId = item.id;
チェックするJsFiddleリンク5.アプリケーションコードを逆アセンブルします
コードのすべての行にコメントしようとしました。 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> </title> <link rel="stylesheet" type="text/css" href="client/styles/main.css"> <link rel="stylesheet" type="text/css" href="client/styles/font-awesome.css"> <link rel="icon" type="image/png" href="favicon.png"> </head> <body> <div id="fake-header" tabindex="1"></div> <header><i class="fa fa-bars"></i> </header> <div id="extendable"> <label> <input name="task" tabindex="2"><button><i class="fa fa-external-link"></i></button> </label> </div> <div id="container"> <i class="fa fa-circle-o-notch fa-spin fa-5x"></i> </div> <script type="text/javascript" src="client/scripts/duel/public/lib/duel.min.js"></script> <script type="text/javascript"> var header = document.getElementsByTagName('header')[0]; var taskInput = document.getElementsByName('task')[0]; var taskBtn = document.getElementsByTagName('button')[0]; var extendable = document.getElementById('extendable'); extendable.show = function () { this.style.display = 'block'; taskInput.focus(); }; extendable.hide = function () { this.style.display = 'none'; }; header.onclick = function () { if (!this.secondState) { extendable.show(); this.secondState = true; } else { extendable.hide(); this.secondState = false; } }; document.getElementById('fake-header').onfocus = function () { extendable.show(); header.secondState = true; }; taskBtn.onclick = function () { tasks.add({title: taskInput.value}); taskInput.value = ''; }; taskInput.onkeyup = function (event) { if (event.keyCode == 13) { extendable.hide(); header.secondState = false; taskBtn.onclick(); } }; var todoList = { firstRender: true, render: function (items) { var todoContainer = document.getElementById('container'); var listElement = document.createElement('ul'); todoContainer.innerHTML = ''; items.map(function (item) { var itemElement = document.createElement('li'), itemLabel = document.createElement('label'), itemCheck = document.createElement('input'), itemFACheck = document.createElement('i'), itemText = document.createTextNode(item.title); itemCheck.type = 'checkbox'; itemCheck.objectId = item._id.$oid; itemFACheck.classList.add('fa'); itemFACheck.classList.add('fa-square'); itemFACheck.classList.add('fa-check-fixed'); itemLabel.appendChild(itemCheck); itemLabel.appendChild(itemFACheck); itemLabel.appendChild(itemText); itemElement.appendChild(itemLabel); if (todoList.firstRender) { itemElement.classList.add('fadeInLeft'); } listElement.appendChild(itemElement); itemCheck.onclick = function (event) { itemFACheck.classList.remove('fa-check'); itemFACheck.classList.add('fa-check-square'); itemLabel.style.textDecoration = 'line-through'; tasks.remove(this.objectId); this.onclick = function () {}; }; }); todoContainer.appendChild(listElement); if (todoList.firstRender) { todoList.firstRender = false; } } }; var MongoRESTRequest = function (apiConfig) { var api = new XMLHttpRequest(); api.server = apiConfig.server; api.key = apiConfig.apiKey; api.collections = apiConfig.collections; api.error = function () { console.error('database connection error'); }; api.addEventListener('error', api.error, false); api.call = function (method, resource, data, callback) { this.onreadystatechange = function () { if (this.readyState != 4 || this.status != 200) return; return (callback instanceof Function) ? callback(JSON.parse(this.responseText)) : null; }; this.open(method, api.server + this.collections + '/' + resource + '?apiKey=' + this.key + '&bypass=' + (new Date()).getTime().toString()); this.setRequestHeader('Content-Type', 'application/json;charset=UTF-8'); this.send(data ? JSON.stringify(data) : null); }; api.get = function () { var bIsFunction = arguments[1] instanceof Function, resource = arguments[0], data = bIsFunction ? null : arguments[1], callback = bIsFunction ? arguments[1] : arguments[2]; return this.call('GET', resource, data, callback); }; api.post = function () { var bIsFunction = arguments[1] instanceof Function, resource = arguments[0], data = bIsFunction ? null : arguments[1], callback = bIsFunction ? arguments[1] : arguments[2]; return this.call('POST', resource, data, callback); }; api.put = function () { var bIsFunction = arguments[1] instanceof Function, resource = arguments[0], data = bIsFunction ? null : arguments[1], callback = bIsFunction ? arguments[1] : arguments[2]; return this.call('PUT', resource, data, callback); }; api.delete = function () { var bIsFunction = arguments[1] instanceof Function, resource = arguments[0], data = bIsFunction ? null : arguments[1], callback = bIsFunction ? arguments[1] : arguments[2]; return this.call('DELETE', resource, data, callback); }; return api; }; var config = { server: 'https://api.mongolab.com/api/1', apiKey: '_API', collections: '/databases/_/collections' }; var tasks = []; var api = new MongoRESTRequest(config); var channel = duel.channel('task_tracker'); tasks.sync = function () { if (window.isMaster()) { api.get('tasks', function (result) { tasks.splice(0); channel.broadcast('tasks.sync', result); for (var i = result.length - 1; i >= 0; i--) { tasks.push(result[i]); } todoList.render(tasks); }); } else { tasks.splice(0); var result = arguments[0]; for (var i = result.length - 1; i >= 0; i--) { tasks.push(result[i]); } todoList.render(tasks); } }; tasks.rename = function (id, title) { for (var i = tasks.length - 1; i >= 0; i--) { if (tasks[i]._id.$oid === id) { tasks[i].title = title; todoList.render(tasks); if (window.isMaster()) { channel.broadcast('tasks.rename', id, title); var api = new MongoRESTRequest(config); api.put('tasks/' + id, {title: title}); } break; } } }; tasks.add = function (task) { if (window.isMaster()) { var apiThread1 = new MongoRESTRequest(config); var apiThread2 = new MongoRESTRequest(config); apiThread1.post('tasks', task, function (result) { tasks.push(result); channel.broadcast('tasks.add', result); todoList.render(tasks); }); apiThread2.post('logs', { when: new Date(), type: 'created' }); } else { tasks.push(arguments[0]); todoList.render(tasks); } }; tasks.remove = function (id) { for (var i = tasks.length - 1; i >= 0; i--) { if (tasks[i]._id.$oid === id) { if (window.isMaster()) { var apiThread1 = new MongoRESTRequest(config); var apiThread2 = new MongoRESTRequest(config); apiThread1.delete('tasks/' + id); apiThread2.post('logs', { when: new Date(), type: 'done' }); } break; } } }; setInterval(function () { if (window.isMaster()) { tasks.sync(); } }, 30000); channel.on('tasks.add', tasks.add); channel.on('tasks.sync', tasks.sync); channel.on('tasks.rename', tasks.rename); tasks.sync(); </script> </body> </html>
1つの変数のみを変更してプロジェクト全体の配色を変更する方法main.lessファイルのメインコードには、次のコードが含まれています(完全に記載されているわけではありません。主なことは本質を理解することです)。
@import 'palette'; @themeRed: 'red'; @themePink: 'pink'; @themePurple: 'purple'; @themeDeepPurple: 'deep-purple'; @themeIndigo: 'indigo'; @themeBlue: 'blue'; @themeLightBlue: 'light-blue'; @themeCyan: 'cyan'; @themeTeal: 'teal'; @themeGreen: 'green'; @themeLightGreen: 'light-green'; @themeLime: 'lime'; @themeYellow: 'yellow'; @themeAmber: 'amber'; @themeOrange: 'orange'; @themeDeepOrange: 'deep-orange'; @themeBrown: 'brown'; @themeGrey: 'grey'; @themeBlueGrey: 'blue-grey'; @theme: @themeBlueGrey; @r50: 'md-@{theme}-50'; @r100: 'md-@{theme}-100'; @r200: 'md-@{theme}-200'; @r300: 'md-@{theme}-300'; @r400: 'md-@{theme}-400'; @r500: 'md-@{theme}-500'; @r600: 'md-@{theme}-600'; @r700: 'md-@{theme}-700'; @r800: 'md-@{theme}-800'; @r900: 'md-@{theme}-900'; @color50: @@r50; @color100: @@r100; @color200: @@r200; @color300: @@r300; @color400: @@r400; @color500: @@r500; @color600: @@r600; @color700: @@r700; @color800: @@r800; @color900: @@r900; @font-face { font-family: 'Roboto Medium'; src: url('../fonts/Roboto-Regular.ttf') format('truetype'); } body { font-family: 'Roboto Medium', Roboto, sans-serif; font-size: 24px; background-color: @color900; color: @color50; margin: 0; padding: 0; }
@theme
上記のいずれかに変更し、同時にアプリケーション全体のテーマを変更します。私は以前、LESSでこのようなトリックを複数回行っていました。たとえば、次のようにできます(これは、LESSを見たことがない人にとってはボーナスと見なすことができます)。 @baseColor: #000000; @textColor: contrast(@baseColor); @someLightenColor: lighten(@baseColor, 1%);
6.
->-> GitHub