- 投稿日:2020-05-27T23:41:12+09:00
改行のあるjson文字列をjavascriptでパースするときに気を付けること
webアプリに入門しようと思って掲示板を作っていた際に投稿内容をjsonで受け取ってjavascriptでパースしようとした際にコメント内容に改行が含まれていて数時間詰まりました。既出だとは思いますがメモとして書き残しておきます。
(文字列内の "\"をエスケープする必要があります(それを二重の "\"に変換する)。そうしないと、JSONデータではなく、JSONソース内の改行になります。)
サーバー側で
// go言語です s := "{\"name\":\"hoge\",\"comment\":\"fug\r\na\"}" rep := regexp.MustCompile(`[\(\r\n\)\n]`) s = rep.ReplaceAllString(s,"\\n")このように置換することで解決しました。
- 投稿日:2020-05-27T23:14:07+09:00
はじめてのThree.js 4章 日記
新たに知った知識
- multimaterialはジオメトリのそれぞれの面に独自のマテリアルを適応する
- materialにはneedUpdateプロパティがあり、変形していくならばtrueにする必要がある
- sideプロパティはdoubleにすることでパフォーマンスに影響が出てくる
- arrowhelperプロパティで法線ベクトルが可視化できる(サンプルはコメントアウトしてある)
- ゴスパー曲線
気づいたこと
- linebasicmaterialは結構使えそう
- materialプロパティのwireframeはメディアアートっぽくなる
- かっこいい表現求めるならshaderは必須(早く覚えなければ)
wow moment
- multimaterialのルービックキューブ
- 太陽
まだ解決していない点
- idとuuidの違いと使用用途
- 高度なプロパティ
- clippingの使い所
- 今回気づいたけどguiでsphere, cubeなど切り替える際にコードでは用意していたオブジェクトを全部消して新たに作るというのだったけどこれがエラーにならない原因が分からない
spGui.add(controls, 'selectedMesh', ["cube", "sphere", "plane"]).onChange(function (e) { scene.remove(plane); scene.remove(cube); scene.remove(sphere); switch (e) { case "cube": scene.add(cube); break; case "sphere": scene.add(sphere); break; case "plane": scene.add(plane); break; } scene.add(e); });
- 投稿日:2020-05-27T22:10:29+09:00
配列の中にオブジェクトがある場合のデータの入れ替え方法
二つの配列かつ中にオブジェクトがある場合
二つの配列かつ中にオブジェクトがある場合にデータが片方に存在しない場合にデータを入れ替えたいって言う需要ってありますよね。
はい、最近ありました。と言うわけで、結構忘れがちなので、備忘録も込めて記載します。
const ArrayObject1 = [{id:1,name: 'shuichi'},{id:2, name: 'takesi'},{id:3,name: 'tabasa'},] const ArrayObject2 = [{id:1,name: 'ichiro'},{id:2, name: 'ziro'},{id:3,name: 'saburo'},] // idが同じ場合に名前を入れ替える console.log(ArrayObject1.map((obj1) => { if (obj1.id !== null) { ArrayObject2.map((obj2) => { if (obj1.id === obj2.id) { obj1.name = obj2.name } }) return obj1 } }))結果
便利なんだけど、わかりづらいこともある。
参考
- 投稿日:2020-05-27T21:35:08+09:00
[JavaScript] DOM 要素の照合
概要
ある DOM 要素を照合する API の忘備録です。
以下の3メソッドについて解説してます。
- element.contains(element)
- element.closest(selector)
- element.matches(selector)
element.contains(element)
- 要素A と 要素B の親子関係を調べます。
<div id="parent"> <span id="child">8^-^8</div> </div> <span id="friend">8^-^-^8</div>const parentNode = document.getElementById('parent') const childNode = document.getElementById('child') const friendNode = document.getElementById('friend') parentNode.contains(childNode) // true parentNode.contains(friendNode) // false parentNode.contains(parentNode) // true parentNode.contains(null) // false parentNode.contains(undefined) // falseelement.closest(selector)
- ある要素の祖先要素の内、
selector
にマッチする直近要素を返します。<h1>Title</h1> <section class="root"> <div class="message"> <div class="target">8^-^8</div> </div> </section>const targetElement = document.getElementById('target') targetElement.closest('.message') // <div class="message"> targetElement.closest('.root') // <div class="root"> targetElement.closest('h1') // nullelement.matches(selector)
- 指定要素が CSS セレクタにマッチするかどうかを論理値で返します。
<div id="apple">???</div> <div id="banana">???</div> <div id="banana-fresh" class="fresh">???</div>const apple = document.getElementById('apple') const banana = document.getElementById('banana') const bananaFresh = document.getElementById('banana-fresh') apple.matches('#apple') // true apple.matches('#banana') // false banana.matches('#banana') // true banana.matches('#banana.fresh') // false bananaFresh.matches('.fresh') // true bananaFresh.matches('#banana.fresh') // true
- 投稿日:2020-05-27T21:26:52+09:00
JS1日クッキング まとめページ
キューピー3分クッキングのように、お手軽に何か作ってお勉強するシリーズのまとめページです。
コンセプトは、
- JavaScript(or TypeScript)を使う
- 1日前後でお手軽に作れる
- どの記事からでも独立して読めるように努める
です。
サーバー
- 投稿日:2020-05-27T20:57:50+09:00
【JavaScript】JavaScriptの基本構文
学習中に忘れてしまうことがありましたので、書き留めます。
特に初学者の方の参考になればと思います。
JavaScriptES6コンソール出力
nicotine.jsconsole.log("文字列");コメントアウト
nicotine.js//コメント
変数の定義
nicotine.jslet 変数名 = 値1; //値を上書き 変数名 = 値2;定数の定義
nicotine.jsconst 定数名 = 値; //上書きできないテンプレートリテラル
nicotine.js//変数を文字列に連結 console.log(`こんにちは、${変数名}さん`);条件分岐
nicotine.js//if文 if (条件式){ 処理; } ←セミコロン不要 //switch文 switch (条件の値){ case 値1: 処理; break; case 値2: 処理; break; default: 処理 break; }比較演算子
- 等号 ===
- 不等号 !==
繰り返し処理
nicotine.js//while文 while(条件式){ 処理; インクリメント; } //for文 for(変数定義; 条件式; インクリメント){ 処理; }配列
nicotine.js//定義 const 配列名 = [値, 値, 値]; //値の参照 let 変数名 = 配列名[インデックス番号]; //値が無ければundefinedを返す //undefinedは条件分岐ではfalse扱い //配列の長さ 配列名.length;オブジェクト
nicotine.js//定義 const オブジェクト名 = {プロパティ1:値1, プロパティ2:値2}; //値の参照 オブジェクト名.プロパティ //値が無ければundefinedを返す //undefinedは条件分岐ではfalse扱い配列、オブジェクトの組み合わせ
nicotine.js//定義 const 配列名 = [ {プロパティ:値, プロパティ:値}, {プロパティ:値, プロパティ:値} ]; //値の参照 配列名[インデックス番号].プロパティ;関数
nicotine.js//定義 const 関数名 = (引数名)=>{ 処理; }; //オブジェクトに定義 const 定数名 = { プロパティ: ()=>{ 処理; } }; //呼び出し 関数名(引数); //オブジェクトに定義した場合 定数名.プロパティ();クラス
nicotine.js//定義 class クラス名 {} //インスタンス生成 const 定数名 = new クラス名(); //コンストラクタ class クラス名{ constructor(引数){ this.プロパティ = 値; } } //インスタンスのプロパティ参照 インスタンス名.プロパティ //メソッド定義 class クラス名{ constructor(引数){ this.プロパティ = 値; } メソッド名(){ 処理; } } //メソッドの呼び出し インスタンス.メソッド(); //メソッド内でのインスタンスのプロパティ参照 this.プロパティ //クラス継承 class 子クラス名 extends 親クラス名{} //コンストラクタのオーバーライド constructor(引数1, 引数2, 引数3){ super(引数1, 引数2) this.プロパティ = 引数3; }ファイルの分割
nicotine.js//デフォルトエクスポート //クラス export default クラス名; //値 export default 値; //ファイルがインポートされると自動でインポートされる //エクスポート時とインポート時の名前が違っても良い //一つのファイルに一つの値のみ //名前付きエクスポート export{値1,値2}; //デフォルトインポート import 名前(クラス名) from "ファイル"; //名前付きエクスポート import {値1,値2} from "ファイル";パッケージのインポート
nicotine.jsimport 定数名 from "パッケージ名";配列操作
nicotine.js//値の追加 配列名.push(値); //配列繰り返し 配列名.forEach((変数名)=>{ 処理; }); //find(条件に合う一つ目の要素取得) const 定数名 = 配列名.find((変数名)=>{ return 条件式; }); //オブジェクトにも使える //filter(条件に合う要素全ての要素取得) const 配列名 = 配列名.filter((変数名)=>{ return 条件式; }); //配列として取得 //オブジェクトにも使える //map(配列の全ての要素に処理を行い、新しい配列に取得) const 新配列名 = 配列名.map((変数名)=>{ return 処理; });
- 投稿日:2020-05-27T20:45:52+09:00
js - 処理の流れ
DOMとは
DOMはDocument Object Modelの略で、JSからHTMLの情報を取得し、データを作成する仕組みのこと。
このDOMによって解析されたHTMLは階層として捉えられる。
DOMツリー
<body> <header> </header> <div class = 'contents'> <div class = 'left-content'></left-content> <div class = 'right-content'></right-content> </div> <footer> </footer> </body>上記のコードを階層として捉えると
こんな感じ↑
そして階層構造のあるデータをDOMツリーやドキュメントツリーと言い、JSはこのDOMツリーを操作することができる。
DOMツリーの要素である、bodyやheaderなどの要素をそれぞれノードと言い、JSでHTMLを操作する時(表示の変更や、クラスの付与、いわゆるJSで画面に動きを持たせたい時)はまず、このDOMを構成する要素の取得が必要になる。
細かいメソッドなどは割愛するが、流れとしては
①DOMツリーからノードを取得する
↓
②JavaScriptで、取得したノードに対する処理を書く
↓
③HTML側に反映させるといった具合。
また、②の取得したノードに対する処理を実装する上で重要な概念がある。
イベント駆動
取得したノードオブジェクトに対して、処理が行われるきっかけとして、イベントと言う処理要求が起こる。
イベントは、クリック、特定の場所にカーソルを合わせる、ページの読み込みなど、様々だ。
イベントが発火することによって、取得したノードオブジェクトに対し、処理が行われ、画面上に反映される。
例えば、ボタンのクリックをした際に「送信しました」と言うアラートメッセージを表示させたい時↓
#省略 let btn = document.getElementById("submit"); function message() { window.alert("送信しました"); } btn.addEventListener("click", message);まずid名が「submit」のノードオブジェクトの取得を1行目で行い、変数btnに代入。
そして最後の行で取得したノードに対し、クリックイベントが起きた場合、関数messageを行うと書かれている。
関数messageには「送信しました」と言うアラートメッセージを表示すると言う処理を記述。
つまり、取得したノードに対し、何らかのイベントが起きた場合、処理を行うと言うのがイベント駆動の概念であり、JSはイベント駆動の概念を元に作られてる。
- 投稿日:2020-05-27T20:45:52+09:00
DOMについて
DOMとは
DOMはDocument Object Modelの略で、JSからHTMLの情報を取得し、データを作成する仕組みのこと。
このDOMによって解析されたHTMLは階層として捉えられる。
DOMツリー
<body> <header> </header> <div class = 'contents'> <div class = 'left-content'></left-content> <div class = 'right-content'></right-content> </div> <footer> </footer> </body>上記のコードを階層として捉えると
こんな感じ↑
そして階層構造のあるデータをDOMツリーやドキュメントツリーと言い、JSはこのDOMツリーを操作することができる。
DOMツリーの要素である、bodyやheaderなどの要素をそれぞれノードと言い、JSでHTMLを操作する時(表示の変更や、クラスの付与、いわゆるJSで画面に動きを持たせたい時)はまず、このDOMを構成する要素の取得が必要になる。
細かいメソッドなどは割愛するが、流れとしては
①DOMツリーからノードを取得する
↓
②JavaScriptで、取得したノードに対する処理を書く
↓
③HTML側に反映させるといった具合だ。
- 投稿日:2020-05-27T20:38:06+09:00
はじめてのThree.js 3章 日記
新たに知った知識
- webGLにライトのサポートなしなのでゼロからライティング書くとしたらここ
THREE.LensFlare // lightingにlensflareが存在する
- decay ・・・ライトからの距離に応じて光が減衰する量
- penumbra・・・spotlightがどのくらい急速に減速するかの値
- spotlightはtargetが必要
- flareにはtextureが必要
気づいたこと
- 3Dモデリングを経験しているとかなりやりやすい
- lensflareかっこいい
- lensflare2つのテクスチャ使うけど、違いわからなかった
wow moment
- lensflareのところ
まだ解決していない点
- 01ライティング
- 一回突然chromeが落ちたけど演算処理が重かったのかしら
- サンプルが動かなかったのが数回あったが発動条件が謎
- 投稿日:2020-05-27T19:56:00+09:00
GROWI 上で Slack 通知をデフォルトで有効にする
前提
GROWI には Slack に更新情報を通知する機能があります。
通知できるものとしては、
- ページ作成の通知
- ページ更新の通知
- コメント投稿の通知
などがあります。
通知を流したい場合、投稿時に以下のようにチェックボックスをオンにします。
このチェックボックスはデフォルトではオフになっており、通知を流したい時に都度オンにします。
なぜデフォルトで通知を有効にしたいか
- チーム内の情報共有のスピード感を上げたいため。
- 「通知したいときだけ通知」よりも「通知したくない理由がなければ通知」の方がチームのスタイルに合っていそうだった。
- 「Global notification」の設定により漏れなく通知することも可能だが、できれば任意で通知しないことも選択できる「User trigger notification」でやりたかった。
環境
- GROWI 3.8.2-RC
- Chrome 81.0
カスタムスクリプト
管理者権限で「Wiki の管理 →カスタマイズ」を開き、カスタムスクリプトに以下を設定すればデフォルトで有効にできます。
// ページエディター上でチェックボックスを有効にする (ページの新規作成時 or 更新時) function turnOnSlackNotificationOnPageEditor() { const target = document.querySelector('body'); const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.attributeName !== 'class') { return; } const isEditorMode = mutation.target .classList .contains('on-edit'); if (isEditorMode) { const checkbox = document.querySelector('#save-page-controls input[type=checkbox]'); // チェックボックスがオフならオンにする if (!checkbox?.checked) { $(checkbox).closest('label')?.click(); } } }); }); observer.observe(target, { attributes: true }); } // コメントエディター上でチェックボックスを有効にする (コメント投稿時) function turnOnSlackNotificationOnCommentEditor() { const target = document.querySelector('div.page-comments'); if (!target) { return; } const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { for (const node of mutation.addedNodes) { const isElement = (node && node.nodeType === 1); if (!isElement) { continue; } const checkboxes = node.querySelectorAll('.input-group-slack input[type=checkbox]'); checkboxes.forEach(checkbox => { // チェックボックスがオフならオンにする if (!checkbox.checked) { $(checkbox).closest('label')?.click(); } }); } }); }); observer.observe(target, { childList: true, subtree: true }); } $(() => { // ここに読み込みたい関数の名前を列挙する const functions = [ turnOnSlackNotificationOnPageEditor, turnOnSlackNotificationOnCommentEditor, ]; for (const i in functions) { functions[i](); } });エディターは動的に表示される場合があるので MutationObserver でノードの変更を監視し、エディターが表示されたタイミングでチェックボックスをオンにします。
参考にさせていただいた記事(記事作成の経緯)
既に同じ試みをされている記事があります。
こちらの記事ではキー入力を監視し、何か入力される度にチェックボックスをオンにする方法が取られています。
試させてもらったのですが、以下の点が気になりこの記事を書いてみました。
- コメント投稿時でも有効にしたかった。
- 「手動でチェックを外したはずなのにまた付いてる」ということが起こりそうだと思った。
- 試した環境では上手く通知されなかった。
checkbox.checked = true
とするとチェックは付くが、どうやら内部的には有効にならないみたい(バージョンの問題かも?)。$(checkbox).closest('label').click()
のようにして外側の label に対してクリックを発生させたら上手く動いた。
- 投稿日:2020-05-27T19:56:00+09:00
Growi 上で Slack 通知をデフォルトで有効にする
前提
Growi には Slack に更新情報を通知する機能があります。
通知できるものとしては、
- ページ作成の通知
- ページ更新の通知
- コメント投稿の通知
などがあります。
通知を流したい場合、投稿時に以下のようにチェックボックスをオンにします。
このチェックボックスはデフォルトではオフになっており、通知を流したい時に都度オンにします。
なぜデフォルトで通知を有効にしたいか
- チーム内の情報共有のスピード感を上げたいため。
- 「通知したいときだけ通知」よりも「通知したくない理由がなければ通知」の方がチームのスタイルに合っていそうだった。
- 「Global notification」の設定により漏れなく通知することも可能だが、できれば任意で通知しないことも選択できる「User trigger notification」でやりたかった。
環境
- Growi 3.8.2-RC
- Chrome 81.0
カスタムスクリプト
管理者権限で「Wiki の管理 →カスタマイズ」を開き、カスタムスクリプトに以下を設定すればデフォルトで有効にできます。
// ページエディター上でチェックボックスを有効にする (ページの新規作成時 or 更新時) function turnOnSlackNotificationOnPageEditor() { const target = document.querySelector('body'); const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.attributeName !== 'class') { return; } const isEditorMode = mutation.target .classList .contains('on-edit'); if (isEditorMode) { const checkbox = document.querySelector('#save-page-controls input[type=checkbox]'); // チェックボックスがオフならオンにする if (!checkbox?.checked) { $(checkbox).closest('label')?.click(); } } }); }); observer.observe(target, { attributes: true }); } // コメントエディター上でチェックボックスを有効にする (コメント投稿時) function turnOnSlackNotificationOnCommentEditor() { const target = document.querySelector('div.page-comments'); if (!target) { return; } const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { for (const node of mutation.addedNodes) { const isElement = (node && node.nodeType === 1); if (!isElement) { continue; } const checkboxes = node.querySelectorAll('.input-group-slack input[type=checkbox]'); checkboxes.forEach(checkbox => { // チェックボックスがオフならオンにする if (!checkbox.checked) { $(checkbox).closest('label')?.click(); } }); } }); }); observer.observe(target, { childList: true, subtree: true }); } $(() => { // ここに読み込みたい関数の名前を列挙する const functions = [ turnOnSlackNotificationOnPageEditor, turnOnSlackNotificationOnCommentEditor, ]; for (const i in functions) { functions[i](); } });エディターは動的に表示される場合があるので MutationObserver でノードの変更を監視し、エディターが表示されたタイミングでチェックボックスをオンにします。
参考にさせていただいた記事(記事作成の経緯)
既に同じ試みをされている記事があります。
こちらの記事ではキー入力を監視し、何か入力される度にチェックボックスをオンにする方法が取られています。
試させてもらったのですが、以下の点が気になりこの記事を書いてみました。
- コメント投稿時でも有効にしたかった。
- 「手動でチェックを外したはずなのにまた付いてる」ということが起こりそうだと思った。
- 試した環境では上手く通知されなかった。
checkbox.checked = true
とするとチェックは付くが、どうやら内部的には有効にならないみたい(バージョンの問題かも?)。$(checkbox).closest('label').click()
のようにして外側の label に対してクリックを発生させたら上手く動いた。
- 投稿日:2020-05-27T19:43:53+09:00
Nuxt/Content で Markdown なブログ・お知らせサイト作る
Yo 我らすぐブログ作るけど中身更新しなーい
三行
- @Nuxt/Content ってやつの
v1.0.0
が公開された- Nuxt.js で Markdown ブログとかニュースページを作るのが死ぬほど簡単になった
- Markdown ファイルとか JSON から Nuxt.js へ受け渡しをいい感じにやってくれるモジュールだよ
作ってみよう
package.json と nuxt.config.js を準備
package.json{ ... "scripts": { "dev": "nuxt", "generate": "nuxt generate" }, "dependencies": { "@nuxt/content": "^1.0.0", "nuxt": "^2.12.2" } }nuxt.config.js... modules: [ '@nuxt/content' ],レポジトリ構造
content ディレクトリを作って、 md ファイルを入れます。
pages ディレクトリに news ディレクトリを切って、ここに詳細ページと一覧を表示します。% tree . -N . ├── content │ └── news │ ├── 20200526.md │ └── 20200527.md ├── layouts │ └── default.vue ├── node_modules ├── nuxt.config.js ├── package-lock.json ├── package.json ├── pages │ ├── index.vue │ └── news │ ├── _slug.vue │ └── index.vue └── static └── images └── news ├── sample26.png └── sample27.png一覧に表示: content ディレクトリから md を拾ってくる
news/index.vue<template> <div> <div v-for="n in news" :key="n.slug"> <nuxt-link :to="'/news/'+ n.slug">{{n.title}} {{n.date}}</nuxt-link> </div> </div> </template> <script> export default { async asyncData ({ $content, params }) { const query = await $content('news' || 'index').limit(15) const news = await query.fetch() return { news } } } </script>詳細を表示: content ディレクトリから md を拾ってくる
news/_slug.vue<template> <article> <h1>{{news.title}}</h1> <dl> <dt>date</dt> <dd>{{news.date}}</dd> </dl> <div><img :src="news.image" /></div> <div v-for="tag in news.tags" :key="tag">{{tag}}</div> <nuxt-content :document="news" /> </article> </template> <script> export default { async asyncData ({ $content, params }) { const news = await $content('news', params.slug || 'index').fetch() return { news } } } </script>実際に実行してみて、起動できるかチェック
% npm run dev localhost:3000生成もしてみる
以下 generate を nuxt.config.js に追加しておこう (今最新の Nuxt 2.12 までは)
nuxt.config.jsgenerate: { // Nuxt 2.13+ では、これすらいらなくくなるらしい(すごい) async routes () { const { $content } = require('@nuxt/content') const files = await $content('news' || 'index').fetch() return files.map(file => file.path === '/index' ? '/' : file.path) } }% npm run generate > nuxt-content-web@1.0.0 generate /Users/takahashi.nobuhiro/git/web/nuxt-content-web > nuxt generate Entrypoint app = server.js ℹ Generating pages 14:00:07 ✔ Generated /news/20200526 14:00:07 ✔ Generated /news 14:00:07 ✔ Generated / 14:00:07 ✔ Generated /news/20200527 14:00:07動的なファイルも SSG される
上記サンプルソースコード
https://github.com/feb19/nuxt-content-web
ほかなにができるの
- Vue Component を Markdown に埋めるとかできる
- MDX みがある
- Twitter Component 作って埋め込めたり nuxt-link とか書けるのでいいね
- Markdown のホットリロードとかも用意されて快適
- テキスト検索とかも用意されている
- シンタックスハイライトも実装済み
- Hooks 構造体を追加して、カスタムアクションとかも追加できる
- 例えばこの例だと、Markdown ファイルを解析して、「読了時間」を追加するっていうのができる
https://content.nuxtjs.org/advanced#hooksかんそう
- GitHub ベースの Headless CMS アーキテクチャなサイトにめっちゃ良さそう
- いろんな人が Markdown 書けるようになって git の基礎的な操作ができるようになったらいいなぁ
- 投稿日:2020-05-27T19:43:53+09:00
Nuxt/Content で Markdown をブログ・お知らせサイト作る
三行
- @Nuxt/Content ってやつの
v1.0.0
が公開された- Nuxt.js で Markdown ブログとかニュースページを作るのが死ぬほど簡単になった
- Markdown ファイルとか JSON から Nuxt.js へ受け渡しをいい感じにやってくれるモジュールだよ
作ってみよう
package.json と nuxt.config.js を準備
package.json{ ... "scripts": { "dev": "nuxt", "generate": "nuxt generate" }, "dependencies": { "@nuxt/content": "^1.0.0", "nuxt": "^2.12.2" } }nuxt.config.js... modules: [ '@nuxt/content' ],レポジトリ構造
content ディレクトリを作って、 md ファイルを入れます。
pages ディレクトリに news ディレクトリを切って、ここに詳細ページと一覧を表示します。% tree . -N . ├── content │ └── news │ ├── 20200526.md │ └── 20200527.md ├── layouts │ └── default.vue ├── node_modules ├── nuxt.config.js ├── package-lock.json ├── package.json ├── pages │ ├── index.vue │ └── news │ ├── _slug.vue │ └── index.vue └── static └── images └── news ├── sample26.png └── sample27.png一覧に表示: content ディレクトリから md を拾ってくる
news/index.vue<template> <div> <div v-for="n in news" :key="n.slug"> <nuxt-link :to="'/news/'+ n.slug">{{n.title}} {{n.date}}</nuxt-link> </div> </div> </template> <script> export default { async asyncData ({ $content, params }) { const query = await $content('news' || 'index').limit(15) const news = await query.fetch() return { news } } } </script>詳細を表示: content ディレクトリから md を拾ってくる
news/_slug.vue<template> <article> <h1>{{news.title}}</h1> <dl> <dt>date</dt> <dd>{{news.date}}</dd> </dl> <div><img :src="news.image" /></div> <div v-for="tag in news.tags" :key="tag">{{tag}}</div> <nuxt-content :document="news" /> </article> </template> <script> export default { async asyncData ({ $content, params }) { const news = await $content('news', params.slug || 'index').fetch() return { news } } } </script>実際に実行してみて、起動できるかチェック
% npm run dev localhost:3000生成もしてみる
以下 generate を nuxt.config.js に追加しておこう (今最新の Nuxt 2.12 までは)
nuxt.config.jsgenerate: { // Nuxt 2.13+ では、これすらいらなくくなるらしい(すごい) async routes () { const { $content } = require('@nuxt/content') const files = await $content('news' || 'index').fetch() return files.map(file => file.path === '/index' ? '/' : file.path) } }% npm run generate > nuxt-content-web@1.0.0 generate /Users/takahashi.nobuhiro/git/web/nuxt-content-web > nuxt generate Entrypoint app = server.js ℹ Generating pages 14:00:07 ✔ Generated /news/20200526 14:00:07 ✔ Generated /news 14:00:07 ✔ Generated / 14:00:07 ✔ Generated /news/20200527 14:00:07動的なファイルも SSG される
上記サンプルソースコード
https://github.com/feb19/nuxt-content-web
ほかなにができるの
- Vue Component を Markdown に埋めるとかできる
- MDX みがある
- Twitter Component 作って埋め込めたり nuxt-link とか書けるのでいいね
- Markdown のホットリロードとかも用意されて快適
- テキスト検索とかも用意されている
- シンタックスハイライトも実装済み
- Hooks 構造体を追加して、カスタムアクションとかも追加できる
- 例えばこの例だと、Markdown ファイルを解析して、「読了時間」を追加するっていうのができる
https://content.nuxtjs.org/advanced#hooksかんそう
- GitHub ベースの Headless CMS アーキテクチャなサイトにめっちゃ良さそう
- いろんな人が Markdown 書けるようになって git の基礎的な操作ができるようになったらいいなぁ
- 投稿日:2020-05-27T18:42:05+09:00
JavaScript thisの挙動の不思議
thisの挙動や関数スコープのことについて。
実行環境 node v14.2.0
thisの挙動(通常関数)
function hoge() { console.log(this); } function foo() { "use strict"; console.log(this); } hoge(); //globalオブジェクトが出力 foo(); //undefinedが出力ほぼほぼ同じ関数ですが結果が変わってきます。
hoge関数は、globalに紐付けせずに実行してもglobalオブジェクトを参照し出力します。デフォルトでglobalオブジェクトが紐付けされているということです。しかし、strictモードでは関数がどのオブジェクトにも紐付けされていなければ、この値は未定義になります。したがって、strictモードだとデフォルトでオブジェクトの紐付けは行われないということです。
thisの挙動(アロー関数)
console.log(this); //{} が出力 var hoge = () => { console.log(this); }; var bar = () => { "use strict"; console.log(this); }; hoge(); //{} が出力 bar(); //{} が出力ここでの結果は、globalオブジェクトを指すのではなく、関数実行時に内包している親の{}を示しています。
thisの挙動2(アロー関数)
global.name = "Taro"; let helloName = () => { console.log(`hello! ${this.name}`); }; let Hanako = { name: "Hanako", hello: helloName, }; helloName(); //hello! Hanako.hello(); //hello!@shiracamus様にご指摘、コメントいただいたため、整理、修正中。
nodeの場合は上記のように"hello!"しか表示されません。内包している{}が表示されています。しかし、ブラウザであれば"hello! Taro"と表示されます。つまり、globalオブジェクトのnameが呼び出されていることがわかります。
※ブラウザの場合は1行目のglobalをwindowに変更thisはいろいろな場合で値が変わるようでややこしいです。
また、thisの挙動からは少し逸れますがこんなこともやってみます。global.nameをvar宣言に変えてみます。
thisの挙動3(アロー関数)
var name = "Taro"; let helloName = () => { console.log(`hello! ${this.name}`); }; let Hanako = { name: "Hanako", hello: helloName, }; helloName(); //hello! Hanako.hello(); //hello!結果はブラウザ、node共に同じになります。
つまりvarもグローバル変数に値が格納されていることになります。では次に、if文の中で宣言してみます。
thisの挙動4(アロー関数)
if(true) { var name = 'Taro'; } let helloName = () => { console.log(`hello! ${this.name}`); }; let Hanako = { name: "Hanako", hello: helloName, }; helloName(); //hello! Hanako.hello(); //hello!nodeの場合は{}を示しているので何も表示されません。
しかし、ブラウザの場合は同じようにTaroが表示されます。if文の中で宣言してもglobal変数として宣言しているのでnameを使用することができます。次に、実行する場所によってthisの示す値が異なる挙動をみていきます。
こちらは関数の実行する場所が異なることによる挙動の違いが見れます。
Hanakoの場合はHanakoオブジェクトの中で関数を実行しているのでHanakoが出力されます。thisの挙動5(アロー関数)
var name="Taro" let displayName=function(){ console.log(this.name); } let hanako={ "name":"Hanako", "display":displayName } displayName(); // nodeではundefined、 ブラウザはTaroと表示 hanako.display(); //Hanakothisの値を制御してみます。
明示的にバインドしてthisが任意の物を指し示すようにします。thisの挙動6(アロー関数)
var name="Taro" let Hanako ={ "name":"Hanako", } let displayName=function(){ console.log(this.name); } displayName(); //Taro displayName.apply(Hanako); //Hanako displayName.call(Hanako); //Hanako let bindDisplayName= displayName.bind(Hanako); //Hanako bindDisplayName();thisの値を制御しました。
結果apply, call, bindDisplayNameで明示的にバインドしたものはHanakoを指し示すようになっています。
- 投稿日:2020-05-27T18:36:33+09:00
JavaScriptでの非同期通信実装①
- 投稿日:2020-05-27T17:34:13+09:00
【Vue初心者向け】BootstrapVueでシンプルなスライダーをコピペのみで作成してみた
開発環境
BootstrapVueでシンプルなスライダーを導入
これ超簡単なので文字付きのスライダーをVue開発にて導入したい場合は是非覚えといてください
シンプルかつ洗練されたデザインなのでおすすめです
では早速templateタグ内に下記コードをコピペしてください
home.vue<template> <div> <b-carousel id="carousel-1" v-model="slide" :interval="4000" controls indicators background="#ababab" img-width="1024" img-height="480" style="text-shadow: 1px 1px 2px #333;" @sliding-start="onSlideStart" @sliding-end="onSlideEnd" > <!-- Text slides with image --> <b-carousel-slide caption="First slide" text="Nulla vitae elit libero, a pharetra augue mollis interdum." img-src="https://picsum.photos/1024/480/?image=52" ></b-carousel-slide> <!-- Slides with custom text --> <b-carousel-slide img-src="https://picsum.photos/1024/480/?image=54"> <h1>Hello world!</h1> </b-carousel-slide> <!-- Slides with image only --> <b-carousel-slide img-src="https://picsum.photos/1024/480/?image=58"></b-carousel-slide> <!-- Slides with img slot --> <!-- Note the classes .d-block and .img-fluid to prevent browser default image alignment --> <b-carousel-slide> <template v-slot:img> <img class="d-block img-fluid w-100" width="1024" height="480" src="https://picsum.photos/1024/480/?image=55" alt="image slot" > </template> </b-carousel-slide> <!-- Slide with blank fluid image to maintain slide aspect ratio --> <b-carousel-slide caption="Blank Image" img-blank img-alt="Blank image"> <p> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse eros felis, tincidunt a tincidunt eget, convallis vel est. Ut pellentesque ut lacus vel interdum. </p> </b-carousel-slide> </b-carousel> </div> </template>はい次はscriptタグ内に下記コードをコピペです
home.vue<script> export default { data () { return { slide: 0, sliding: null } }, methods: { onSlideStart (slide) { this.sliding = true }, onSlideEnd (slide) { this.sliding = false } } } </script>ブラウザで確認してみてください
どうですか?シンプルでそれなりに洗練されたスライダーができましたね
あとは自分なりにデザインを変更してみてください
以上です
- 投稿日:2020-05-27T17:23:10+09:00
[JS1日クッキング]APIサーバーにJestでユニットテストをする
何かを簡単に作って、ちょっとした勉強になる。そんなシリーズになる予定のものの第2回です。
今回は、シンプルなAPIサーバーをJestでユニットテストをします。
以下のコマンドで、以前の記事で作ったAPIサーバーを用意できます。
$ git clone git@github.com:kei-lvngbk/sequelize-todo-api-server.git $ git checkout base完成品はこちら -> sequelize-todo-api-server
材料
作り方
1. ライブラリのインストール
Jestをインストールします。
npm i -D jest
そして、npm-scriptsにtestコマンドを書きます。
package.json{ ... "scripts": { "test": "export NODE_ENV=test && jest" }, ... }
NODE_ENV=test
と環境変数を設定しているのは、環境変数で使用するデータベースをテスト用に切り替えるためです。Sequelizeは環境変数NODE_ENVで使用するデータベースを切り替えられます。例えば、config/config.jsonが、
config/config.json{ "development": { ... }, "test": { "username": "sequelize", "password": "sequepass", "database": "database_development", "host": "127.0.0.1", "dialect": "mysql" }, "production": { ... } }のようなとき、NODE_ENVをtestにすればconfig/config.jsonのtestの部分に書かれているのデータベースを使用でき、productionにすればproductionの部分に書かれているデータベースを使用できます。NODE_ENVを設定していない場合は、デフォルトでdevelopmentのデータベースを使用します。
2. テストを書く
実際にテストを書いていきます。Jestがテストコードを発見できるように、__tests__ディレクトリ内に書いていきます。create関数のテストコードは、最終的には以下のようになります。
__tests__/db.test.jsconst { execSync } = require("child_process"); const { sequelize } = require("../models"); const db = require("../lib/db"); beforeAll(() => { execSync("npx sequelize-cli db:create && npx sequelize-cli db:migrate"); }); afterAll(async () => { await sequelize.close(); execSync("npx sequelize-cli db:drop"); }); beforeEach(() => { execSync("npx sequelize db:seed:all"); }); afterEach(() => { execSync("npx sequelize db:seed:undo:all"); }); describe("create関数", () => { test("書き込みができて、書き込み内容が一致する", async () => { const title = "潮干狩り"; const todo = await db.create(title, false); expect(todo.title).toBe(title); expect(todo.completed).toBeFalsy(); }); test("引数が0個で、書き込みができない", async () => { expect.assertions(1); try { await db.create(); } catch (e) { expect(e).toBeTruthy(); } }); test("引数が1個で、書き込みができない", async () => { expect.assertions(1); try { await db.create("潮干狩り"); } catch (e) { expect(e).toBeTruthy(); } }); test("titleの型が文字列ではない", async () => { expect.assertions(1); try { await db.create(123, false); } catch (e) { expect(e).toBeTruthy(); } }); test("completedの型が真理値ではない", async () => { expect.assertions(1); try { await db.create("潮干狩り", "カラオケ"); } catch (e) { expect(e).toBeTruthy(); } }); }); ...まず、すべてのテストの開始前にすること、終了後にすることを書きます。ここでは、すべてのテスト前にデータベースを作成し、すべてのテストが終了したらデータベースを削除するようにします。
const { execSync } = require("child_process"); const { sequelize } = require("../models"); ... beforeAll(() => { execSync("npx sequelize-cli db:create && npx sequelize-cli db:migrate"); }); afterAll(async () => { await sequelize.close(); execSync("npx sequelize-cli db:drop"); });すべてのテストが終了したとき、データベースの削除だけでなく、
sequelize.close()
でデータベースへの接続も閉じています。データベースへの接続を閉じないと、Jestは非同期処理が残り続けているという警告文を表示してJestが終了しなくなるため、ここでデータベースへの接続を閉じています。次に、それぞれのテストの開始前、終了後にする処理を書きます。前回用意した開発用のデータをそのままテスト用のデータとして使います。このデータをテスト前にセットし、テスト後に削除します。
beforeEach(() => { execSync("npx sequelize db:seed:all"); }); afterEach(() => { execSync("npx sequelize db:seed:undo:all"); });
describe()
でテストするもののグループを作り、test()
にテストを書いていきます。ここから先は、以下の2つのテストについてみていきます。describe("create関数", () => { test("書き込みができて、書き込み内容が一致する", async () => { const title = "潮干狩り"; const todo = await db.create(title, false); expect(todo.title).toBe(title); expect(todo.completed).toBeFalsy(); }); test("引数が0個で、書き込みができない", async () => { expect.assertions(1); try { await db.create(); } catch (e) { expect(e).toBeTruthy(); } }); ... });通常は、"書き込みができて、書き込み内容が一致する"のテストのように、実際の値と想定している値とが一致するようにテストを書きます。
"引数が0個で、書き込みができない"のテストのように、非同期処理の途中で例外が発生することをテストする場合は、
- 何回アサーション(expect()~の文)が実行されるか
expect.assertions()
を書く- 非同期処理の例外処理でエラーが存在するかチェックする
という手順でテストを書けばOKです。1.でアサーションの回数をチェックしないと、エラーが発生しない場合は、アサーションが実行されず、そのままテストが通ってしまうので、忘れずに
expect.assertions()
でチェックしましょう。3. テストを実行する
テストを実行します。
$ npm run test > sequelize-todo-api-server@1.0.0 test /home/kei/Programming/Project/temp/sequelize-todo-api-server > export NODE_ENV=test && jest console.log Executing (default): INSERT INTO `Todos` (`id`,`title`,`completed`,`createdAt`,`updatedAt`) VALUES (DEFAULT,?,?,?,?); at Sequelize.log (node_modules/sequelize/lib/sequelize.js:1187:15) console.log Executing (default): INSERT INTO `Todos` (`id`,`createdAt`,`updatedAt`) VALUES (DEFAULT,?,?); at Sequelize.log (node_modules/sequelize/lib/sequelize.js:1187:15) FAIL __tests__/db.test.js (15.021 s) create関数 ✓ 書き込みができて、書き込み内容が一致する (3479 ms) ✕ 引数が0個で、書き込みができない (3248 ms) ● create関数 › 引数が0個で、書き込みができない expect.assertions(1) Expected one assertion to be called but received zero assertion calls. 31 | 32 | test("引数が0個で、書き込みができない", async () => { > 33 | expect.assertions(1); | ^ 34 | try { 35 | await db.create(); 36 | } catch (e) { at Object.<anonymous> (__tests__/db.test.js:33:12) Test Suites: 1 failed, 1 total Tests: 1 failed, 1 passed, 2 total Snapshots: 0 total Time: 17.385 s, estimated 25 s Ran all test suites. npm ERR! code ELIFECYCLE npm ERR! errno 1 npm ERR! sequelize-todo-api-server@1.0.0 test: `export NODE_ENV=test && jest` npm ERR! Exit status 1 npm ERR! npm ERR! Failed at the sequelize-todo-api-server@1.0.0 test script. npm ERR! This is probably not a problem with npm. There is likely additional logging output above. npm ERR! A complete log of this run can be found in: npm ERR! /home/kei/.npm/_logs/2020-05-27T07_20_39_355Z-debug.log"引数が0個で、書き込みができない"でテストに失敗しました。Jestの出力をみていくと、
expect.assertions(1) Expected one assertion to be called but received zero assertion calls.
expect.assertions(1)
でテストに失敗したようです。どうやら、エラーが発生せず、そのまま実行できてしまったようです。4. コードを修正する
引数がない状態で
create()
を実行して、テーブルではどのようなデータが書き込まれるかみてみましょう。npx sequelize-cli db:create
とnpx sequelize-cli db:migrate
でテーブルを用意してから、const db = require("./lib/db"); const createNoArgs = async () => { await db.create(); }; createNoArgs();を実行します。すると、テーブルは以下のようになります。
mysql> use database_development; Database changed mysql> select * from Todos; +----+-------+-----------+---------------------+---------------------+ | id | title | completed | createdAt | updatedAt | +----+-------+-----------+---------------------+---------------------+ | 1 | NULL | NULL | 2020-05-27 07:38:34 | 2020-05-27 07:38:34 | +----+-------+-----------+---------------------+---------------------+ 1 row in set (0.00 sec)titleとcompletedにNULLが書き込まれています。実際にTodoリストアプリに使う場合、NULLが返ってきたら不便です。なので、NULLは書き込みできないようにします。
Sequelizeでは、モデルを生成すると、そのモデルの詳細はmodelsディレクトリ内のファイルに書き込まれます。前回、Todoでモデルを作っているので、models/Todo.jsを覗くと、
models/Todo.js"use strict"; module.exports = (sequelize, DataTypes) => { const Todo = sequelize.define( "Todo", { title: DataTypes.STRING, completed: DataTypes.BOOLEAN, }, {} ); Todo.associate = function (models) { // associations can be defined here }; return Todo; };となっています。
sequelize.define()
でテーブルを作成しています。titleは文字列、completedは真理値になるように作成しています。この場合だと、NULLでもOKな状態でテーブルを作っているので、この部分をconst Todo = sequelize.define( "Todo", { title: { type: DataTypes.STRING, allowNull: false }, completed: { type: DataTypes.BOOLEAN, allowNull: false }, }, {} );というように書き換えて、NULLは書き込めないようにします。
npx sequelize-cli db:drop
でデータベースを削除してから、テストを実行します。$ npm run test > sequelize-todo-api-server@1.0.0 test /home/kei/Programming/Project/temp/sequelize-todo-api-server > export NODE_ENV=test && jest PASS __tests__/db.test.js (14.684 s) create関数 ✓ 書き込みができて、書き込み内容が一致する (3468 ms) ✓ 引数が0個で、書き込みができない (3172 ms) console.log Executing (default): INSERT INTO `Todos` (`id`,`title`,`completed`,`createdAt`,`updatedAt`) VALUES (DEFAULT,?,?,?,?); at Sequelize.log (node_modules/sequelize/lib/sequelize.js:1187:15) Test Suites: 1 passed, 1 total Tests: 2 passed, 2 total Snapshots: 0 total Time: 17.077 s Ran all test suites.テストが無事通りました。
5. すべてのテストを書いて、実行する
このように、テストを書く→コードを修正する→テストを書く→...と繰り返していくと、最終的には以下のようになります。余分な出力は予め省いています。
$ npm run test > sequelize-todo-api-server@1.0.0 test /home/kei/Programming/Project/temp/sequelize-todo-api-server > export NODE_ENV=test && jest PASS __tests__/db.test.js (82.44 s) create関数 ✓ 書き込みができる (3427 ms) ✓ 引数が0個で、書き込みができない (3288 ms) ✓ 引数が1個で、書き込みができない (3249 ms) ✓ titleの型が文字列ではない (3264 ms) ✓ completedの型が真理値ではない (3308 ms) read関数 ✓ 読み込みができる (3254 ms) ✓ 読み込み内容が一致する(idは無視) (3218 ms) update関数 ✓ 更新ができる (3215 ms) ✓ 更新時間が更新されている (4405 ms) ✓ 内容が更新されている (3607 ms) ✓ 引数が0個で、更新ができない (3169 ms) ✓ 引数が1個で、更新ができない (3501 ms) ✓ 引数が2個で、更新ができない (3625 ms) ✓ 指定したidのデータが存在しないので、更新ができない (3335 ms) ✓ 引数のidの型が違うので、更新ができない (3251 ms) ✓ 引数のtitleの型が違うので、更新ができない (3550 ms) ✓ 引数のcompletedの型が違うので、更新ができない (4048 ms) delete関数 ✓ 削除できる (3722 ms) ✓ 引数が0個で、削除ができない (3621 ms) ✓ 引数のidの型が違うので、削除ができない (4458 ms) ✓ 指定したidのデータが存在しないので、削除ができない (3230 ms) Test Suites: 1 passed, 1 total Tests: 21 passed, 21 total Snapshots: 0 total Time: 84.814 s Ran all test suites.すべてのテストが通りました。
テストの数が思っていたより多くなりました。テストを書いていて思ったことですが、引数の個数と型の違いはTypeScript使うとテストを書く必要がないような気がします。つまり、TS使えってことですかね。
おわりに
下の記事を参考にsequelizeで作ったAPIサーバーのユニットテストをしました。
Sequelize + Jest のユニットテスト環境構築手順 - Qiita
テストを書いていくと、最初書いていたコードじゃちょっとダメな部分もみえてきて、テストを書く→コードを修正する→テストを書く→...と繰り返していくうちに、より良いコードになることが実感できます。それと、例外処理と非同期処理のテストの書き方でちょっと戸惑いましたが、Jestは慣れればとても便利でいいライブラリだと思いました。
次回は、変更がなければCircleCIで自動テストをする予定です。
コード -> sequelize-todo-api-server
- 投稿日:2020-05-27T17:07:25+09:00
【Vue/コピペでOK】 ページ遷移アニメーション(フェードアウト・イン)を実装する方法
開発環境
実際のコード
app.vueのstyleタグ内に下記コードをコピペしましょう
app.vue.v-enter { transform: translate(-100px, 0); opacity: 0; } .v-enter-to { opacity: 1; } .v-enter-active { transition: all 1s 0s ease; } .v-leave { transform: translate(0, 0); opacity: 1; } .v-leave-to { transform: translate(100px, 0); opacity: 0; } .v-leave-active { transition: all .5s 0s ease; }これで気持ちよく右にフェードアウトして左からフェードインするアニメーションが導入できたかと思います
他にも左にフェードアウトして右からフェードインするようなコードもあるのですが
複雑になるしあまり気持ちの良いアニメーションではないので今回は割愛させてもらいます。
以上です。
参考記事(templateのコードなどより詳しく記述してあります)
【Vue開発】ページ遷移CSSアニメーション(フェードアウト・イン)をコピペで実装する方法
- 投稿日:2020-05-27T16:37:55+09:00
Javascriptで電話番号のバリデーションと変換(E.164)
今回したいこと
- 日本の電話番号(03-1234-5678)などを、E.164表記(+81312345678)に変換したい
- ついでにバリデーションも出来るといいかも
環境
- Node.js
ライブラリ
Googleが提供している電話番号をバリデーションと自動フォーマット可能なlibphonenumberのnpm版であるgoogle-libphonenumberを使用します。
google-libphonenumberの使用方法
インストール
npm install google-libphonenumber例1)'202-456-1414'を'US'のE164表記に変換
// Require `PhoneNumberFormat`. const PNF = require('google-libphonenumber').PhoneNumberFormat; // Get an instance of `PhoneNumberUtil`. const phoneUtil = require('google-libphonenumber').PhoneNumberUtil.getInstance(); // Parse number with country code and keep raw input. const number = phoneUtil.parseAndKeepRawInput('202-456-1414', 'US'); // Format number in the E164 format. console.log(phoneUtil.format(number, PNF.E164)); // => +12024561414例2)全角・半角混合の日本の電話番号をE164表記に変換
// Require `PhoneNumberFormat`. const PNF = require('google-libphonenumber').PhoneNumberFormat; // Get an instance of `PhoneNumberUtil`. const phoneUtil = require('google-libphonenumber').PhoneNumberUtil.getInstance(); const phoneNumbers = [ '03―1111―2222', '03 1111 2222', '(03)1111-2222', '0312345678', '03 1234 5678', '03-1234-5678', '(03)-1234-5678', '09012345678', '090-1234-5678', '09012345678', '090―1234―5678', '0120-123-123' ] const replacedNumbers = phoneNumbers.map(phoneNumber => { const replacedNumber = formatNumber(phoneNumber); return replacedNumber; }) function formatNumber(str){ const number = phoneUtil.parseAndKeepRawInput(str, 'JP'); return phoneUtil.format(number, PNF.E164); } console.log(replacedNumbers); // =>["+81311112222","+81311112222","+81311112222","+81312345678","+81312345678","+81312345678","+81312345678","+819012345678","+819012345678","+819012345678","+819012345678","+81120123123"]その他の使い方
READMEをそのままのっけています。
バリデーションも出来そうです。// Require `PhoneNumberFormat`. const PNF = require('google-libphonenumber').PhoneNumberFormat; // Get an instance of `PhoneNumberUtil`. const phoneUtil = require('google-libphonenumber').PhoneNumberUtil.getInstance(); // Parse number with country code and keep raw input. const number = phoneUtil.parseAndKeepRawInput('202-456-1414', 'US'); // Print the phone's country code. console.log(number.getCountryCode()); // => 1 // Print the phone's national number. console.log(number.getNationalNumber()); // => 2024561414 // Print the phone's extension. console.log(number.getExtension()); // => // Print the phone's extension when compared to i18n.phonenumbers.CountryCodeSource. console.log(number.getCountryCodeSource()); // => FROM_DEFAULT_COUNTRY // Print the phone's italian leading zero. console.log(number.getItalianLeadingZero()); // => false // Print the phone's raw input. console.log(number.getRawInput()); // => 202-456-1414 // Result from isPossibleNumber(). console.log(phoneUtil.isPossibleNumber(number)); // => true // Result from isValidNumber(). console.log(phoneUtil.isValidNumber(number)); // => true // Result from isValidNumberForRegion(). console.log(phoneUtil.isValidNumberForRegion(number, 'US')); // => true // Result from getRegionCodeForNumber(). console.log(phoneUtil.getRegionCodeForNumber(number)); // => US // Result from getNumberType() when compared to i18n.phonenumbers.PhoneNumberType. console.log(phoneUtil.getNumberType(number)); // => FIXED_LINE_OR_MOBILE // Format number in the E164 format. console.log(phoneUtil.format(number, PNF.E164)); // => +12024561414 // Format number in the original format. console.log(phoneUtil.formatInOriginalFormat(number, 'US')); // => (202) 456-1414 // Format number in the national format. console.log(phoneUtil.format(number, PNF.NATIONAL)); // => (202) 456-1414 // Format number in the international format. console.log(phoneUtil.format(number, PNF.INTERNATIONAL)); // => +1 202-456-1414 // Format number in the out-of-country format from US. console.log(phoneUtil.formatOutOfCountryCallingNumber(number, 'US')); // => 1 (202) 456-1414 // Format number in the out-of-country format from CH. console.log(phoneUtil.formatOutOfCountryCallingNumber(number, 'CH')); // => 00 1 202-456-1414(おまけ)ライブラリを使わないでやってみた
最初はgoogle-libphonenumberを知らなかったので、正規表現でやってみました。
const phoneNumbers = [ '03―1111―2222', '03 1111 2222', '(03)1111-2222', '0312345678', '03 1234 5678', '03-1234-5678', '(03)-1234-5678', '09012345678', '090-1234-5678', '09012345678', '090―1234―5678', '0120-123-123' ] const replacedNumbers = phoneNumbers.map(phoneNumber => { const replacedNumber = toE164Number(phoneNumber); return replacedNumber; }) function toE164Number(strVal){ // 半角変換 const halfVal = strVal.replace(/[!-~]/g, function( tmpStr ) { // 文字コードをシフト return String.fromCharCode( tmpStr.charCodeAt(0) - 0xFEE0 ); } ); console.log("halfVal" ,halfVal); // 文字の変換 const number = phoneUtil.parseAndKeepRawInput(halfVal, 'JP'); console.log(number) console.log(phoneUtil.format(number, PNF.E164)); return halfVal.replace(/[\-\s+()-―]/gi, '').replace('0','+81'); }最後に
google-libphonenumberはかなり便利だなと思いました。
ただ電話番号を変換するときにJPなどの国コードを指定しないといけないので、素の電話番号形式から国コードを判定するものがあればいいんですけどね。。
- 投稿日:2020-05-27T16:37:55+09:00
Javascriptで電話番号のバリデーションと変換(E.164)に便利なライブラリの紹介
今回したいこと
- 日本の電話番号(03-1234-5678)などを、E.164表記(+81312345678)に変換したい
- ついでにバリデーションも出来るといいかも
環境
- Node.js
ライブラリ
Googleが提供している電話番号をバリデーションと自動フォーマット可能なlibphonenumberのnpm版であるgoogle-libphonenumberを使用します。
google-libphonenumberの使用方法
インストール
npm install google-libphonenumber例1)'202-456-1414'を'US'のE164表記に変換
// Require `PhoneNumberFormat`. const PNF = require('google-libphonenumber').PhoneNumberFormat; // Get an instance of `PhoneNumberUtil`. const phoneUtil = require('google-libphonenumber').PhoneNumberUtil.getInstance(); // Parse number with country code and keep raw input. const number = phoneUtil.parseAndKeepRawInput('202-456-1414', 'US'); // Format number in the E164 format. console.log(phoneUtil.format(number, PNF.E164)); // => +12024561414例2)全角・半角混合の日本の電話番号をE164表記に変換
// Require `PhoneNumberFormat`. const PNF = require('google-libphonenumber').PhoneNumberFormat; // Get an instance of `PhoneNumberUtil`. const phoneUtil = require('google-libphonenumber').PhoneNumberUtil.getInstance(); const phoneNumbers = [ '03―1111―2222', '03 1111 2222', '(03)1111-2222', '0312345678', '03 1234 5678', '03-1234-5678', '(03)-1234-5678', '09012345678', '090-1234-5678', '09012345678', '090―1234―5678', '0120-123-123' ] const replacedNumbers = phoneNumbers.map(phoneNumber => { const replacedNumber = formatNumber(phoneNumber); return replacedNumber; }) function formatNumber(str){ const number = phoneUtil.parseAndKeepRawInput(str, 'JP'); return phoneUtil.format(number, PNF.E164); } console.log(replacedNumbers); // =>["+81311112222","+81311112222","+81311112222","+81312345678","+81312345678","+81312345678","+81312345678","+819012345678","+819012345678","+819012345678","+819012345678","+81120123123"]その他の使い方
READMEをそのままのっけています。
バリデーションも出来そうです。// Require `PhoneNumberFormat`. const PNF = require('google-libphonenumber').PhoneNumberFormat; // Get an instance of `PhoneNumberUtil`. const phoneUtil = require('google-libphonenumber').PhoneNumberUtil.getInstance(); // Parse number with country code and keep raw input. const number = phoneUtil.parseAndKeepRawInput('202-456-1414', 'US'); // Print the phone's country code. console.log(number.getCountryCode()); // => 1 // Print the phone's national number. console.log(number.getNationalNumber()); // => 2024561414 // Print the phone's extension. console.log(number.getExtension()); // => // Print the phone's extension when compared to i18n.phonenumbers.CountryCodeSource. console.log(number.getCountryCodeSource()); // => FROM_DEFAULT_COUNTRY // Print the phone's italian leading zero. console.log(number.getItalianLeadingZero()); // => false // Print the phone's raw input. console.log(number.getRawInput()); // => 202-456-1414 // Result from isPossibleNumber(). console.log(phoneUtil.isPossibleNumber(number)); // => true // Result from isValidNumber(). console.log(phoneUtil.isValidNumber(number)); // => true // Result from isValidNumberForRegion(). console.log(phoneUtil.isValidNumberForRegion(number, 'US')); // => true // Result from getRegionCodeForNumber(). console.log(phoneUtil.getRegionCodeForNumber(number)); // => US // Result from getNumberType() when compared to i18n.phonenumbers.PhoneNumberType. console.log(phoneUtil.getNumberType(number)); // => FIXED_LINE_OR_MOBILE // Format number in the E164 format. console.log(phoneUtil.format(number, PNF.E164)); // => +12024561414 // Format number in the original format. console.log(phoneUtil.formatInOriginalFormat(number, 'US')); // => (202) 456-1414 // Format number in the national format. console.log(phoneUtil.format(number, PNF.NATIONAL)); // => (202) 456-1414 // Format number in the international format. console.log(phoneUtil.format(number, PNF.INTERNATIONAL)); // => +1 202-456-1414 // Format number in the out-of-country format from US. console.log(phoneUtil.formatOutOfCountryCallingNumber(number, 'US')); // => 1 (202) 456-1414 // Format number in the out-of-country format from CH. console.log(phoneUtil.formatOutOfCountryCallingNumber(number, 'CH')); // => 00 1 202-456-1414(おまけ)ライブラリを使わないでやってみた
最初はgoogle-libphonenumberを知らなかったので、正規表現でやってみました。
const phoneNumbers = [ '03―1111―2222', '03 1111 2222', '(03)1111-2222', '0312345678', '03 1234 5678', '03-1234-5678', '(03)-1234-5678', '09012345678', '090-1234-5678', '09012345678', '090―1234―5678', '0120-123-123' ] const replacedNumbers = phoneNumbers.map(phoneNumber => { const replacedNumber = toE164Number(phoneNumber); return replacedNumber; }) function toE164Number(strVal){ // 半角変換 const halfVal = strVal.replace(/[!-~]/g, function( tmpStr ) { // 文字コードをシフト return String.fromCharCode( tmpStr.charCodeAt(0) - 0xFEE0 ); } ); console.log("halfVal" ,halfVal); // 文字の変換 const number = phoneUtil.parseAndKeepRawInput(halfVal, 'JP'); console.log(number) console.log(phoneUtil.format(number, PNF.E164)); return halfVal.replace(/[\-\s+()-―]/gi, '').replace('0','+81'); }最後に
google-libphonenumberはかなり便利だなと思いました。
ただ電話番号を変換するときにJPなどの国コードを指定しないといけないので、素の電話番号形式から国コードを判定するものがあればいいんですけどね。。
- 投稿日:2020-05-27T16:08:06+09:00
iOS13.5 Safari でページがスクロールできないバグの解消
iOS13.5バグ発生
複雑なコードの一部のため、細かい発生条件の切り分けまでできていませんが…
高さを指定したdiv
にoverflow:auto
指定をして、画面内でスクロールするコンテンツが、OSアップデートしたとたん動かなくなりました。解決法
いろいろ試し、結果として、以下が判明しました。
- CSSのoverflow
の値を再定義してやればスクロールできるようになる。
- ただし、2度同じ再定義をしても、以後はスクロールできるようにならない。※
div
の中のHTMLを動的に書き換えるコンテンツだったため、スクロールが必要だったり必要なかったりします。コード
結果として、すごくびみょうな方法ですが…値として
auto
とscroll
を交互に入れ替える方法で解決しました。//表示されてから実行するために、setTimeoutで遅延。 setTimeout(function(){ if($(target).css('overflow')=='auto'){ $(target).css('overflow','scroll'); }else{ $(target).css('overflow','auto'); } },100);
- 投稿日:2020-05-27T15:36:06+09:00
Rails でJavaScriptが動作しない…?
railsでJavaScriptを使おうとした際に少しハマったのでメモ
現象
jsファイルを作成し、コードを書いているのにブラウザのコンソール画面が反応しない。
↓ jsファイル
posts.js$(function(){ console.log("OK") })原因調査
jqueryの設定にミスがあるかと思ってファイルを確認しても特にミスは見つからず…
というかコンソール画面でエラーすら出ないということはjsファイルが読み込まれていない?
ということでGoogle先生に聞き込み結果
やはりjsファイルが読み込まれていませんでした。
原因はコントローラ作成時に生成されるcoffeeファイル
このファイルと同じ名前でjsファイルがあるとcoffeeファイルが優先して読み込まれるためjsファイルが読み込まれないということでした。coffeeファイルを消してリロードしてみると
ちゃんとコンソール画面にOKと表示されました!coffeeファイルが何に使われるのか、なぜコントローラ作成時に自動で作られるのか謎ですが、coffeeファイルは基本削除するようにしておきます。
(自動生成されるくらいなので割と重要なファイルな気もしますが…)間違いあれば指摘してください。
- 投稿日:2020-05-27T15:14:56+09:00
jQueryでチェックされた要素を取り出す方法
- 投稿日:2020-05-27T15:04:52+09:00
jQueryでフォームを取得する方法
フォームの情報を取得するために
結論、submitというイベントを使います。
submitイベントはフォームが送信された時に呼び出されます。
以下のようなウェブページを準備しました。この送信ボタンを押した際の流れをみていきたいので
以下のようなコードを施しています。$(function() { $("form").on("submit", function() { console.log("送信ボタンが押されました"); }); });これで送信ボタンが押された際にコンソール画面に
「送信ボタンが押されました」と表示されるはずです。
しかし、実際にやってみると一瞬で表示されて、
一瞬にして消えてしまうはずです。preventDefault()
消えてしまう原因を考えてみましょう。
送信ボタンを押した後、ウェブページがリロードされてしまうため
初期の状態に戻ってしまうからです。
「この初期の状態に戻る」というイベントをキャンセルしたいため、preventDefault()を用います。
なので以下のコードにしてみましょう$(function() { $('form').on('submit', function(a) { console.log('送信ボタンが押されました'); a.preventDefault(); }); });この関数の流れは引数aにpreventDefault()を入れています。
この中身をもっと詳しくみるためにconsole.logを使ってみていきます。
以下、検証画面です。
送信しましたの下のところにconsole.log(a)の結果が書いています。
これをイベントオブジェクトと言うのですがイベントの発生元の要素や押されたキーの種類などを知ることができます。
- 投稿日:2020-05-27T14:36:21+09:00
ReactでHTMLヘッダを変更する
概要
react-helmet
を利用する。
どこからでもHTMLヘッダを変更できる。使い方
導入
yarn add react-helmet @types/react-helmetタイトルの変更
import React from 'react'; import { Helmet } from 'react-helmet'; const title = 'Help'; const Help = () => ( <> <Helmet> <title>{title}</title> </Helmet> <h1>{title}</h1> <p> This is Help page ... </p> </> ); export default Help;まとめ
簡単に変更できる。
SPAとかで、ページによってタイトルを変更したいときに便利。
他にも変更できるので、以下を確認する。
https://github.com/nfl/react-helmet
- 投稿日:2020-05-27T14:26:23+09:00
WSlで,Vue.jsによるアンドロイドアプリ開発環境を構築しようとしたときにハマった
備忘録
【Vue.js】WSL 上で Vue.js を用いた Android アプリを開発する
こちらを用いて,よっしゃ開発環境作るどーーと意気込んでいたら…sudo $ANDROID_HOME/tools/bin/sdkmanager "tools" "emulator" "platform-tools" "platforms;android-28" "build-tools;28.0.3" "extras;android;m2repository" "extras;google;m2repository" Error: Could not find or load main class com.android.sdklib.tool.sdkmanager.SdkManagerCliとのエラーが出た.調べていると,JDKが8よりも新しいものを使うとうまくいかないとのこと.この段階で最新版のjava14を入れていた.
入れなおしてもう一度!としたが変わらなかった.いろいろ探しまくった結果,以下でsdkmanagerのバージョンを古くするといけた.
エラー:メインクラスcom.android.sdklib.tool.sdkmanager.SdkManagerCli #5304が見つからないか、読み込めませんでした
https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip
結局これでダウンロードしたtoolsに置き換えるとうまくいった.めっちゃ悩んだのにあっけない…
というか,最新のを使う方法はないんでしょうか?
- 投稿日:2020-05-27T14:09:25+09:00
three.jsのloaderからverticesを抜き出そうと四苦八苦したこと
結論
配列.array[index]
背景
Three.jsでPLYオブジェクトをロードして
その頂点を取得したかった。
あとjsは10年ぶりぐらいに触ったので文法周りはすべて飛んでます。罠1:バージョン
githubのPLYLoaderと使ってたthree.minのバージョンがあってなくて
setattribute→addattributeに代わってて書き換えた罠2:ローダーの内部構造
てっきり3Dオブジェクト格納の仕方は同じだと思ってたら
buffer格納するときにFloat32Arrayに詰め込まれてて
lengthじゃなくてcountだった罠3:配列アクセス
配列[index]で何故か取得できなくてundefinedになってて
けどデバッグからはFloat32Arrayの中身が見えてて
12時間悩んでstackoverflowで全く同じ質問見付けて
配列.array[index]で取れた。こういう書き方だったっけ?ともかく取れたのでよし。
- 投稿日:2020-05-27T14:07:45+09:00
JavaScriptのロゴを調べた話(そしてわからなかった話)
謎ロゴとの出会い
先日、YouTubeでプログラミング関係の動画を見ていたらJavaScriptの話題とともにこんな画像が出てきました。
これを見て僕は「あちゃー、JavaとJavaScriptを混同しちゃってるのね 初心者よくある、どんまい」と思い、コメント欄に「JavaとJavaScriptは別物ですよ?」と書き込み、送信ボタンを押そうとしたその瞬間、突然嫌な予感がしてその手を止めました。
(いや、動画内で一瞬表示させるためにJavaのアイコンとテキストを組み合わせてロゴを作ったとは思えないし、このロゴは本当に存在しているのでは…?)
ググってみたら案の定、素材として配布されているロゴでした。
JavaScriptの公式ロゴ
現在、JavaScriptに公式ロゴは存在しません。(そもそも現在の「公式」というのがEcma InternationalなのかMozillaなのかJavaScriptの商標を持っているSun/Oracleなのかという話もあるのですが)
よく見かける、黄色地に黒文字でJSと書かれているロゴは「JS Logo By The Community」です。
盾っぽいロゴはW3CによるHTML5のロゴに合わせて同じスタイルでCSS3とJSが作られたものだと思われます。(W3CがCSS3のアイコンを提供しているにもかかわらず、別でCSS3の盾デザインが存在しているので、その流れでJSも作られたのではと思っているのですが、ソース無しなので自信ないです)なのでこの謎ロゴが存在する理由として以下のものが考えられます。
- 過去にいずれかの公式だったもの(あるいはそれを加工したもの)
- JavaScriptコミュニティ向けなど何かしら真面目な理由で作られたもの
- Java≠JavaScriptのパロディとして作られたもの
1はかなり大穴ですが、Java人気にあやかってLiveScriptがJavaScriptに名前を変えたくらいなので、当時はJavaに合わせたロゴにしていたという可能性は捨てきれません。
謎ロゴの出自を探る
謎ロゴ自体の出自がわかれば上記の2か3に当てはまるはずなので話は簡単なのですが、画像検索をしてみても素材配布サイトばかりヒットしてなかなか情報が出てきません。
代わりにJavaScriptの歴史を紹介するスライドや記事、コミュニティカレッジのコース紹介などで登場しているのを発見しました。
やっぱり過去に公式ロゴだったのでは?という気持ちが出てきます。JavaScriptの過去のロゴを探す
NetscapeのJavaScriptドキュメント(Internet Archive)やEcma International、Mozilla、Google Booksで初期のJavaScriptの洋書、YouTubeのNetscape利用動画などを当たってみましたが、JavaScriptのロゴは謎ロゴ含めて全く登場しません。
アメリカ合衆国特許商標庁の商標検索では図案付きのJavaの商標(Serial Number 76495912)は見つかったもののJavaScriptの商標(Serial Number 75026640)には図案がありませんでした。いかがでしたか?
JavaScriptの謎ロゴについて調査してみましたが、結局わかりませんでした。
このロゴを知っているよ!という方がいましたら、ぜひコメント欄で情報をお寄せください。謝辞
もともとはQiitadonに何気なく投稿した謎ロゴの話ですが、ユーザーの皆さんの協力があってこの記事がまとまりました。
一緒に調査してくれた方、⭐で後押しをしてくれた方、ありがとうございます。
(もし記事に事実誤認やつまらないジョークが含まれていた場合、その責は私にあります)おまけ
謎ロゴが載ってる謎ページ
http://computerprogrammingsteppingstone.weebly.com/java-script.html
白背景に白文字の怪しさもさることながら、さらに新しい謎ロゴが登場して調査団を混乱させる。
新しい謎ロゴはGoogleの画像検索では類似画像が見つからないものの、Bingの画像検索に突っ込んだところ機械的に作られたロゴらしいと判明するが、それでもやっぱり存在が謎すぎる…
- 投稿日:2020-05-27T14:00:58+09:00
railsでGoogleMapAPIの導入
このページのコードでできること
・自分のrailsプロジェクトにGoogleMapを埋め込み、表示させる。
・ページを開いたら地図と、初期値のマーカーを表示させる。
・inputボックスと検索ボタンを用意。
・inputボックスに検索したい場所を入力し、検索ボタンを押すことでその場所の地図を表示。Maps JavaScript API & Geocoding APIの取得
これら記事にお世話になりました。(参考文献より)
コードを書く前に
Google Maps API を使ってみた
Google MAP 名称から場所を検索・特定する
オリジナルアプリ作成 〜RailsでGoogleMap利用検証〜コード
postsに記述するとします。
@post.locationは一例です。ご自身のアプリケーションにふさわしい初期値を入れてください。posts/index.html.erb<div id='target'></div> <div class='map-btn'> <input id="address" type="textbox" value="<%= @post.location %>"> <input type="button" value="検索" onclick="codeAddress()"> <div> <script src="https://maps.googleapis.com/maps/api/js?key=自分のAPIキー&callback=initMap" async defer></script>style.scss#target { height: 300px; width: 300px; }Javascriptの記述
post.jslet map let geocoder let centerp = {lat: 33.60639, lng: 130.41806} function initMap(){ geocoder = new google.maps.Geocoder() map = new google.maps.Map(document.getElementById('target'), { center: centerp, zoom: 12, }); marker = new google.maps.Marker({ position: centerp, map: map }); } function codeAddress(){ let inputAddress = document.getElementById('address').value; geocoder.geocode( { 'address': inputAddress}, function(results, status) { if (status == 'OK') { map.setCenter(results[0].geometry.location); var marker = new google.maps.Marker({ map: map, position: results[0].geometry.location }); } else { alert('該当する結果がありませんでした:' + status); } }); }参考文献
大変お世話になりました。感謝。
コードを書く前に
[https://qiita.com/nagaseToya/items/e49977efb686ed05eadb]Google Maps API を使ってみた
[https://qiita.com/Haruka-Ogawa/items/997401a2edcd20e61037]Google MAP 名称から場所を検索・特定する
[https://qiita.com/yoshi_yast/items/521c1f36306a180f45dd]オリジナルアプリ作成 〜RailsでGoogleMap利用検証〜
[https://note.com/daddy0055/n/nddbe8da38bbc]