- 投稿日:2020-06-25T22:36:59+09:00
Docker TypeScript で React 環境を構築
自分用のmemoのつもりで
Dockerで環境構築
Dockerはinstall済み■Dockerfile作成
空のPJディレクトリ内にDokerfile作成し以下を記載FROM node:13.5.0-alpine3.11 WORKDIR /usr/src/appFROMは新しいイメージの元となるイメージの読み込み
alpineというのでめちゃくちゃファイルサイズを小さくできるらしい■docker-compose.yml作成
※ymlは「yaml ain't markup language」の略で構造化データの表現方法
※簡単に言えば設定ファイルversion: '3' services: sample: build: context: . tty: true environment: - NODE_ENV=production volumes: - ./:/usr/src/app command: sh -c "cd tips && yarn start" ports: - "3000:3000"■イメージをビルド
docker-compose build■React と create-react-app をインストール + アプリ作成
docker-compose run --rm sample sh -c 'npx create-react-app sample_app --template typescript'出来上がるディレクトリ 構成
- Dockerfile - docker-compose.yml - アプリ名 |- node_module |- public |- src■起動
docker-compose up -d※ローカルでyarn startする時よりも時間がかかる感覚はある
Localhost:3000でApp.jsの中身が表示される
■停止
docker-compose stop
- 投稿日:2020-06-25T22:25:40+09:00
初めてのJavaScript-備忘録03
初めてのJavaScript-備忘録03
今回はMapとSetオブジェクトについて書こうと思う。
オブジェクトの簡単な説明なので、手短に…Mapについて
Mapは連想配列を扱うためのオブジェクトで連想配列を容易に扱う事ができる。
Mapは何かと使えるヤツなので是非抑えておきたいね。Mapでよく使うメンバーは以下の通り
メンバー 概要 size サイズを取得 set キーと値をセットでセット get キー指定でその対応する値を取得 delete キー指定でそのキーと値を削除 clear 全ての値とキーを削除 使い方
let hoge = new Map(); // オブジェクトの生成 //名前、年齢、性別をセット hoge.set('name',"hoge"); hoge.set('age',18); hoge.set('sex',"man"); //セットした年齢を変数に代入 let age = hoge.get('age'); //hogeのキーと値を順番にログに出力 for (let [key,val] of hoge){ console.log(key); console.log(value); }最後にsetについて書こうかと
Setについて
Setも配列で配列の中の値は重複しない、性質がある。
使い道はぱっと思いつかないけど、
まぁ便利っちゃ便利そう?
あと、index指定やkey指定で要素にアクセスできない事も注意。
メンバー 概要 add 要素を追加 delete 指定した値を削除 clear 全て削除 values 全ての値を取得 size サイズ 使い方
let s = new Set(); // 要素の追加 s.add(1); s.add(2); s.add(3); s.add(4); let size = s.size; // サイズは4 s.delete(4); // 要素の4が削除されsの中身は(1,2,3)となる。 let size = s.size; // サイズは3 for(let val of s){ console.log(val); //1,2,3と順番にログに出力する } s.clear();//要素を全て削除 size = s.size//サイズは0今回はここまで、おわり
- 投稿日:2020-06-25T17:48:17+09:00
【javascript】DSL構築
書籍のアウトプットとして
DLSとは
言語の機能や構文をうまく使って、その言語に最初からサポートされていたかのように特定のタスクを解決することです。
以降では文字列を処理するためのヘルパー関数を作成していきます。ヘルパー関数を作成する
前回のテンプレートの記事ではこのようなコードを持つ関数を定義しました。
function stringWS() { let stringParts = arguments[0]; let values = [].slice.call(arguments, 1) }
let values = [].slice.call(arguments, 1)
で文字列部分と補間部分を取得しています。
しかし上記コードでは少しわかりにくいです。
さしあたり、この部分をレイヤ化して、ロジックの他の部分から隠してしまいます。
そのための関数を作成し、取得した値をタグ関数に渡すようにすれば良いでしょう。function createTag(func) { return function() { const strs = arguments[0]; const vals = [].slice.call(arguments, 1); return func(strs, vals); } }後は、タグ関数を次のように作成します。
javascript
const stringW = createTag(function(strs, vals) {
/* strsとvalsを使った処理 */
})
また渡された文字列strsと補間値valsをつなぎ合わせるためにreduceを使用する必要があります。これも抽象化してしまいます。function interlace(strs, vals) { vals = vals.slice(0); //配列のコピーを作成 return strs.reduce(function(all, str) { return all + String(vals.shift()) + str; }); }
vals = vals.slice(0);
で配列をコピーしています。
shiftのように配列をその場で変更してしまうときにはこのようにします。
あとはcreateTagとinterlaceを組み合わせれば標準的な実装でテンプレートリテラルを処理できます。const processNormally = createTag(interlace); const text = 'Click Me'; const link = processNormally `<a>${text}</a>`; // <a>Click Me</a>
- 投稿日:2020-06-25T17:40:14+09:00
アリババクラウドが提供するサービスを利用したNode.jsアプリケーションの展開
このチュートリアルでは、Alibaba Cloudが提供するサービスを利用して、本番環境に対応したNode.jsアプリケーションのセットアップを取り上げます。
本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。
序章
Node.jsは、高性能で高速なサーバーサイドアプリケーションを構築するために使用されるJavascriptのランタイム環境です。Node.js サーバーサイドアプリケーションは、間違った方法でデプロイされた場合、本当に遅くなり、トラフィックの増加をサポートできなくなる可能性があります。
このチュートリアルでは、アリババクラウドサービスが提供する素晴らしいサービスを使用して、本番環境に対応したNode.jsアプリケーションを設定する方法を説明します。Node.js Blogというアプリケーションをデプロイしますが、そのために以下のAlibaba Cloudサービスを使用します。
- Elastic Compute Service:CPU、メモリ、OS、帯域、ディスクなどの基本的なコンピューティングコンポーネントを含む仮想コンピューティング環境。このサービスを使用してLinuxインスタンスを起動し、そこにNode.jsアプリケーションをデプロイします。
- ApsaraDB for MongoDB: 安全で信頼性が高く、弾力的にスケーラブルなクラウドデータベースサービスです。このサービスを使ってNode.jsアプリケーション用のMongoDBデータベースを起動します。
前提条件
このチュートリアルを始める前に、以下のものが必要です。
- デプロイする Node.js アプリケーション。以下の Github リポジトリにあります。
- Ubuntu 16.04 64ビットECSインスタンス。このチュートリアルを使って作成することができます。
- ApsaraDB for MongoDB のインスタンス。このチュートリアルを使って作成することができます。
ECS インスタンスへ接続します。SSH を使って接続するには、このチュートリアルに従ってください。
注: ECS インスタンスと ApsaraDB for MongoDB インスタンスがまったく同じゾーンに作成されていることを確認してください。そうすることでアプリケーションのパフォーマンスが向上し、接続の問題や制限を回避することができます。
ステップ 1 - すべてのサーバーの依存関係をインストールする
このステップでは、node js アプリケーションを実行するために必要なすべての依存関係をインストールします。手順に従うには、ECS インスタンスにログインしていることを確認してください。
Git のインストール
GitHubからアプリケーションのコードを取得できるようにするには、gitを使ってリポジトリをクローンする必要があります。以下のコマンドでインスタンスにgitをインストールしてみましょう。
sudo apt-get install gitインストールを確認するには、以下のコマンドを実行してインストールされているgitのバージョンを確認します:
git --version.
私の場合、以下のような出力が出てきました:git version 2.7.4.
Node.jsのインストール
node.jsアプリケーションを実行するためには、このアプリケーションを実行するために必要な特定のnode.jsのバージョンをインストールする必要があります。私たちのnode.jsブログの場合は、Nodeのバージョン8以上が必要です。インストールするには、以下のコマンドを実行します。
curl -sL https://deb.nodesource.com/setup_8.x -o nodesource_setup.shこのコマンドは、
nodeource_setup.sh
というファイルをダウンロードします。これはnode jsをインスタンスにインストールするためのシェルスクリプトです。このシェルスクリプトを以下のコマンドで実行します。sudo bash nodesource_setup.shそして最後に、インストールを完了させるために、以下を実行します。
sudo apt-get install -y nodejsnode jsのインストールを確認するには、
node -v
を実行して、インストールされているnodeのバージョンを確認します。私の場合は、最新のバージョン8のサブバージョン(v8.13.0)
がインストールされています。Nginxのインストール
Nginx は世界で最も人気のあるウェブサーバーの一つであり、いくつかの大規模なサイトで使用されています。アプリケーションをデプロイするために、プロキシサーバとしてnginxを使用してアプリケーションを提供します。これについては後ほど詳しく説明します。nginxをインストールするには、以下を実行してください。
sudo apt-get install nginxインストールをテストするには、
nginx -v
を実行して、インストールされているバージョンを確認します。私の場合、以下のような出力が出てきました:nginx version: nginx/1.10.3 (Ubuntu)
PM2プロセスマネージャのインストール
Node.jsアプリケーションは非常に脆いため、運用環境では強力なプロセスマネージャを使用してnode.jsアプリケーションを監視し、プロセスが常に実行されていることを確認する必要があります。このケースで使用するプロセスマネージャは
pm2
と呼ばれ、node.js パッケージとして提供されています。PM2はnode jsアプリケーションのクラッシュを監視するだけでなく、node jsアプリケーションを自動的にスケーリングし、インスタンス上のすべてのCPUを最適化します。PM2をインストールするには、次のように実行します。npm i -g pm2インストールを確認するには、
pm2 -v
を実行してください。私の場合、以下のような出力が表示されました。3.2.2.
ステップ 2 - アプリケーションコードの取得
このステップでは、インスタンス上で実行できるようにアプリケーションリポジトリをクローンします。
アプリケーションコードのクローン
アプリケーションのコードをクローンするには、以下のコマンドを実行します。
cd /home git clone https://github.com/bahdcasts/node-js-blog.gitプロジェクトの依存関係のインストール
プロジェクトの依存関係をインストールするには、以下のコマンドを実行します。
cd /home/node-js-blog npm install環境変数の設定
ほとんどのnode jsアプリケーションでは、サーバの環境変数を設定する必要があります。このプロジェクトでは
dot-env
というパッケージを使用しており、このパッケージを使用することで、すべての環境変数を.env
というファイルに配置することができます。このファイルを作成し、環境変数を追加します。以下のコマンドを実行します。cd /home/node-js-blog nano .envターミナルコードエディタ
nano
を使って.env
という新しいファイルを開きます。ファイルの中に以下の内容を入れます。PORT=3000 DB_URI= CLOUDINARY_API_KEY=xxxx CLOUDINARY_API_SECRET=xxxx CLOUDINARY_NAME=xxx EXPRESS_SESSION_KEY=d29z4944SxETKPNWqKAkCNUjrZ4Aこれらはこのプロジェクトが動作するために必要な環境変数です。
PORT
はNode.jsアプリケーションが動作する特定のポートです。EXPRESS_SESSION_KEY
は、ユーザーセッションを暗号化するために使用されるランダムな文字列です。また、このブログの画像はcloudinaryにアップロードされており、そのためにcloudinaryの環境変数が必要になります。このチュートリアルの焦点ではないので省略しても構いません。これは、独自のNode jsプロジェクトをデプロイする際に、独自のカスタム環境変数を設定する方法を紹介するだけです。
最後の環境変数は
DB_URI
で、これが ApsaraDB for MongoDB インスタンスに接続するための環境変数になります。これは後のステップで追加します。注:
nano editor
を終了するには、CTRL + X
を押してからY
を押してください。ステップ3 - MongoDBインスタンスへの接続
このステップでは、先ほど作成した
ApsaraDB for MongoDB
インスタンスにアプリケーションを接続します。ApsaraDB for MongoDB のサーバー IP をホワイトリスト化する
デフォルトでは、MongoDB インスタンスはどのアドレスからの接続も許可しません。コンソールを使って、ECS インスタンスの IP アドレスをホワイトリストに登録して、アプリケーションが正常に接続できるようにします。
まず、コンソールにアクセスして MongoDB インスタンスを開き、「データセキュリティ」タブにアクセスします。ここで、
Manually modify
ボタンをクリックして新しい IP アドレスを追加します。変更を保存します。このアクションは、アプリケーションがこの MongoDB インスタンスに接続するための許可を与えます。
DB_URI接続文字列の設定
Database Connection
タブから、データベースへの接続文字列を取得します。環境変数 DB_URI の設定
接続文字列をコピーし、環境変数
DB_URI
の値を更新します。nano .env
を使うと、.env
ファイルは以下のようになります。PORT=3000 DB_URI="mongodb://root:passWORD1234@dds-gs50fb23291731d41.mongodb.singapore.rds.aliyuncs.com:3717,dds-gs50fb23291731d42.mongodb.singapore.rds.aliyuncs.com:3717/admin?replicaSet=mgset-300215248" CLOUDINARY_API_KEY=132255634713478 CLOUDINARY_API_SECRET=aeBNzdZFcju9ZEAiL7KsEMbBlFI CLOUDINARY_NAME=bahdcoder EXPRESS_SESSION_KEY=d29z4944SxETKPNWqKAkCNUjrZ4P注: 文字列中の***は、インスタンスの実際のルートパスワードに置き換えることを忘れないでください。
ステップ4 - Nginxの設定をする
このステップでは、nginx ウェブサーバが node js アプリケーションを利用できるように、nginx の設定を行います。
これを行うために、デフォルトのnginx設定ファイルを編集します。以下のコマンドを実行します。
# Empty the current configuration file echo > /etc/nginx/sites-available/default # Open the file using nano nano /etc/nginx/sites-available/defaultこのファイルの内容を以下のように置き換えてください。
server { listen 80; server_name 47.88.230.255; location / { proxy_pass http://localhost:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_cache_bypass $http_upgrade; } }注意すべき2つの重要な設定は、
server_name
とproxy_pass
です。server_name
は理想的にはアプリケーションのドメイン名であるべきで、ユーザーが特定のサイトにアクセスしようとした場合に基礎となる設定を使用するよう nginx に指示します。この場合、このアプリケーションのためにドメイン名を設定しないので、単に ECS のインスタンスの IP アドレスを使用します。第二に、
proxy_pass
の設定は nginx に、http://localhost:3000
上で実行されているプロセスに、server_name
のために入ってくるすべてのトラフィックをリダイレクトするように指示します。つまり、ユーザーが47.88.230.255
にアクセスしようとすると、nginxはユーザーのリクエストをポート3000
で動作しているnode jsアプリケーションにリダイレクトします。ここでは3000番ポートを使用していますが、これはnode jsアプリケーションを起動するポートであるためです。注:
47.88.230.255
をECSインスタンスの実際のIPアドレスに置き換えることを忘れないでください。最後に、設定変更を保存するためにnginxを再起動する必要があります。
以下のコマンドを実行してください。
sudo systemctl restart nginxステップ5 - アプリケーションの起動
最後に、プロセスマネージャPM2を使用してnode jsアプリケーションを起動する必要があります。 これを行うには、以下のコマンドを実行します。
cd /home/node-js-blog pm2 start index.js --name "blog"このコマンドは、
index.js
ファイルをルートとして使用してアプリケーションを起動し、このプロセスにblog
という名前を与えます。このコマンドは以下のような出力が出てきます。アプリケーションログのトラッキング
アプリケーションをデバッグして監視するには、以下のコマンドを使用して、アプリケーションからログに記録されたすべてのメッセージをリアルタイムで表示することができます。
pm2 logs blogこれは
blog
というプロセスのログをプリントアウトします。出力は以下のようになります。結論
このチュートリアルでは、Alibaba Cloud Elastic Compute Service (ECS) と ApsaraDB for MongoDB を使用して本番アプリケーションをデプロイする方法を学びました。また、PM2と呼ばれる高度なプロセスマネージャを使用してNode.jsアプリケーションを管理し、自動的にスケーリングしました。
アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ
- 投稿日:2020-06-25T17:13:49+09:00
古の書き方を卒業してちょいと今どきよりなJSを書こう
はじめに
基本的にはワタクシ様はPHPを書いて日々過ごしているのですが、たまにjsを書いたりすることもあります。
さて業界では未だにjs = jQuery
みたいな方も少なくなく、jQueryでjsを覚えてそのままAngularとかに手を出さずCoffeeScriptとか通らなかった方はなんでか知りませんが、古文のようなES5以前のjsを書くことが多い印象があります。
ES6(ES2015)が世に爆誕してから5年も経つと便利さ、書きやすさが桁違いにUPします。
ES5があまりにも書きづらくてCoffeeScriptやTypeScriptやDartや Reactに関係ない方のJSX などといったaltJSと呼ばれるものが2010年頃からポコポコ生まれていったわけですが、それらの影響を受け、格段に書きやすくなった今どきなjsの書き方をふたつばかり紹介していきたいと思います。varからの卒業
// ES5以前 var hoge = ''; // よくない // ES6以後 const hoge = ''; // 中身が変わらない予定ならこっち(そのうち書き換えが効かなくなると思う) let hoge = ''; // 書き換わるならこっち
var
で変数宣言をする時代は終わりです。今はconst
とlet
です。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/let
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/constvarがダメなわけではないのですが、varは関数全体だったりとかグローバル全体に影響を及ぼしてしまい大変危険だったわけです。
「お前は今まで食べたパンの数を数えてるのか」ばりに宣言した変数名を全部記憶してるならvarでもいいのですが、そんな自身がないならconst
かlet
を使いましょう。
使い分けとしてはずっと中身が変わらないならconst
、変わるならlet
です。functionからの卒業
// ES5以前 var hoge = function(fuga){return '';}; var obj = { onClick: function(e){ return e.target; } }; // ES6以後 const hoge = fuga => {return '';}; const org = { // 旧来のメソッド定義と同じ onClick(e){ return e.target; }, // こちらはthisがうごかなくなる onClick: e => { return e.target; }, };ES5の頃はfunction宣言地獄だったわけですが、ES6以降はそんな心配はありません。
というか、functionというキーワードを書く機会もあんまりなくなると思います。
普通の無名関数ならかっこいいアローファンクションが使えますし、アローファンクション使うと「呼び出し元によってthisが変わっちまう問題」から開放されます。違う問題には直面しますが……
オブジェクトのメンバーに関数を定義する「メソッド定義」はちょっとJavaっぽい書き方ができるようになりました。
もちろんアローファンクション使ってメソッド定義もできるのですが、thisの意味合いが変わってしまうので、全く同じようには動作しないので注意が必要です。参考記事
- 投稿日:2020-06-25T15:47:26+09:00
ブラウザ上でzipファイルを圧縮展開できるWebアプリを作ってみた
こんにちわ。
創作意欲が湧いた勢いで、JavaScriptからzip形式のアーカイブを操作できる "JSZip" というライブラリを使用して、ブラウザ上でzipを圧縮展開できる簡単なWebアプリを作ってみました。
作ったもの
めちゃくちゃシンプルです。
Babelは通してないので、動作確認済のChrome以外は新生EdgeやFirefoxでなら多分動くと思います。
仕組み
凝ったことはしていないので、とても簡単です。
- Input要素でファイルを読込
- ファイルをワーカースレッドへ転送
- ワーカースレッドでJSZipをロードし圧縮展開
- 処理結果をメインスレッドへ転送
- a要素に処理結果のBlobURLを充ててダウンロード
全ての処理がローカルで完結しています。
ヘビータスクの圧縮展開はWebWorkerで処理しており、かつファイルはTypedArray化して参照渡し(Transferable Object)しているので、処理中の描画レスポンスへの影響はほとんどありません。
- 投稿日:2020-06-25T15:40:28+09:00
Firebase Cloud Messagingで「FirebaseError: Messaging: This browser doesn't support the API's required to use the firebase SDK. (messaging/unsupported-browser).」
Firebase Cloud Messaging(以下FCM)で「FirebaseError: Messaging: This browser doesn't support the API's required to use the firebase SDK. (messaging/unsupported-browser).」が出た場合
Chromeの最新版で FCM をつかっていて遭遇した。
原因
原因はFCM使ったアプリにhttpでアクセスしていたということ
ngrokをつかってデバッグしているときに、うっかり http でアクセスしてしまっていた。
エラーメッセージを読むと「ブラウザがAPIに対応していない」ようにみえてしまうので注意。対策
FCMのクライアントとなるWebアプリはhttpsでアクセスしましょう。または開発・デバッグ時はhttp://localhostも可。
- 投稿日:2020-06-25T15:33:33+09:00
TestCafeを使ってみる①
TestCafeとは
アメリカのシステム開発会社が開発しているE2Eテストツール(2016年に登場した比較的新しいツール)
公式ドキュメント特徴
- ES2017(async/await)を含む最新のJavascript機能をサポート、TypeScriptにも対応
- サポートブラウザが豊富
- Chrome、firefoxなどの主要ブラウザは網羅されており、IE11もサポートされています。
- スマホやIEはテスト実行時に発行されるURLおよびQRコード(オプションで設定)を読み込むことで実機テストを行うことができる。
- 非WebDriver依存
- 一昔前のE2Eテストツールは、各種ブラウザベンダ間の差異を吸収し抽象化して操作するためにWebDriverを使うのが常識でしたが、現在はブラウザ側がサポートするようになり、テストツール側が意識しなくてよくなりました。TestCafeもこれを前提に設計されています。
- BDD(ビヘイベア駆動開発)に則ったテストコード
- UIテスト手順をメソッドチェーン形式で記述。これにより、ユーザー操作や外部仕様をまるで自然言語のようにコードで表現できるので、テストコードがそのままテスト仕様書となります。
- BrowserStackとの連携をサポート
- E2Eテストの実行環境は開発マシンとは別に用意しておくことが望ましい。そうすることで開発(実装)と並行してテストを実行しやすくなります。
- 参考:BrowserStackを使ってブラウザテストに取り組んでいる知見
- 構築が簡単
- Seleniumではwebdriverのクライアントとテストを実行する各ブラウザのwebdriverのインストールが必要ですが、TestCafeはnpmでインストールするだけでOK。設定ファイルも必要なくテスト環境が整います。
セットアップ
npmもしくはyarnでtestcafeをインストール
$ npm install --save-dev testcafe$ yarn add -D testcafeテストコードを書く
test.js
など任意のファイルを用意しコードを書いていきます。
まずはtestcafeをインポートtest.jsimport {Selector} from 'testcafe’;次に
fixture
メソッドでフィクスチャを宣言(定義)
fixtureはカテゴリのようなもので、テストケースをここで分類していきます。test.jsimport {Selector} from 'testcafe’; fixture('Getting started');ちなみにタグ付きテンプレートリテラル構文でも書くことができます。
fixture`FormTest Start`;次にfixtureメソッドの戻り値から
page
メソッドを実行します。
pageはフィクスチャの開始ページを宣言するものです。こちらもテンプレートリテラルで書くことができます。test.jsimport {Selector} from 'testcafe’; fixture('FormTest Start'); .page('https://example.com'); // ↓テンプレートリテラル .page`https://example.com`;テストケースを書く
test
メソッドでケース名とテスト内容(UI操作手順)を記述していきます。
第一引数にケース名を渡し、これが実行ログに表示されるので分かりやすい名前が良いです。
第二引数も記述することができ、テスト内容を関数として設定します。
関数は戻り値がPromise型となっているのでasync/awaitを使います。test.jsimport {Selector} from 'testcafe’; fixture('FormTest Start'); .page('https://example.com'); test('必須項目を入力し送信', async (t: TestController) => { const dataTarget = await Selector('[data-target-input]'); const name = dataTarget.withAttribute('name', 'name'); const mail = dataTarget.withAttribute('name', 'email'); const phone = dataTarget.withAttribute('name', 'phone'); const submitBtn = await Selector("button"); await t .typeText(name, 'テスト名前') .typeText(mail, 'test_sample@gmail.com') .typeText(phone, '0312345678') .click(submitBtn.withExactText('送信')) });第二引数はなくてもOKです。その場合はこんな感じ
test('必須項目を入力し送信', async t => { })セレクタについて
エレメントに関する情報を取得するには
Selector
を使用します。
ページ要素を識別し、それらに対してアクション(クリックやドラッグなど)を実行するか、アサーションでその状態を確認することができます。
今回はwithAttribute
メソッドを使い、指定したname属性と値が一致する要素を取得しています。
セレクタの詳細は次回の投稿で紹介します。画面操作について
今回の例として
click
とtypeText
を使用しています。
click
:クリックオプション
typeText
:キー(テキスト)入力オプション
その他メソッドについても次回以降の記事で紹介します。テスト実行
package.jsonのscriptsにtestコマンドを追記し、コマンドラインから実行できるようにします。
package.json"scripts": { "formTest": "testcafe chrome e2e/test.js --page-load-timeout 5000 --speed 0.8", }コマンドに実行環境(ブラウザ)と実行ファイルのパスを指定します。※この2つは必須
今回はchromeを設定。
インストールされている全てのブラウザでテストをする場合はall
を指定します。package.json"scripts": { "formTest": "testcafe all e2e/test.js --page-load-timeout 5000 --speed 0.8", }オプション
--page-load-timeout 5000
ページロード時のタイムアウト時間を指定できます。デフォルトは(3000ms)
--speed 1
テスト実行速度を指定できます。デフォルトは最速(1)で実行されるので値を変更して遅くすることもできます。1が最速、0.01が最も遅い。値を設定する際は1~0.01の間でなくてはいけません。
:headless
chrome、firefoxに対してこのパラメーターをつけるとUIシェルを表示せずにヘッドレスモードでテストが実行できます。$ npm run formTestコマンドを実行すると指定したブラウザが立ち上がりテストが実行されます。
リモートブラウザ上でテスト実行
ホストマシンがMacやLinuxの場合にIE11でテストを実行したい場合は、ブラウザに
remote
と指定することでリモートアクセス用のURLを発行できます。package.json"scripts": { "formTest": "testcafe remote e2e/test.js", }同一のネットワーク上にいるマシンからであればOSやブラウザ問わずURLにアクセスすることでテストを行えます。
スマホ端末からアクセスする場合は、--qr-code
オプションを指定することでQRコードが発行され、読み込むことでスマホからもテストができます。package.json"scripts": { "formTest": "testcafe remote e2e/test.js --qr-code", }E2EテストツールというとSelenium、Puppeteerがよく聞きますが、他にないかなーと調べてTestCafeを知りました。(テストカフェって名前も可愛くて好きです)
まだまだ勉強中で基本的な使い方しか出来ていませんが、所感としては直感的に分かりやすいコードになるのでTestCafe良い感じです。
今回は備忘録としてセットアップをメインにまとめました。
オプションやセレクタ、メソッドはたくさんの種類があるので、よく使いそうなものを中心に今後の記事でまとめ、スクリーンショットを撮る方法についても書いていきたいと思います。
- 投稿日:2020-06-25T15:27:30+09:00
[1行で完了]最速でReact環境構築
※自分のアウトプット用(メモ)です
結論だけ知りたい人用
1行のコマンドで
React
の環境構築を完了する。
[条件]
1.Node.js 8.10以上
2.npm 5.6以上$ npx create-react-app プロジェクト名
順番通り説明していきます。
必要なパッケージのインストール方法
1.
brew
をインストールMacのパッケージ管理システムであるbrewをインストール。
brew公式サイト$ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"2.
nodebrew
をbrew
でインストールnodeのバージョン管理ができるnodebrewをインストール。
(brewでインストールするので先にbrewをインストールした)。$ brew install nodebrew3.
Node.js
をnodebrew
を使ってインストールNode.jsはnodebrewで管理した方が使いやすいと思います。他のバージョンに移るの簡単ですし、普通にインストールすると削除するのに手間なんですよね...
ほとんどの場合、安定版をインストールしておけば問題ないと思います。// リモートにあるインストールできるNode.jsの一覧表示 $ nodebrew ls-remote // 安定版をローカル(自分のMac)にインストール $ nodebrew install stable // ローカルにインストールされている`Node.js`のバージョンを一覧表示 $ nodebrew ls // インストールしたバージョンの`Node.js`を選択する $ nodebrew use 使用したいバージョン番号4.
Node.js
のパスを通す// "~/.bash_profile"にパスを追記する(bashの場合) $ echo 'export PATH=$HOME/.nodebrew/current/bin:$PATH' >> ~/.bash_profile // ターミナルを再起動して反映 // または以下を実行して反映 $ source ~/.bash_profile5.
node
とnpm
が使用可能か確認Node.jsをインストールした時点でnpm(node package manager)もインストールされているので、うまくいっていればこの時点で使用可能になっているはずです。
// Node.js $ node -v // npm $ npm -vこれで準備は整いました。
create-react-app
でReact
の環境構築任意の場所で以下のコマンドを叩いてください。
npx
コマンドを簡潔に説明すると、パッケージをローカルにインストールすることなく実行できるコマンドです。
環境を汚染しないので手軽に実行して見たいときに便利です。
詳細はこちら。$npx create-react-app プロジェクト名
これでカレントディレクトリにreactのプロジェクトが作成されました。
以下のコマンドでブラウザ表示します。$ npm start
お疲れ様でした。
- 投稿日:2020-06-25T15:20:05+09:00
vis.jsを利用し相関図を作成してみた
初めに
相関図を作成する機会があり簡単に描画できるライブラリはないかと探した所、
vis.js の vis-network が良さそうでした。
サンプルを参考に自分の好きな飲み屋 徳田酒店 の店舗と利用頻度を描画してみます。ソースコード
tokuda.html<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>test</title> <script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script> <style type="text/css"> #mynetwork { width: 600px; height: 400px; border: 1px solid lightgray; } </style> </head> <body> <div id="mynetwork"></div> <script> // create an array with nodes const nodes = new vis.DataSet([ {id:1 , label: '徳田酒店', shape: 'box', color:'#C2FABC'}, {id:'1-1', label:'京橋本店', size:25}, {id:'1-2', label:'片町店', size:25}, {id:'1-3', label:'プロムナード店', size:10}, {id:'1-4', label:'西梅田店', size:15}, {id:'1-5', label:'御肉', size:20}, {id:'1-6', label:'第3ビルB1店', size:30}, {id:'1-7', label:'第3ビルB2店', size:20}, {id:'1-8', label:'第4ビルB2店', size:5}, {id:'1-9', label:'瓦町店', size:15}, {id:'1-10', label:'ルクア大阪店', size:5}, {id:'1-11', label:'京阪高架下店', size:5}, {id:'1-12', label:'ホワイティうめだ店', size:5}, ]); // create an array with edges var edges = new vis.DataSet([ {from: 1, to: '1-1' }, {from: 1, to: '1-2' }, {from: 1, to: '1-3' }, {from: 1, to: '1-4' }, {from: 1, to: '1-5' }, {from: 1, to: '1-6' }, {from: 1, to: '1-7' }, {from: 1, to: '1-8' }, {from: 1, to: '1-9' }, {from: 1, to: '1-10' }, {from: 1, to: '1-11' }, {from: 1, to: '1-12' }, ]); // create a network var container = document.getElementById('mynetwork'); // provide the data in the vis format var data = { nodes: nodes, edges: edges }; var options = { nodes: { shape: 'dot', } }; // initialize your network! var network = new vis.Network(container, data, options); </script> </body> </html>表示結果
最後に
今回は簡単なものを表現してみましたが大量のノード表示や画像に変更するなども可能です。
どのような表現ができるかは サンプル をご確認下さい。
- 投稿日:2020-06-25T15:13:13+09:00
自分を励ますアプリ
自分を励ますアプリ
ひたすらポジティブな言葉をかけてくれる励ましアプリ。
HTML5、CSS3、Javascriptにて製作。HTML
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>励ましてくれるアプリ</title> <link rel="stylesheet" href="./style.css"> <link href="https://fonts.googleapis.com/css?family=M+PLUS+Rounded+1c&display=swap" rel="stylesheet"> <meta name="robots" content="noindex,nofollow,noarchive"> </head> <body> <div id = "container"> <header> <h1>励ましアプリ</h1> </header> <div id = "message"><strong>悩みや不安を書き出してみよう!</strong></div> <div id = "commentBox"> <form action="" method="post"> <input id="comment" type="text" value=""> </form> </div> <div id ="button"><strong>悩みを聞いてもらおう</strong></div> <div id ="result"></div> <div id ="buttonClear"><strong>参考になったよありがとう</strong></div> </div> <script src="encouragement.js"></script> </body> </html>Javascript
//メッセージを用意 let encouragement = [ "十分睡眠を取ろう", "海を見に行こう", "陽の光を浴びよう", "明日は今よりいい日", "自分を信じろ", "努力は裏切らない", "諦めちゃダメだよ", "人事を尽くして天命を待て", "神は見捨てない" ,"君ならできる" ]; //表示領域保持 let resultBox = document.getElementById("result"); //クリアボタンの表示領域を隠す document.getElementById('buttonClear').style.display="none"; ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //ボタンを押したらの動き document.getElementById("button").onclick = function(){ //入力値保持 let commentBox = document.getElementById("comment").value; //もしもコメントボックスに何か入力されていたら if (commentBox != "") { //あらかじめ用意したメッセージから.lengthで配列の要素数取得、その要素数だけループ処理、この場合条件を満たしている限りずっとループ while (0 < encouragement.length) { //クリアボタン表示 document.getElementById('buttonClear').style.display="block"; //配列の要素数に乱数を発生させてそれを整数に直す。 let eNumber = Math.floor(Math.random()*encouragement.length); //配列の中身の文字[ランダムに取得したインデックスナンバー]、つまりランダムでメッセージを取得しそれを表示領域に表示 resultBox.innerHTML = encouragement[eNumber]; //処理終わり break; } } //そうでない場合、つまりコメントが何も入力されていなかったら。 else { //結果を空に resultBox.innerHTML = ""; //クリアボタン削除 document.getElementById('buttonClear').style.display="none"; //警告文表示 alert("本当に悩みはありませんか?"); } } //ボタンを押したらの動き document.getElementById("buttonClear").onclick = function(){ //コメントを空に document.getElementById("comment").value = ""; //結果を空に resultBox.innerHTML = ""; //クリアボタン削除 document.getElementById('buttonClear').style.display="none"; }css
* { box-sizing: border-box; } html, body { margin: 0; padding: 0; display: flex; flex-direction: column; justify-content: center; height:100vh; } #container { margin:0 auto; width: 80vw; min-width: 300px; min-height: 50vh; border-radius: 5px; font-size: 1.2rem; text-align:center; overflow: auto; -webkit-box-shadow: 4px 4px 8px 6px rgba(0,0,0,0.17); -moz-box-shadow: 4px 4px 8px 6px rgba(0,0,0,0.17); box-shadow: 4px 4px 8px 6px rgba(0,0,0,0.17); } #container h1 { margin: 0; font-size: 1.3rem; padding:2vh 0vh; letter-spacing: 5px; } header { font-family: 'M PLUS Rounded 1c', sans-serif; background: #27e0b7; width:100%; color:#333; background: repeating-linear-gradient(45deg, #2affd0, #2affd0 10px, #27e0b7 0, #27e0b7 20px); text-shadow: white 3px 0px, white -3px 0px, white 0px -3px, white -3px 0px, white 3px 3px, white -3px 3px, white 3px -3px, white -3px -3px, white 1px 3px, white -1px 3px, white 1px -3px, white -1px -3px, white 3px 1px, white -3px 1px, white 3px -1px, white -3px -1px, white 1px 1px, white -1px 1px, white 1px -1px, white -1px -1px; } #message { padding:5vh 0vh; color:#333; font-weight:500; } #comment { width: 80%; height: 5vh; font-size: 1.15rem; } #button { background: #27e0b7; margin: 0 auto; text-align: center; cursor: pointer; width: 50vw; padding: 2vh; margin-top: 7vh; margin-bottom: 7vh; display: inline-block; padding: 0.5em 1em; text-decoration: none; color: #FFF; border-bottom: solid 4px rgb(201, 201, 201); border-radius: 5px; } #button:active { -webkit-transform: translateY(4px); transform: translateY(4px); border-bottom: none; } #result { font-family: 'M PLUS Rounded 1c', sans-serif; color: #ffeb3b; font-size: 1.5rem; font-weight: bold; letter-spacing: 5px; text-shadow: #806b20 3px 0px, #806b20 -3px 0px, #806b20 0px -3px, #806b20 -3px 0px, #806b20 3px 3px, #806b20 -3px 3px, #806b20 3px -3px, #806b20 -3px -3px, #806b20 1px 3px, #806b20 -1px 3px, #806b20 1px -3px, #806b20 -1px -3px, #806b20 3px 1px, #806b20 -3px 1px, #806b20 3px -1px, #806b20 -3px -1px, #806b20 1px 1px, #806b20 -1px 1px, #806b20 1px -1px, #806b20 -1px -1px; } #buttonClear { background: #27e0b7; margin: 0 auto; text-align: center; cursor: pointer; width: 50vw; padding: 2vh; margin-top: 5vh; margin-bottom: 7vh; display: inline-block; padding: 0.5em 1em; text-decoration: none; color: #FFF; border-bottom: solid 4px rgb(201, 201, 201); border-radius: 5px; } #buttonClear:active { -webkit-transform: translateY(4px); transform: translateY(4px); border-bottom: none; }
- 投稿日:2020-06-25T15:06:23+09:00
素のJavaScriptでパラメーターを取得する方法
まずは全体像から
アクセスするURLを以下のようにします。大切なのはパラメーター部分です。
http://localhost/index.html?param=hoge&name=aaaローカルホストはVSCodeの機能で簡単に作成ができます。以下の記事を参考にしてください。
https://qiita.com/sventouz/items/af54ce2ba27350b72343
let param = window.location.search; param = param.substring(1); let parameters = param.split('&'); for (let i = 0; i < parameters.length; i++) { let element = parameters[i].split('='); let name = decodeURIComponent(element[0]); let value = decodeURIComponent(element[1]); console.log(name); console.log(value); break; }解説
まず?以下のパラメーター部分を全て取得し、変数に入れます。
let param = window.location.search;substring
substringでいらない文字を削除します。本来は開始と終了を指定し、文字を切り取るのですが、1つのみの選択だと2文字目からの切り取りになるので今回であれば「?」が削除され、それ以下の文字列が変数に入ります。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/substring
param = param.substring(1);split
splitは指定した文字で文字列を区切ることができます。今回であれば「&」があるタイミングで文字列を区切っています。
今回であれば以下がパラメーター部分なのです。「?」がsubstringで削除されて、「&」で文字列が区切られるので変数に入っているのは
Array [ "param=hoge", "name=aaa" ]です。
?param=hoge&name=aaahttps://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/split
let parameters = param.split('&');decodeURIComponent
ぶっちゃけまだよくわかってない。decodeってことはencodeされたやつを戻すもの?日本語パラメーターが英語とか記号に変わってしまったものを直してくれるやつ?ってことかな?
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent
let name = decodeURIComponent(element[0]);まとめ
JavaScriptは奥が深い。が勉強を再始動してからいろいろ思い出してきた!
- 投稿日:2020-06-25T14:52:39+09:00
2020/6/25 これまでのプログラミング学習 独学
これまでの学習
3月・4月 progate
・HTML&CSS 初級・中級・上級
・Ruby
・Ruby on Rails
・SQL「そもそもプログラミングって何?」という疑問を解決するために、progateから始めた。
5月 ドットインストール
・HTML&CSS 復習
・JavaScript メインここから本格的にプログラミング学習を開始した時期、動画を使った方が自分には合っているなと実感した。
6月 上旬
基礎学習を終え道に迷う
6月 中旬・下旬 (これを書いてる最中)
Udemyの教材を使って基礎学習の深掘りをする。
+
同時並行で模写コーディング・自分が作ってみたいと思ったサイトを作ってみる
(Udemyで学んだことを生かせるような内容で)
+
Qiitaを使ってその日の学習内容を記録する。Qliitaを利用するきっかけとして、3月から6月上旬までインプットばかりしていたので、アウトプットする機会を少しでも増やそうと思ったから。
模写コーディングもアウトプットの一貫。環境構築
ドットインストールで使用していたものを継続して使い続けている。
・Google Chrome(ブラウザ)
・Visual Studio Code(コードエディター)
https://azure.microsoft.com/ja-jp/products/visual-studio-code/今後の学習内容と流れ
Udemyでインプットしながら、Qiitaにアウトプットする。
(これを主軸)
仕事でHTMLとCSSを使うので、復習も兼ねてHTML&CSSから開始し、Qiitaに学習メモを残す。模写コーディングに関して、自分のアイデアでCSSを書いた後に、CSSのフレームワークであるbootstrapを使ったコーディングもやってみる。
最後に
プログラミング独学自体は1年以内に終えることを目標とし、継続することに重きを置きながら頑張る。
- 投稿日:2020-06-25T14:08:10+09:00
Rails6のJavasqriptとJ.Queryの取り扱い方法
概要
Rails6からJS周りの取り扱いが大きく変わったようなので、
JavascriptとJ.Queryの取り扱いについてまとめました。概念
まずRailsではブラウザにJ/Sファイルの内容を表示するときには、
基本的にwebpackで複数のJ/Sファイルコンパイルして出力するという流れがあります。Rails6におけるJavasqriptの使用方法
Rails6では下記のjsに参照を書き込む事でコンパイル対象のモジュールを追加できる。
app/javascript/packs/application.jsコンパイルされたjsは、下記のタグヘルパーを記載する事でscriptタグが記載される。
javascript_pack_tag
デフォルトでは共通レイアウト(※)に書かれていて、全てのページにこれが読み込まれるようになっている。
app/views/layout/application.html.erb<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>実際作成したJ/Sファイルを仕込む方法
application.js
に参照を追加する事でコンパイル対象にする。
app/javascript以下の任意のディレクトリもしくは直下にjsを作成する。
(※packs以下に作らない事。)// This file is automatically compiled by Webpack, along with any other files // present in this directory. You're encouraged to place your actual application logic in // a relevant structure within app/javascript and only use these pack files to reference // that code so it'll be compiled. require("@rails/ujs").start() require("turbolinks").start() require("@rails/activestorage").start() require("channels") // 追加 require ('user/sample') // Uncomment to copy all static images under ../images to the output folder and reference // them with the image_pack_tag helper in views (e.g <%= image_pack_tag 'rails.png' %>) // or the `imagePath` JavaScript helper below. // // const images = require.context('../images', true) // const imagePath = (name) => images(name, true)Rails6でjQueryの導入方法
ターミナル
$ yarn add jquerywebpacker/environment.jsconst { environment } = require('@rails/webpacker') #追記 const webpack = require('webpack') environment.plugins.prepend('Provide', new webpack.ProvidePlugin({ $: 'jquery/src/jquery', jQuery: 'jquery/src/jquery' }) ) #ここまで module.exports = environmentapp/javascript/packs/application.jsrequire("@rails/ujs").start() require("turbolinks").start() require("@rails/activestorage").start() require("channels") require('jquery') #追記 require("J/Sファイル名")#実際に読み込むファイル
- 投稿日:2020-06-25T13:14:00+09:00
(デザイナー向け?)jsのデバッグコンソールに見覚えのないエラーが大量発生した時に原因解析を少し楽にしたい(特殊ケース)
タイトルについて
私用のアプリをサクッと作っていた時にコンソールがエラーで埋め尽くされたので原因を調べていたら、参照できないオブジェクト(null)に対し操作をしているらしい事が分かった。
ページごとに必ず発生しているがエラー件数も違うので原因がつかめずハマったのでviewを見ながらビルドしたところjsファイルに問題がある事が発覚した。昨今のフレームワークはこういう作りになっている事は珍しいが、古いCMSだとありそうだな、と思ったので提案。
良かれと思って書いているがバッドノウハウである(後述)事は留意しておいてほしい。
前提
- 生成側ではhtmlソースを管理していない
- たとえばindex.phpとかcontents.phpのようなファイルは存在しないか、編集不可である
- 生成されたhtmlファイルをview側で変更できない
- 複数のページで同じjsファイルを呼び出している
- 呼び出されるjsファイルはscript.jsしかない
- jsとcssは編集できる
という場合を想定。
なお、実際はユーザーが参照するファイルはhtmlではないのですが、説明をしやすいようにここでは生成物をhtmlとしている。生成されるソースコード例
index.html(中略) <script src="script.js"> (中略) <section id="top_block"> (中略) <!-- こちらにid:contents_blockは存在しない -->contents.html(中略) <script src='script.js'> (中略) <section id="contents_block"> (中略) <!-- こちらにid:top_blockは存在しない -->この時、
id:top_block
とid:contents_block
に対して何かしらのアプローチをしたい場合、他のページで存在しない要素に対して制御を行うとブラウザのコンソールに期待しないエラーが発生するためデバッグが面倒になる。
これが少ない数なら無視できるが、それぞれのページや要素に対して何か行っている場合、呼び出している回数だけエラーが発生することになってしまう。解決(提案)
script.jsfunction run_judge_function ( obj, func ) { obj == null ? null : func(); }関数を実施させる際に直接呼び出すのではなく、
run_judge_function
に対象となるオブジェクトと実施したい関数を渡してnull(undefined)を検出してあげて、見つかれば実施したい関数を実行するというアプローチを検討する。注意
この関数が想定される・本来は検出したいエラーを揉み消す可能性があり、本番環境に乗せてしまうと異常が発生した場合に適切なエラーログが取得できない事が考えられる。
script.jslet stage_flg = true; // ※1 function run_judge_function ( obj, func ) { if (stage_flg) obj == null ? null : func(); }(※11)
としておくと、消し忘れを防止できるが検証環境と本番環境で差異が発生する事に留意。
検証環境の時はtrue、そうでなければfalseにする環境変数を用意しておく事も検討すべきか。備考
本来は呼び出し側で適切なスクリプトを参照するようにしてあげるのがスマートだが、これらのhtmlソースを生成する側のファイルでヘッダ情報などを共通化しているために起こってしまう。
生成側のファイルを変更できるなら調整や対応をissueに上げるべきだが、今回はあくまでview側で解決する場合を考えた。注意とお節介
この問題は現場でも起こる可能性があるのではないかと考えており、たとえばプロジェクト初期に想定されなかった要素を継ぎ接ぎで追加した時に起こりがちな問題ではないだろうか?
後で継ぎ接ぎした時に良かれと思ってこういう関数をかますと(既存部分への影響が大きすぎてテストが大変になり)メチャクチャ怒られるのが普通なので導入時は注意するか、なんならstage環境にも上げるべきではない。githubなどコードレビュー文化が根付いているならチェックが入るはずだが、コードを書けるプロの人はレビューもプロか?というとそうではないので運用上の注意が必要。
お願い
ページを参照した(view)がイコール最後まで読んでもらった(complete)と思っていないので、お手数を掛けて恐縮だがLGTMをいただけると、読みやすい書き方や記事がどういうものか分析しやすくなり大変助かります。
注釈
【※1】実際の使用例としてstage_flgは環境変数か何かで設定する事が望ましい。 ↩
- 投稿日:2020-06-25T13:08:40+09:00
AdonisJSとReactを使ってイベントを管理するためのモダンなWebアプリを構築する
このチュートリアルでは、ReactとAdonis Node.jsフレームワークを使って、Alibaba Cloud上でREST APIのバックエンドとフロントエンドを構築します。
本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。
序章
Adonisは、スケーラブルで安定した効率的なWebアプリケーションやREST APIを構築するために使用できるNode.jsフレームワークです。Reactを使用して、Adonisで構築されたREST APIを消費するためのモダンで堅牢なフロントエンドWebアプリケーションを構築します。
これから構築するWebアプリケーションは、MySQLのデータベースにイベントを格納し、これらのイベントを読み込んで削除します。
完成したら、以下のことができるようになります。
- Adonis.jsを使ったREST APIの構築
- Reactを使ってREST APIを消費する堅牢なクライアントサイドアプリケーションを構築する
- Bootstrap 4とReactを統合する
前提条件
このガイドを始める前に、以下のものが必要です。
- Ubuntu 18.04を搭載したElastic Compute Service (ECS)開発マシン。
- Node.js 8.0.0.0+とNpm 3.0.0.0+がマシンにインストールされている。
- ローカルにインストールされたMySQL 5.7+。
ステップ1 - Adonis APIプロジェクトの作成と依存関係のインストール
このステップでは、Adonisプロジェクトを足場にするのに役立つAdonis CLIをインストールし、アプリケーションのデータベース接続をセットアップするために必要な依存関係をインストールします。
ターミナルを開き、以下のコマンドを実行してAdonis.js CLIをグローバルにインストールします。
npm i -g @adonisjs/cliこれでインストールが完了したら、ターミナルのどこからでも
adonis
を実行できるようになるはずです。以下のコマンドを実行して、インストールしたCLIのバージョンを確認することで試すことができます。adonis --version次に、新しいAdonisアプリケーションを足場にする必要があります。任意のディレクトリに移動し、以下のコマンドを実行します。
adonis new adonis-events --api-only新しいコマンドは
adonis-events
という新しいプロジェクトを足場にし、--api-only
は生成されたプロジェクトが特に REST API 用であることを指定します。新しいコマンドは、新しいプロジェクトに必要なすべての npm 依存関係をインストールします。生成されるプロジェクトは以下のような構造になっているはずです。
次に、データベース接続を設定する必要があります。Adonis は多くの SQL データベースをサポートしていますが、使用する特定のデータベース、この場合は MySQL 用のパッケージをインストールする必要があります。プロジェクトフォルダに移動し、以下のコマンドを実行します。
npm i --save mysqlこのプロジェクトでは、
.env
というファイルにアプリケーションを設定するための環境変数が含まれています。データベース接続がmysql
に設定されていることを確認してください。[~/adonis-events/.env] ... DB_CONNECTION=mysql ...最後に、このプロジェクトで使用する MySQL データベースをローカルに作成する必要があります。ローカルにMySQLがインストールされている場合は、以下のコマンドを実行してデータベースを作成します。
mysql -u rootmysql> CREATE DATABASE events;新しいデータベースを作成したら、
.env
ファイルの資格情報を作成したデータベースと一致するように更新します。[~/adonis-events/.env] ... DB_CONNECTION=mysql DB_USERNAME=root DB_DATABASE=events DB_PASSWORD= ...これが完了したら、REST APIとReactフロントエンドの作成に進みます。
ステップ2 - Reactフロントエンドを作成する
このステップでは、Reactを使ってアプリケーションのフロントエンドを作成します。webpack の設定や本番のビルドプロセスを気にすることなく、React アプリケーションを素早く足場にするために、
crease-react-app
という人気のあるパッケージを使用します。ローカルにcreate-react-app
がインストールされていない場合は、以下のコマンドを実行してください。npm i -g create-react-appインストールしたら、新しいフォルダに移動し、以下のコマンドを実行して新しいReactプロジェクトを作成します。
create-react-app adonis-events-react新たに生成されたReactプロジェクトの構造は以下のようになります。
新しく作成したReactプロジェクトを起動するには、Reactプロジェクトディレクトリに移動し、以下のコマンドを実行します。
npm startこれにより開発サーバーが起動し、デフォルトのブラウザで以下のようなReactアプリケーションが開くはずです。
最後に、基本的なスタイリングを行うためのBootstrap CSSフレームワークをインストールしてみましょう。
Reactプロジェクト内の
src
フォルダに入り、index.css
ファイルを開きます。このファイルの内容を以下のように置き換えます。[~/adonis-events-react/src/index.css] @import 'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css';上記は、ブートストラップCSSファイルへのリンクをインポートしたもので、フロントエンドプロジェクトのスタイルを素早く設定するために使用できるすべてのスタイルとクラスが含まれています。
フロントエンドアプリケーションのセットアップが完了したので、データベースモデルと移行を開発しましょう。
ステップ3 - イベントの移行とモデルの作成
このステップでは、データベーステーブルをどのように構造化するかを決定する移行をセットアップし、このデータベーステーブルとの対話に役立つモデルをセットアップします。
まず、ターミナルでAdonisアプリケーションに移動し、次のコマンドを実行します。
adonis make:model Event -mこのコマンドは、モデルとそれに対応するマイグレーションを素早く作成する方法です。以下のような出力が得られます。
create app/Models/Event.js create database/migrations/1541120387036_event_schema.js作成したファイルは2つで、
app/Models/Event.js
はこんな感じです。'use strict' const Model = use('Model') class Event extends Model { } module.exports = Eventこれは基本的なES6のクラスで、ベースとなる
Model
クラスを継承しています。見ての通り、このクラスには多くの静的メソッドがあり、データベースと簡単にやりとりするのに役立ちます。2つ目のファイルは、
database/migrations/1541120387036_event_schema.js
で、以下のようになっています。'use strict' const Schema = use('Schema') class EventSchema extends Schema { up () { this.create('events', (table) => { table.increments() table.timestamps() }) } down () { this.drop('events') } } module.exports = EventSchemaAdonisのマイグレーションは、ベースの
Schema
クラスを拡張したES6クラスで、2つの重要なメソッドを含んでいます:up
は、データベーステーブルの作成や変更を含むデータベースへの変更を行うために使用され、down
はそれらの変更を反転させるために使用されます。up
関数では、データベーステーブルの構造を記述するAdonisが提供するメソッドを呼び出します。現時点では、
up
関数の中の関数this.create('events')がデータベース内にイベントテーブルを作成します。また、down
関数のthis.drop('events’)
は、必要に応じてデータベース内のイベントテーブルを削除します。イベントテーブルには以下のフィールドがあります。
- title - イベントのタイトル
- start_date - イベントの開始日.
- end_date - イベントの終了日.
- location - 偶数の場所
- price - イベントの費用
マイグレーションでは、以下のようにこれらのフィールドを定義します。
次に、このマイグレーションファイルを実行して、Adonisが接続されたデータベースにこれらの変更を影響させることができるようにする必要があります。
プロジェクトフォルダにあることを確認し、以下のコマンドを実行してください。
adonis migration:run以下のような出力が表示されるはずです。
migrate: 1503250034279_user.js migrate: 1503250034280_token.js migrate: 1541120387036_event_schema.js Database migrated successfully in 206 msこのコマンドを実行すると、Adonisは各マイグレーションファイルを読み込み、これらのファイル内で
up
機能を実行します。したがって、現時点では、私たちのデータベースには、users
、tokens
、events
の3つのテーブルが新たに作成されています。最初の2つのテーブルは、Adonisの新規インストール時にデフォルトで出荷されます。データベースのセットアップが完了したら、REST APIの作成に進みます。
ステップ4 - REST APIの作成
このステップでは、REST APIを作成します。イベントに対するさまざまなアクションを処理するために、いくつかの API エンドポイントを作成します。作成するエンドポイントは以下の通りです。
[POST] /api/events
は、イベントを作成[DELETE] /api/events/:id
は、 特定のイベントを削除[GET] /api/events
は、すべてのイベントのリストを取得[POST]/api/eventsエンドポイントの実装
まずは、これらのエンドポイントを処理するためのコントローラを生成することから始めましょう。コントローラは基本的に ES6 クラスで、すべてのビジネスロジックを処理するメソッドを含んでいます。以下のコマンドを実行してください。
adonis make:controller EventController以下のような出力が表示されるはずです。
> Select controller type For HTTP requests create app/Controllers/Http/EventController.js次に、この特定のエンドポイントを扱うルートを登録する必要があります。
start/routes.js
ファイルを開き、以下を追加します。
Route
クラスにはpost
という静的関数があり、このメソッドはpostエンドポイントを登録します。最初のパラメータはパス、api/events
、2番目のパラメータはこのエンドポイントへのリクエストを処理するコントローラとメソッドです。この場合、EventController
のstore
メソッドです。つまり、このメソッドを作成する必要があります。EventController
のように追加します。Adonis は
context
と呼ばれるオブジェクトを注入しますが、このcontext
にはrequest
オブジェクトとresponse
オブジェクトを含む、requestに関する多くの関数とオブジェクトが含まれており、request
オブジェクトにはrequestに関するすべての情報が含まれています。現時点でのstore
メソッドでは、request.all()
関数を呼び出すと、POSTリクエストで送信された全てのデータを返してくれます。今回は、イベントを作成するために、リクエストデータにtitle、start_date、end_date、location、price
が提供されていることを想定しています。次に、このデータをデータベースに保存しなければなりません。データベース、特に
events
テーブルと通信するために、モデルを使用します。events
モデルを使用すると、Adonisは自動的にevents
テーブルに接続します。
events
モデルを使用して、create
関数を呼び出しました。これは、POSTリクエストから取得したデータを使用して、自動的に新しい行をeventsデータベーステーブルに保存します。この関数は新しく作成されたイベントを返し、このeventsと一緒にJSONレスポンスを返します。注: Adonisの
use
関数はNode.jsのrequire
関数のラッパーです。これは名前空間を使用し、上で行ったようにファイルを簡単にrequireするのに役立ちます。ですから、require('../.../.../Models/Event.js’)
の代わりに、単に `use('App/Models/Event') と書きます。先ほど実装した/POSTエンドポイントを試してみましょう。そのためには、Adonis APIを実行する必要があります。
以下のコマンドでadonisサーバーを実行します。
adonis serve --devinfo: serving app on http://127.0.0.1:3333新しく作成したPOST
/api/events
エンドポイントを試すために、任意のRESTクライアントを使用することができます。今回はPOSTMANを使ってみます。操作結果は以下の通りです。
注:POSTMANでは
Content-type
をapplication/json
に設定することを忘れないでください。[取得] api/eventsエンドポイントの実装
このエンドポイントのルートを追加してみましょう。
routes.js
ファイルで、次のように追加します。[~/adonis-events/start/routes.js] Route.get(api/events', 'EventController.index')これはエンドポイントを
EventController
のindex
メソッドにマッピングします。このコントローラでは、次のように追加してみましょう。
index
メソッドは、events
モデルのall()
関数を使用してevents
テーブルからデータベースのすべての行を取得します。これをPOSTMANで試してみると、以下のような結果が得られるはずです。[削除] api/events/:id エンドポイントの実装
最後に実装しなければならないエンドポイントは、イベントを削除するためのものです。このエンドポイントはこれまでの他のエンドポイントとは少し異なり、動的パラメータを使用します。このエンドポイントのルートを登録してみましょう。
[~/adonis-events/start/routes.js] Route.delete(api/events/:id', 'EventController.delete')ルートの
:id
は動的パラメータで、削除したいイベントの特定のid
を渡すことができます。このルートをEventController
のdelete
メソッドにマッチさせます。
delete
メソッドでは、ルートから取得した動的ID
を使用してイベントを検出し、検出されたイベントに対してdelete
関数を呼び出します。以下に POSTMAN でのテストの様子を示します。それだけです。Adonisで完全に機能するAPIを作るのはこんなに簡単なことなのです。フロントエンドに切り替える前にもう一つ。現時点では、Adonis APIは他のサーバーやドメインからのリクエストを許可しません。これはCORS保護と呼ばれるもので、不正なリクエストがAPIに行われないようにするために本当に重要です。この特定のケースでは、ReactフロントエンドがAPIリクエストを行うことを許可するようにAdonisに指示しなければなりません。これを行うには、Adonisには設定ファイルが既に設定されているので、アプリケーションのCORS設定を変更することができます。
config/cors.js
に移動して、このファイルをこのように修正します。[~/adonis-events/config/cors.js] origin: ['http://localhost:3000'],ここでは、すべての認可されたオリジンの配列を作成し、この配列にリストされたすべてのドメインがAdonisアプリケーションにAPIリクエストを行うことができるようになります。クライアント側のReact開発サーバーのドメインは
http://localhost:300
なので、これをorigin
配列に追加しました。次のステップでは、Reactフロントエンドに切り替えて、堅牢なクライアントサイドアプリケーションを構築するために、このデータの消費を開始しましょう。
ステップ5 - クライアントサイドコンポーネントを設定する
最初に設定するコンポーネントは、
App
コンポーネントです。これは、最初にアプリケーションにアクセスしたときに表示されるコンポーネントです。他の不要なファイルもすべてクリーンアップします。不要なファイルを削除した後の現在のフォルダ構造は以下の通りです。
次に、単一のイベントを表現するために使用するコンポーネントを作成してみましょう。プロジェクトフォルダに移動し、
src
フォルダ内にEvent.js
というファイルを作成します。このコンポーネントは、現時点での静的データを表示し、単一のイベントを表示するためのテンプレートを表しています。
最後に、
AddEvent.js
という別のファイルを作成します。これは、新しいイベントを追加するためのフォームとして機能します。必要なコンポーネントの作成が完了したので、APIサービスを作成してみましょう。
ステップ6 - Axios APIサービスの作成
Adonis API と通信できるようにするために、シンプルな HTTP クライアントを使用します。このクライアントをインストールするには、Reactプロジェクトに移動し、次のコマンドを実行します。
npm install axios --saveこれをインストールしたら、
src
フォルダにapiService.js
というファイルを作成し、以下のクラスを追加します。このクラスは axios のシンプルなラッパーであり、Adonis API への HTTP リクエストを行うためのヘルパー関数を提供します。コンストラクタでは、すべてのリクエストの
BaseURL
をAdonisアプリケーションのURLに設定します。サービスの準備ができたので、これらのコンポーネントを機能させてみましょう。
ステップ7 - イベントの作成
イベントを作成するために、
Create Event
ボタンを用意します。ユーザーがこのボタンをクリックすると、イベントを作成するためのフォームを持つコンポーネントが表示されます。Appコンポーネントを以下のように変更してみましょう。現時点では以下のようになっています。
まず、
AddEvent
コンポーネントをインポートしました。showCreateEventForm
というステート・プロパティを設定し、レンダー関数の中で、このプロパティが真であればAddEvent
コンポーネントをマウントし、そうでなければマウントしません。Create Event
ボタンがクリックされると、このステート・プロパティの値を切り替えます。また、showCreateEventForm
がtrue
の場合はCreate Event
ボタンにbtn-danger
クラスを動的に追加し、このプロパティがfalse
の場合はbtn-info
を追加します。次のステップは、このフォームを実際に動作させて、ユーザーがイベントの詳細を入力して保存できるようにすることです。
イベントの作成機能の実装
最初のステップは、ユーザーが入力した入力を取得することです。これを管理するためにステートを使います。ここに
AddEvent.js
コンポーネントのアップデート版があります。この時点では、入力フィールドが変更されるたびに、その値は自動的にこのコンポーネントの状態に設定されます。
現時点では、デフォルトのブラウザの日付ピッカーを使用しており、これには時間ピッカーが統合されていません。さらに、この日付ピッカーの動作はブラウザによって非常に一貫性がありません。そこで、
start_date
とend_date
のフィールドには、FlatPickr
と呼ばれるサードパーティ製の日付ピッカーを使用します。この日付ピッカーをインストールするには、以下のコマンドを実行します。npm i --save react-flatpickrインストールできたら、このようにフォームを修正してみましょう。
まず、インストールした
FlatPickr
コンポーネントをインポートします。前回の日付入力の代わりに、FlatPickr
をマウントし、handleDateChange
と呼ばれる別のハンドラを渡します。これはほとんど同じことを行い、start_date
とend_date
の値が変更されたときの状態に設定します。さて、このイベントを保存するためのAPIリクエストを実際に行う時が来ました。これを行うために、Appコンポーネントから関数を渡し、呼び出されたときにイベントを保存します。Appコンポーネントをこのように修正してみましょう。
コンポーネントをこのように更新してみましょう。
まず、
ApiService
をインポートし、Appコンポーネントのコンストラクタで新しいインスタンスを作成します。次に、apiServiceからの関数であるstoreEvent
というAddEvent
コンポーネントにプロップを渡します。次に、
AddEvent
コンポーネントのCreate Event
ボタンが作成された際に、このプロップを呼び出す必要があります。
Create Event
ボタンがクリックされると、this.props.storeEvent()
を呼び出し、状態をパラメータとして渡します。この関数は axios を呼び出し、イベントを保存するための HTTP リクエストを行います。開発者ツールで発信HTTPリクエストを確認すると、AdonisサーバーからのHTTPレスポンスが表示されているはずです。すべてのイベントのリストが表示されるまでは、作成されたことを確信することはできませんよね?次のステップでは、Adonis APIからすべてのイベントのリストをフェッチして表示します。
すべてのイベントをフェッチして表示する
すべてのイベントを取得するために、コンポーネントがマウントされた直後に
[GET]
リクエストを行い、結果を取得したら、その結果をステートに設定してコンポーネントに表示します。これを実現するために、componentDidMount
というReactのライフサイクルフックを追加してみましょう。この関数は、コンポーネントがマウントされるとすぐに実行されます。まず、イベントという新しいステートプロパティを空の配列に設定します。次に、
componentDidMount
関数の中で、APIリクエストをしてすべてのイベントを取得し、レスポンスが返ってきたら、その結果をstateに設定します。この時点でReact Dev toolsなどのツールを使ってコンポーネントの状態を検査すると、すべてのイベントのリストが表示されているはずです。次のステップは、このイベントのリストをコンポーネントに表示することです。このために作成した
Event
コンポーネントを使用します。App
コンポーネントをこのように変更してみましょう。各イベントをマッピングして、1つのイベントを所定の場所に表示します。現時点では、アプリケーションがマウントされると、対応するイベントのリストが表示されるはずです。
イベント コンポーネントはまだ動的ではありません。これは、特定のイベント(Appコンポーネントから渡された)のためのプロップを受け取り、代わりにそれらを表示する必要があります。この動作をサポートするように変更してみましょう。
これで、各イベントは
event
プロップを介して渡されたカスタムデータを表示するようになりました。現時点では、イベントを作成しても何も起こりません。より良いエクスペリエンスを得るために、App コンポーネントを修正して、
AddEvent
フォームを非表示にし、すべてのイベントを再度取得して、ユーザーが新しく作成されたイベントを見ることができるようにします。最初に、
fetchEvents
という新しい関数を作成します。次に、storeEventという新しい関数を作成します。この関数はAPIを呼び出して新しいイベントを保存し、レスポンスを取得したらfetchEvents
を呼び出してすべてのイベントを取得し、toggleShowCreateEventForm
を呼び出してcreateイベントフォームを非表示にします。ステップ8 - Deleteイベントの実装
最後のタスクは、イベントを削除できるようにすることです。App コンポーネント上でイベントを削除する関数を作成し、この関数を
Event
コンポーネントのプロップとして渡し、ユーザーが削除ボタンをクリックするたびにこの関数が呼び出されます。Appコンポーネントは次のようになります。新しく追加された関数
deleteEvent
はAPIサービスを呼び出し、イベントが削除されると、再びfetchEvents
を呼び出して、すべてのイベントの新鮮なコピーを取得します。最後に、Event
コンポーネントで、id
パラメータを指定してこの関数を呼び出します。このようにEvent
コンポーネントを修正します。すべての変更を行った後のプロジェクトの動作は次のようになります。
このアプリケーションが完成したことで、実際のプロジェクトの参考として使用できる最新のフルスタックJavascriptアプリケーションの基礎を手に入れることができます。
結論
このチュートリアルでは、Adonis.jsとReact.jsを使ってフルスタックのJavascriptデモアプリケーションを作成しました。REST APIの構築にはAdonis.js、APIの消費にはReact、アプリケーションのスタイリングにはBootstrap 4を使用しました。Adonis REST APIのソースコードはこちらのGitHubリポジトリに、ReactフロントエンドアプリケーションのソースコードはこちらのGitHubリポジトリにあります。
Adonisフレームワークについてもっと詳しく知りたい場合は、公式ドキュメントがとても便利で、ビデオチュートリアルも提供されています。
アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ
- 投稿日:2020-06-25T12:11:36+09:00
プロジェクト内にtsconfigが複数あるときに、今開いているtsファイルに適用されるコンフィグを簡単に開く for VSCode
front,server,api,testなどで、tsconfigを分けて管理したいときに、
当該tsファイルに適用しているtsconfigファイルを簡単に開く方法の紹介。
- 投稿日:2020-06-25T11:06:51+09:00
JavaScriptだけでIoTネットワークを構築する方法
この記事では、Alibaba CloudのIoTプラットフォーム上でJavaScriptを使って開発者が簡単にIoTネットワークを構築する方法を説明しています。
本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。
概要
この例では、開発ハードウェアとしてDeveloper Kit(AliOS Thingsを移植したもの)を使用して、基板実装の温度センサからLink Developへのデータアップロードを実装し、デモにはWeb Appを使用しています。
開発プロセス
環境の設定
AliOS Thingsは、アリババクラウドのIoTチームが開発中のIoT指向で拡張性の高いIoTオペレーティングシステムです。フロントエンド開発者の要求に応えるため、AliOS Thingsは、これまでCとC++でしか実装されていなかったIoT開発のためのJavaScriptをサポートするJavaエンジン「BoneEngine」を統合しています。BoneEngineは構文をシンプルにすることで、リソースが限られている組み込み機器でもJavaScriptアプリケーションを実行できるようにしました。
BoneEngineを使ってデバイスサイドの開発を行うには、事前に環境設定を行う必要があります。設定手順は以下の通りです。
1、Node.js(バージョン6.4.0以降、フロントエンド開発の場合はバージョン8.9.0以降を推奨)をインストールします。
2、シリアルポートまたはネットワーク経由でapp.binを更新するためのサポートを提供するbe-cliツールをインストールします:npm i be-cli -g -P
先行のグローバルインストールが完了した後にbeを実行してもコマンドが見つからないと表示される場合は、npm prefix -g
コマンドを実行してNode.jsのモジュールインストールパスを取得し、出力にあるグローバルインストールパスを環境変数の "Path "エントリに追加するだけでOKです。
3、Windowsユーザーのみ)Python、Visual Studio、AliOSプラグインをインストールします。続いて、ハードウェアUSBドライバをインストールします。
4、Gitをインストールし、AliOS Thingsのソースコードをダウンロードします:git clone git@github.com:alibaba/AliOS-Things.git
これで環境が設定できたので、開発を開始する準備ができました。
クラウド上での開発
今回はLink Developを使用して、Alibaba Cloud上にIoTネットワークを設定します。Create Project(任意の名前をつけても構いません)>Device Development>Add Productを選択します。製品の種類はOtherを選択し、通信方式は「WiFi」を選択し、データ形式は「Alink」を選択します。
Product DetailsでAdd Custom Featuresを選択し、以下の機能を追加します。
Device Development > Add Debugging Devices を選択し、デバイスの三重項に注意してください。
デバイスでの開発
コンパイラでAliOS Thingsのソースコードディレクトリを開き、
AliOS-Things/framework/tinyengine/samples/app/lcd_shtc1_mqtt/device.js
ファイルを開き、Three Device Elementsを先ほど取得したDevice Tripletsに変更します。変更内容を保存します。ターミナルを使ってサンプルアプリケーションをコンパイルします。
be -p samples/app/lcd_shtc1_mqtt.
Developer Kit をコンピュータに接続し、
be connnect
を実行します。コンパイルしたbinファイルをデバイスに書き込みます。
be push app.bin
デバイスを再起動します。デバイスを再起動すると、デバイスは自動的にこのファイルをロードして実行し、同時にLink Developプラットフォームにアップロードします。
Webアプリでの開発
すぐに始められるように、ここではLink Developの新バージョンに統合されたWeb Appのビジュアル作成機能を利用します。必要なのは、吸湿器の部品をドラッグしてデータに接続するだけです。
経験豊富なフロントエンド開発者は、Bone.jsフレームワークを使ってハードコアなプログラミング機能を実行してみてください。ここでも JavaScript のみで実装できます: https://bone.aliyun.com/bone-web/bonewebsdk.html?name=wpgl7p.
結論
AliOS Things TinyEngineとLink Develop Bone.jsのミックスにより、フロントエンドの開発者は他のプログラミング言語を学ぶことなく、迅速かつシームレスにIoT開発を始めることができます。これは、Alibaba Cloud IoT Platformの互換性と独自性をも表しています。
Alibaba Cloud IoT Platformの詳細については、www.alibabacloud.com/product/iot をご覧ください。
アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ
- 投稿日:2020-06-25T10:59:09+09:00
意外と複雑な「クリップボードにコピー」
メールアドレスなどでよく見かける、「クリックしてクリップボードにコピー」
よく見かけるが故にcopy('コピーしたい文言')
みたいな感じで簡単に出来ると思ったら、意外と複雑だったので備忘録です。clipboardCopy: function(e) { e.preventDefault() const targetSentence = 'コピーしたい文言' let input = document.createElement('input') input.readOnly = true document.body.appendChild(input) input.value = targetSentence input.select() document.execCommand('copy') document.body.removeChild(input) }ざっくりとした流れ
①inputを生成する
let input = document.createElement('input')
②body内の一番最後の要素に、①のinputノードを追加する
document.body.appendChild(input)
③inputの中に、任意のコピーしたい文字列を挿入する
input.value = targetSentence
④inputの内容をselectする
input.select()
⑤選択している内容に対して、コピーコマンドを実行する
document.execCommand('copy')
⑥inputは用済みなのでさようならする
document.body.removeChild(input)
なぜinputか
select()
を使えるのがキモなんだと思います。補足
execCommand()について
コマンドキーを押して出来ることがだいたいできるみたいです。
cutやpasteもできます。ただし、pasteはバックグラウンドのみ。
(個人情報などがクリップボードに入っていた場合勝手に抜き取られる可能性があるため。)
ちなみにIEだとpasteも許容しているみたいです。さすがですね。
https://developer.mozilla.org/ja/docs/Mozilla/Add-ons/WebExtensions/Interact_with_the_clipboardん?と思ったのは、これ非推奨なんですよね。
ほかにクリップボードとやりとりできる代替え手段があるのでしょうか?また調べてみます。
https://developer.mozilla.org/ja/docs/Web/API/Document/execCommand時間がかかったところ
iOSで確認すると、クリックと同時に勝手にスクロールされる
iOSですと、input系を選択(タップ)した時にその要素がズームされたり、中央よりちょい上の位置にスクロールされたりします。
この例では、作成したinputをbodyの中の最後の子ノードのうしろに追加しているので、ページの最後にinputが出現している形になります。そのため、テキストリンクをタップした瞬間に、ページ下方へのスクロールが走ります。
よかれと思ってやってくれているんだろうけども、inputはユーザが認識するよりも先に消えてしまうのでなんの意味もないです…。ここで、inputをreadonlyに設定することで、スクロールを防ぎます。
input.readOnly = true
ズームされる
こちらはjsの問題ではなく、iOSの仕様と関係しています。
iOSですと16px未満のfont-sizeをもつinputをタップすると、問答無用でズームされます。
上記の実装でも目にはinputはみえませんが、input.selectをしているので謎にズームされます。
ズーム禁止にしてしまうとユーザビリティに影響が出るので、ひとまずSPでのfont-sizeを16pxにしてことなきを得ました。
(これを知ってたら最初から16pxにしてたのに…次回から気をつけよう)
- 投稿日:2020-06-25T10:54:21+09:00
window.location.hrefで別タブでリンクを開く方法
- 投稿日:2020-06-25T10:21:16+09:00
DOMとは何か(初心者からの目線)
DOMとは何か
そもそもDOMって一体何?
https://ja.wikipedia.org/wiki/Document_Object_Model
DOMはDocumentObjectModelのこと。
HTLMなどの階層化、構造化された文章のこと。階層化された文章のオブジェクト群だよ(各要素群の集合だよ)という理解。
- 投稿日:2020-06-25T08:52:16+09:00
PropsとStateの違い
Reactを学習していて、PropsとStateの違いってなんやろ
とふとした時に思い調べました。学習メモなのでふわっとしていますが、どうぞよしなに。
*間違いがあれば教えていただけると、ありがたいです。
【Propsとは】
・オブジェクト
・親コンポーネントからもらう値
・Propsを受け取る側の子コンポーネントではPropsを変化させれない
・親コンポーネントから変化させられる【Stateとは】
・オブジェクト
・コンポーネント自身で定義する
・初期値を設定したらsetStateでStateの値を変更する<まとめ>
Props→上から下に流れるデータ
State→コンポーネント自身が持つデータ
- 投稿日:2020-06-25T07:01:13+09:00
インクリメンタルサーチ
はじめに・目的
- search controllerのhtml viewでの検索機能だと、ワード全て入力して検索しないといけない。
- jsonを使ったインクリメンタルサーチだと、非同期で検索可能。
- テーブル内で検索が頻繁に行われるカラムにインデックスを設定することで検索の高速化を図る。
- 今回はtweetsテーブルのtextカラムにインデックスを貼ることで、データの検索を高速化する。
terminal$ rails g migration AddIndexToTweetsマイグレーションファイルclass AddIndexToTweets < ActiveRecord::Migration def change add_index :tweets, :text, length: 32 end end→migrate実行
1.ルーティングなどAPI側の準備
Formatごとの条件分岐
app/controlers/tweets_controller.rbdef search @tweets = Tweet.search(params[:keyword]) respond_to do |format| format.html format.json end end #投稿情報を取得したら、jbuilderを使ってJavaScript側に返す。 #検索結果は、複数の投稿情報を表示させます。 #そのため、複数の投稿情報が格納された配列を返すようなjbuilderの記述にする必要がありますjson.jbuilderの作成
app/views/tweets/search.json.jbuilderjson.array! @tweets do |tweet| json.id tweet.id json.text tweet.text json.image tweet.image json.user_id tweet.user_id json.nickname tweet.user.nickname json.user_sign_in current_user endJSON形式のデータを配列で返したい場合は、上記のように
array!
を使用。2. テキストフィールドを作成
views/tweets/index.html.erb<%= form_with(url: search_tweets_path, local: true, method: :get, class: "search-form") do |form| %> <%= form.text_field :keyword, placeholder: "投稿を検索する", class: "search-input" %> <%= form.submit "検索", class: "search-btn" %> <% end %>3.テキストフィールドに文字入力するたびに、イベント発火
app/assets/javascripts/search.js$(function() { $(".search-input").on("keyup", function() { var input = $(".search-input").val(); }); }); //キーを離したら処理をさせたいときはkeyupメソッドを使用4.イベント時に非同期通信できるよう$.ajaxメソッドの記載
app/assets/javascripts/search.js$(function() { $(".search-input").on("keyup", function() { var input = $(".search-input").val(); $.ajax({ type: 'GET', //HTTPメソッドはGET url: '/tweets/search', //rails routesで確認 data: { keyword: input }, dataType: 'json' }) }); });5.非同期通信の結果を得て、HTMLを作成
app/assets/javascripts/search.js.done(function(tweets) { search_list.empty(); if (tweets.length !== 0) { //tweetsの数が0でない時、 tweets.forEach(function(tweet){ //forEachメソッド appendTweet(tweet); }); } else { appendErrMsgToHTML("一致するツイートがありません"); } }) .fail(function() { //error時のaleart実装 alert('error'); }); }); });
- forEachは、与えられた関数を配列に含まれる各要素に対して一度ずつ呼び出します
search.js変数・メソッド定義var search_list = $(".contents.row"); function appendTweet(tweet) { if(tweet.user_sign_in && tweet.user_sign_in.id == tweet.user_id){ var current_user = `<li> <a href="/tweets/${tweet.id}/edit" data-method="get" >編集</a> </li> <li> <a href="/tweets/${tweet.id}" data-method="delete" >削除</a> </li>` } else { var current_user = "" } var html = `<div class="content_post" style="background-image: url(${tweet.image});"> <div class="more"> <span><img src="/assets/arrow_top.png"></span> <ul class="more_list"> <li> <a href="/tweets/${tweet.id}" data-method="get" >詳細</a> </li> ${current_user} </ul> </div> <p>${tweet.text}</p><br> <span class="name"> <a href="/users/${tweet.user_id}"> <span>投稿者</span>${tweet.nickname} </a> </span> </div>` search_list.append(html); } function appendErrMsgToHTML(msg) { var html = `<div class='name'>${ msg }</div>` search_list.append(html); }
- 投稿日:2020-06-25T06:35:52+09:00
Fetch APIでPHPにデータを送信する方法
画面遷移をせず、ajaxでデータをPHPに渡す方法です。
下記は「ボタンをクリックした時にPHPにデータを渡す」処理についてのサンプルです。
Javascript
id="start"
のボタンがあるとします。index.html<div class="Container"> <button class="btn" id="start">start</button> </div>vanilla Javascriptでは、下記のように記述します。
main.jsstart.addEventListener('click', () => { const postData = new FormData; // フォーム方式で送る場合 postData.set('firstName', 'hoge'); // set()で格納する postData.set('lastName', 'fuga'); const data = { method: 'POST', body: postData }; fetch('test.php', data) .then((res) => res.text()) .then(console.log); });PHP
PHP側で、$_POST連想配列に値が保存されます。
test.php<?php echo $_POST['firstName']; // hoge参考
- 投稿日:2020-06-25T01:49:00+09:00
ReactでSemantic Uiを 使ってみる
■semantic ui react
yarn add semantic-ui-react※フォントを変えたいのでsemantic-cssは読み込まない
LESSを読むためにcracoをインストール
yarn add @craco/craco @semantic-ui-react/craco-less semantic-ui-less --devPackage JSONを書き換え
{ "scripts": { "start": "craco start", "build": "craco build", "test": "craco test", "eject": "craco eject" } }craco.config.jsを作成
module.exports = { plugins: [{ plugin: require('@semantic-ui-react/craco-less') }], }必要なbuildツールをインストール
npx @semantic-ui-react/bootstrapインストールされた新しいフォルダ内にあるtheme.configの設定を変更
src/semantic-ui/theme.config
/******************************* Folders *******************************/ @themesFolder : 'themes'; @siteFolder : '../../src/semantic-ui/site'; /******************************* Import Theme *******************************/ @import (multiple) "~semantic-ui-less/theme.less"; @fontPath : '../../../themes/@{theme}/assets/fonts';Index.jsでimport
import 'semantic-ui-less/semantic.less'font の変更
src/semantic-ui/site/globals/site.variables @headerFont : 'Muli', 'Noto Sans JP', sans-serif; @pageFont : 'Muli', 'Noto Sans JP', sans-serif; @googleFontFamily : 'Noto+Sans+JP:wght@300;400;700|Muli:wght@300;400;700';
- 投稿日:2020-06-25T01:38:34+09:00
node.jsでvercelを使って環境変数を設定するメモ
環境変数設定メモ
vercel では、環境変数を本番環境、プレビュー環境、および開発環境用に簡単に分けることができる。
手順
1. vercle プロジェクト設定の[ 全般]ページ に移動して、[環境変数] セクションを見つける。
2.環境変数の名前を入力する。
たとえば、
Node.js
を使用していて、SENTRY_KEY
という名前の環境変数を作成すると、コード内でprocess.env.SENTRY_KEY
で使用できるようになる。
環境 説明 Production 環境変数が次の本番デプロイメントに適用される。本番デプロイメントを作成するには、デフォルトのブランチにcommitをpushする。 Preview 環境変数は、プレビューデプロイメントに適用される。プレビューデプロイメントは、ブランチ(例えば my-new-feature
)にpushすると作成される。Development 環境変数は、プロジェクトをローカルで、 vercel dev
または任意の開発コマンドで実行するときに使用する。開発環境変数をダウンロードするには、vercel env pull
を実行する。
これだけ!
Enjoy Hacking!
参考
https://vercel.com/docs/v2/build-step?query=envirome#development-environment-variables
- 投稿日:2020-06-25T01:29:24+09:00
iOSで100vhが効かない問題で、楽な方法をNuxt.jsでやってみる
iOS safariで100vhがうまくいかない時に、
window.innerHeight
を取って各要素のstyle属性に一個一個付ける、
というのをやりがちでしたが、楽な方法が無いかと探すとカスタムプロパティが使えるそうなので、
その手順をNuxt.js版で書きます。mixinを作る
--wh
という名前でカスタムプロパティを定義する。
ロード時と、リサイズ時にwindow.innerHeight
を取って--wh
に入れる。mixins/device.jsexport default { data: () => ({ style: { '--wh': '100vh' } }), mounted() { this.$nextTick(() => { this.getWindowSize() window.addEventListener('resize', this.getWindowSize) }) }, methods: { getWindowSize() { this.style['--wh'] = `${window.innerHeight}px` } } }mixinを読み込む
mixinを読み込んで、styleオブジェクトをwrapper要素のstyle属性に入れる。
layouts/default.vue<template lang="pug"> .l-wrap(:style="style") nuxt </template> <script> import deviceMixin from '~/mixins/device' export default { mixins: [deviceMixin] } </script>カスタムプロパティを使う
heightに
var(--wh, 100vh)
と書いて、使用する。
書き方: var(設定したカスタムプロパティ名, プロパティが無い時用の初期値)pages/index.vue<template lang="pug"> .p-top </template> <script> export default {} </script> <style lang="stylus" scoped> .p-top height var(--wh, 100vh) </style>まとめ
これで、iOSのツールバーを考慮した実装が
- wrapperで
window.innerHeight
取って、変数に格納する- wrapperかそれ以下の小要素で
height var(--wh, 100vh)
するという形でほぼCSSで指定出来るようになりました。
IEはまだ未検証ですが、Nuxt.jsの場合は
height var(--wh, 100vh)
とすると、
height 100vh
を自動的に手前に追加してくれるようなので、
varが認識されなくても問題なくvhできるかと思います。<style lang="stylus" scoped> .p-top height 100vh height var(--wh, 100vh) </style>※mixinを使ったのはlayoutsが複数あるときに、各所に同じ処理を入れることになりそうで面倒だと思ったからなので、default.vueでmixinでやってる処理をそのまま書いても問題ないです。