- 投稿日:2020-05-16T23:05:44+09:00
Expressでcssとjavascriptを読み込む方法
1. ディレクトリ構成
Expressでプロジェクトを作成するとディレクトリは以下のような構成になります。
詳細は今後に書くとして、cssとjavascriptをテンプレートエンジンへ適用させたい時は以下の手順を行ってください。
2. css作成
publicフォルダ直下のstylesheetsフォルダにcssファイルを作成してください。
3. javascript作成
publicフォルダ直下のjavascriptsフォルダにjsファイルを作成してください。
javascriptの場合はAPIの作成だったりルーティングの兼ね合いがありますので、
その点を考慮した実装としておいてください。4.適用
viewsフォルダに作成した(プロジェクト作成時は1ファイルのみ作成されている)テンプレートエンジンのファイルに読み込みをさせていきます。
4-1. cssの場合
おなじみ、linkタグを使いましょう
e.g.
<link rel='stylesheet' href='/stylesheets/style.css' />
publicフォルダ直下のstylesheetsフォルダのstyle.cssファイルなので良いですね?4-2. javascriptの場合
こちらもおなじみ、scriptタグを使いましょう
<script type="text/javascript" src='/javascripts/test.js'></script>
こちらも問題ないですね?5. 最後に
まだ終わりません。
生htmlと違い、タグで読み込むだけでは表示ができません。
どうしたら良いかと言うと、ディレクトリ構成の中にある、app.jsファイルに1行書き込みます。
app.jsはルーティングを定義したりしていますので、全体のコントローラと思ってください。
そこで以下を追加します。
app.use(express.static('public'));
若しくは
app.use(express.static(path.join(__dirname, 'public')));
※__dirname
は現在実行されているファイルの絶対パスが入っています。
これで表示ができると思います。
- 投稿日:2020-05-16T23:02:18+09:00
Kinx ライブラリ - DateTime
DateTime
はじめに
「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。言語はライブラリが命。ということでライブラリの使い方編。
今回は DateTime です。Range でも使えるようにしました。
- 参考
- 最初の動機 ... スクリプト言語 KINX(ご紹介)
- 個別記事へのリンクは全てここに集約してあります。
- リポジトリ ... https://github.com/Kray-G/kinx
- Pull Request 等お待ちしております。
使い方
using DateTime
DateTime ライブラリは標準組み込みではないため、using ディレクティブを使用して明示的に読み込む。
using DateTime;インスタンス化
インスタンス化は基本的には DateTime オブジェクトを new する方法で行う。
new DateTime()
... 現在時刻でインスタンス化new DateTime(dateString)
... 文字列をパースしてインスタンス化new DateTime(Unixtime)
... UNIXエポックの時刻からインスタンス化new DateTime(year, month, day[, hour, minute, second])
... 日時情報を個別に指定してインスタンス化ただし、以下でも可能(内部で new して返しているだけ)。好きなものを使ってください。
DateTime.parse(...)
DateTime(...)
尚、
dateString
は以下のような書式を解釈する。
"2020-01-01"
、"2020-1-1"
"2020/01/01"
、"2020/1/1"
"2020-01-01T10:00:05"
、"2020-1-01T10:0:5"
"2020/01/01 10:00:05"
、"2020/1/01 10:0:5"
メソッド
DateTime オブジェクトには以下のメソッドがある。
メソッド 動作概要 isLeapYear()
うるう年であれば true を返す unixtime()
現在日時の Unix エポック時間を返す datetime()
現在日時を表すオブジェクトを返す year()
現在日時の「年」 month()
現在日時の「月」 day()
現在日時の「日」 hour()
現在日時の「時」 minute()
現在日時の「分」 second()
現在日時の「秒」 weekday()
現在日時の「週」(0: 日曜, 1: 月曜, ..., 6: 土曜) isSunday()
日曜日であれば true を返す isMonday()
月曜日であれば true を返す isTuesday()
火曜日であれば true を返す isWednesday()
水曜日であれば true を返す isThursday()
木曜日であれば true を返す isFriday()
金曜日であれば true を返す isSaturday()
土曜日であれば true を返す clone()
日時オブジェクトのコピーを返す addDay(day)
日時オブジェクトを day
日進める(破壊的)subDay(day)
日時オブジェクトを day
日戻す(破壊的)addMonth(month)
日時オブジェクトを month
か月進める(破壊的)subMonth(month)
日時オブジェクトを month
か月戻す(破壊的)next()
次の日を表す新たな日時オブジェクトを返す +(day)
day
日後を表す新たな日時オブジェクトを返す-(day)
day
日前を表す新たな日時オブジェクトを返す>>(month)
month
か月後を表す新たな日時オブジェクトを返す<<(month)
month
か月前を表す新たな日時オブジェクトを返す<=>(dt)
0: 日時が同じ、-1: dt
のほうが後の日時、1:dt
のほうが以前の日時format(fmtString)
fmtString
のフォーマットに従ってフォーマットする。サポートするフォーマットは以下の通り。%YYYY%
:4桁の年、%YY%
:2桁の年%MM%
:2桁の月、%M%
:月%DD%
:2桁の日、%D%
:日%hh%
:2桁の時、%h%
:時%mm%
:2桁の分、%m%
:分%ss%
:2桁の秒、%s%
:秒
月末
<<
や>>
で月を移動した場合、対応する月に同じ日が存在しない時は代わりにその月の末日が使われる。using DateTime; System.println(DateTime("2001-3-28") << 1); // 2001/02/28 00:00:00 System.println(DateTime("2001-3-31") << 1); // 2001/02/28 00:00:00このことは以下のように、もしかすると予期しない振る舞いをするかもしれない(Ruby と一緒)。
using DateTime; System.println(DateTime("2001-1-31") >> 2); // 2001/03/31 00:00:00 System.println(DateTime("2001-1-31") >> 1 >> 1); // 2001/03/28 00:00:00 System.println(DateTime("2001-1-31") >> 1 >> -1); // 2001/01/28 00:00:00Range
Range で使えるようにするには、
next
メソッドと<=>
メソッドを定義しておけば良い。なので、DateTime オブジェクトは Range で使用できる。using DateTime; (DateTime(2020,1,1)..DateTime(2020,1,10)) .each(&(d) => System.println(d));
..
なので最後の日が含まれる。...
の場合は最後の日は含まれない。2020/01/01 00:00:00 2020/01/02 00:00:00 2020/01/03 00:00:00 2020/01/04 00:00:00 2020/01/05 00:00:00 2020/01/06 00:00:00 2020/01/07 00:00:00 2020/01/08 00:00:00 2020/01/09 00:00:00 2020/01/10 00:00:00Range で使えるので for-in でもそのままいける。
using DateTime; for (var d in DateTime(2020,1,1)...DateTime(2020,1,10)) { System.println(d); }最終日を含まないループ。
2020/01/01 00:00:00 2020/01/02 00:00:00 2020/01/03 00:00:00 2020/01/04 00:00:00 2020/01/05 00:00:00 2020/01/06 00:00:00 2020/01/07 00:00:00 2020/01/08 00:00:00 2020/01/09 00:00:00おわりに
作り始めてから約半年。色々できるようになってきましたねー。ライブラリを充実させて、何かしらのアプリを作れるようになることが次の目標ですかね。ニッチな用途でのアプリをサクッと作れる、とかできるとどこかに居場所ができるかもしれない。
ではまた次回。
- 投稿日:2020-05-16T22:23:06+09:00
Wixでjavascript sessionやarrayを使ってみる
コードをもうちょっとだけきれいにしてみた。
先週こちらの記事を書いたんですけど、もうちょっとコードを使いやすくしたので、公開しておきます。
以前は違う販売日を違うページにしていたのですが、今回、WixのSessionという機能(ユーザーがページを開いている間、そのユーザーのデータを保存する機能)を使って、販売日を記録しておき、希望日の在庫だけを見せる、という仕組みです。コードは汚いのですが、忘備録と、誰かの参考になればと思って、上げておきます。やりたいこと
さて、ポストコード販売ページはこんな感じで、ポストコードを打つことでjavascriptが文字の置き換えやボタンの置き換えを行う、というのがやりたかったことです。
これにポストコードを入れると、
こういうボタンが出てきたり、
ゴメンネっていうコメントが出たりします。デリバリーの日付は複数あるので、この日付をSessionに記録します。そうすると、飛んだ先のページで日付を元にオンラインショップを構成します。また、いいやり方かどうかはわからないんですが、一応前日の5時をすぎるとオーダーできなくなる仕組みも実装しました。ホントはGitで管理すればいいんですけど、多分これGitできない仕様なので。。何か方法をご存知の方はお知らせくださいーコードの解説。
汚いですが。
まずsessionをインポートします。'wix-storage'にセッションデータが記録されているようです。import {session} from 'wix-storage'; import wixWindow from 'wix-window';非常に汚いんですが、こういう方法で今回は配送日を記録します。
データベース接続すると、この辺はきれいになりそうです。
その辺は今回は飛ばしました。const deliveryDateDay=[20,21,22,23] const deliveryMonth=[4,4,4,4] //(0 for Jan, 11 for Dec) const deliveryDate=["20May","21May","22May","23May"]; const listmonths=["Jan","Feb","Mar","Apr","May","June","July","Aug","Oct","Nov","Dec"]; const currentDate=today.getUTCDate()+listmonths[today.getUTCMonth()]; const deliveryPostcodeWed=["TW8","TW9","TW10","TW1","TW2"]; const deliveryPostcodeThu=["SW19","SW18"]; const deliveryPostcodeFri=["W6","W8","W10","W11","W14"]; const deliveryDay=["Wed","Thu","Fri","Sat"]; const deliveryPostcodes=[deliveryPostcodeWed,deliveryPostcodeThu,deliveryPostcodeFri];JavascriptのDate機能を使って、現在の日付と時間を取り込み、水曜から土曜の各日付に対して、日付をすぎたかどうかを判定する部分です。ちょっとこれは月跨ぐとバグるコードなので、関数をちゃんと作るときに、月跨いでもいいコードに書き換えることにします。
const today = new Date(); //日付を超えたかどうか var isPassed=[false,false,false,false]; //各日付に対して、月、日と時間が超えてないかどうかを確認します。 for (var i = 0; i < 4; i++){ console.log(deliveryDateDay[i],today.getUTCDate(),deliveryMonth[i],today.getUTCMonth(),today.getUTCHours()); if (deliveryDateDay[i]<=today.getUTCDate() && deliveryMonth[i]<=today.getUTCMonth() && 16<=today.getUTCHours() ){ isPassed[i]=true; } }セッションにこの情報を保存します。
//console.log(isPassed); session.setItem("listDeliveryDate",deliveryDate); session.setItem("listDeliveryDay",deliveryDay); session.setItem("listIsPassed",isPassed);ポストコードの関数などは、前回の記事にあるので省略。
JavascriptのArrayを使って、前回の関数iconButton3_onClickをチョコチョコ書き換えます。export function iconButton3_onClick(event){ let value = $w("#input1").value; //ポストコードがちゃんと入力されれば実行 if( isValidPostcode(value)) { var postcode=formatPostcode(value); var areacodeslist=postcode.split(" "); var areacodes=areacodeslist[0]; session.setItem("POSTCODE", postcode); session.setItem("AREACODE", areacodes); var deliveryList=[(deliveryPostcodeWed.indexOf(areacodes)>-1),(deliveryPostcodeThu.indexOf(areacodes)>-1),(deliveryPostcodeFri.indexOf(areacodes)>-1)] var isDeliverable = false; //console.log(deliveryList,isDeliverable); //配達可能な三日間のArrayをforで巡回します。 for (i = 0; i < 3; i++) { //配達可能なとき if (deliveryList[i] && isDeliverable===false && isPassed[i]===false){ //$w("#text26").text="Sorry! Preorder is over! Please come back next week!"; $w("#iconButton1").label = "Order Delivery on "+deliveryDate[i]+" "+deliveryDay[i]; $w("#iconButton1").link = "/shoppingpage"; $w("#iconButton1").show(); session.setItem("Date", deliveryDate[i]); session.setItem("Day", deliveryDay[i]); $w("#text26").text="Great news! We are delivering to your area, "+deliveryPostcodes[i]+" on "+deliveryDate[i]+". Please tell your friends :)"; isDeliverable = true; } //配達できるけど、時間を過ぎてしまったとき else if (deliveryList[i] && isDeliverable===false && isPassed[i]===true) { $w("#iconButton1").label = "Order Shop Collection"; $w("#iconButton1").link = "/choosedate"; $w("#iconButton1").show(); $w("#text26").text="Sorry! We are delivering to your area, "+deliveryPostcodes[i]+" on "+deliveryDay[i] +" but preorder is over this week. Please wait until next week or you can always use click and collect service from the shop!"; isDeliverable = true; } //そのポストコードに配達できないとき else if (isDeliverable===false){ $w("#iconButton1").label = "Order Shop Collection"; $w("#iconButton1").link = "/choosedate"; $w("#iconButton1").show(); $w("#text26").text="We are very sorry, but at the moment there are no scheduled deliveries for your area in the next few weeks. You can still collect from the store using our click and collect service. Simply choose your order online, select a pick up date and we’ll see you then :)"; //or else do this other thing } } } //ポストコードが判定できなかったとき else{ $w("#text26").text="Sorry! We can't recognize your postcode......"; //or else do this other thing } }こんな感じです。飛んだ先のページでは、こんな感じでデータを取得できます。ただし、sessionの中のarrayはテキストになっているので、arrayに戻したり上げたりすることが必要かもしれません。
import {session} from 'wix-storage'; let postcode=session.getItem("POSTCODE"); let areacode=session.getItem("AREACODE"); let deliveryDate=session.getItem("Date"); let deliveryDay=session.getItem("Day");またそのうちまとめてコードをあげようと思いますが、今日はこの辺にしておきます。
- 投稿日:2020-05-16T22:13:06+09:00
KEN_ALL.CSVをEmEditorでゴニョゴニョしてみる
機能
- KEN_ALL.CSV(日本郵便で公開されている、郵便番号データ)を元に、町域名(半角カタカナ/漢字)部分をスッキリさせる(複数行にわたる括弧部分を削除とか、"以下を除く"等を削除)
- 変換後のファイルを出力する(必要な列のみ出力するようにすることも簡単な修正で可能)
実行環境
-Windows10
-EmEditor Professional (64-bit) Version 19.8.5 にて確認使い方
- 日本郵便の郵便番号データダウンロードページで公開されている郵便番号データ(全国一括)を取得し、"KEN_ALL.CSV"ファイルを準備する(*1)
- EmEditorで"KEN_ALL.CSV"ファイルを開き、下記マクロを実行する。
- KEN_ALL.CSVと同じディレクトリにKEN_ALL_cnv.txtファイルが作成される
- (KEN_ALL_LOG.txtファイルも出力される)
(*1)「読み仮名データの促音・拗音を小書きで表記しないもの」と「読み仮名データの促音・拗音を小書きで表記するもの」と2種類になっているが、"・・・小書きで表記するもの"でテストしています。変換処理の正規表現を変えることにより、"・・・小書きで表記しないもの"でも使えると思われます
マクロ
郵便番号データ変換_v2.jseevar startTime = new Date(); /* □マクロ名:郵便番号データ変換.jsee □バージョン:2.0 2020 05 16 クラスで実装 ■開くファイル 郵便番号データダウンロードから取得した住所の郵便番号(CSV形式) (ken_all.zipを展開したKEN_ALL.CSV。シフトJIS、改行コードは0x0D,0x0A) https://www.post.japanpost.jp/zipcode/dl/kogaki-zip.html ■作成ファイル レイアウト同じで町域名の内容を簡略化したファイル(ファイル名=拡張子の前に"_cnv"を追加したテキストファイル) ■手順(例) KEN_ALL.CSVを開く 郵便番号データ変換_v2.jseeを実行 同一ディレクトリに変換後のファイル(UTF-8)が作成される */ //■■■■■■■■■■■■■■■■■■■■■■■■■■■ //クラス定義 //■■■■■■■■■■■■■■■■■■■■■■■■■■■ //■■■■■■■■■■■■■■■■■■■■■■■■■■■ //■■ ログ情報クラス //■■■■■■■■■■■■■■■■■■■■■■■■■■■ //■■■■■■■■■■■■■■■■■■■■■■■■■■■ //【名称】ログ情報:コンストラクタ //【引数】なし //【返却】なし //【処理】ログ情報を管理する配列を作成する //■■■■■■■■■■■■■■■■■■■■■■■■■■■ Log = function(){ OutputBar.writeln("○Log.コンストラクタ(ログ情報:start)"); //コンストラクタ this.arrLog = []; OutputBar.writeln("○Log.コンストラクタ(ログ情報:end)"); }; //■■■■■■■■■■■■■■■■■■■■■■■■■■■ //【名称】ログ情報:追加 //【引数】ログ種類、郵便番号、ログ内容 //【返却】なし //【処理】this.arrLogにログ情報を追加する //■■■■■■■■■■■■■■■■■■■■■■■■■■■ Log.prototype.push = function( logType,//ログ種別 postalCode,//郵便番号 logContent//ログ内容 ){ // OutputBar.writeln("○Log.push(ログ情報.追加:start)"); tmpArr = []; tmpArr[0] = logType;//ログ種別 tmpArr[1] = postalCode;//郵便番号 tmpArr[2] = logContent;//ログ内容 this.arrLog.push(tmpArr); // OutputBar.writeln("○Log.push(ログ情報.追加:end)"); }; //■■■■■■■■■■■■■■■■■■■■■■■■■■■ //【名称】ログ情報:OutputBar出力 //【引数】なし //【返却】なし(OutputBarに出力するのみなので) //【処理】OutputBarにログ情報を出力する //■■■■■■■■■■■■■■■■■■■■■■■■■■■ Log.prototype.toString = function(){ OutputBar.writeln("○Log.toString(ログ情報.OutputBar出力:start)"); for (var i = 0; i < this.arrLog.length; i++){ OutputBar.writeln("this.arrLog[" + i + "]=" + this.arrLog[i][0] + "," + this.arrLog[i][1] + "," + this.arrLog[i][2] ); } OutputBar.writeln("○Log.toString(ログ情報.OutputBar出力:end)"); }; //■■■■■■■■■■■■■■■■■■■■■■■■■■■ //【名称】ログ情報:書き出し //【引数】出力ファイルの完全パス+ファイル名 //【返却】なし //【処理】ログ情報をまとめたthis.arrLogをファイルに書き出す //■■■■■■■■■■■■■■■■■■■■■■■■■■■ Log.prototype.fWrite = function(sLogFilePathName){ OutputBar.writeln("○Log.fWrite(ログ情報.書き出し:start)"); sLog = ''; for (var i = 0; i < this.arrLog.length; i++){ sLog += this.arrLog[i][0] + "," + this.arrLog[i][1] + "," + this.arrLog[i][2] + "\r\n"; } editor.NewFile();//新規にファイルを作成 docLog = editor.ActiveDocument;//現在開いている Document オブジェクトを返します。 docLog.write( sLog );//現在のカーソル位置に文字列を挿入、または上書き docLog.Encoding = eeEncodingUTF8;//次に保存する時に使用されるエンコードを設定(UTF-8) docLog.UnicodeSignature = false;//次に保存する時に Unicode サイン (BOM) を付けない docLog.Save( sLogFilePathName );//文書を保存します。 editor.ExecuteCommandByID(4120);//docNoWriteを保存しないで閉じます。■DELETE■ OutputBar.writeln("○Log.fWrite(ログ情報.書き出し:end)"); }; //■■■■■■■■■■■■■■■■■■■■■■■■■■■ //■■ 郵便番号管理クラス //■■■■■■■■■■■■■■■■■■■■■■■■■■■ //■■■■■■■■■■■■■■■■■■■■■■■■■■■ //【名称】郵便番号管理:コンストラクタ //【引数】なし //【返却】なし //【処理】郵便番号管理を管理するdocumentオブジェクトを作成する //■■■■■■■■■■■■■■■■■■■■■■■■■■■ PostalCodeManage = function(docInFile,log){ OutputBar.writeln("○PostalCodeManage.コンストラクタ(郵便番号管理:start)"); docInFile.ExtractColumns(":1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13,:14,:15"); this.docPCI = editor.ActiveDocument; docInFile.Activate();//文書をアクティブにします。 editor.ExecuteCommandByID(4120);//ファイルを保存しないで閉じます this.log = log; OutputBar.writeln("○PostalCodeManage.コンストラクタ(郵便番号管理:end)"); }; //■■■■■■■■■■■■■■■■■■■■■■■■■■■ //【名称】郵便番号管理:書き出し //【引数】なし //【返却】なし //【処理】this.docPCIを書き出す //【めも】fn=ファイル名、celn=書き出す列 //■■■■■■■■■■■■■■■■■■■■■■■■■■■ PostalCodeManage.prototype.fWrite = function(fn,celn){ OutputBar.writeln("○PostalCodeManage.fWrite(郵便番号管理.書き出し:start)"); //●書き出し this.docPCI.ExtractColumns(celn); this.docPCI.Activate();//文書をアクティブにします。 editor.ExecuteCommandByID(4120);//ファイルを保存しないで閉じます this.docWrite = editor.ActiveDocument; this.docWrite.Encoding = eeEncodingUTF8;//保存時のエンコード:UTF-8 this.docWrite.UnicodeSignature = false;//保存時に(BOM)を付けない this.docWrite.Save( fn );//文書を保存 this.docWrite.close();//文書を閉じる OutputBar.writeln("○PostalCodeManage.fWrite(郵便番号管理.書き出し:emd)"); }; //■■■■■■■■■■■■■■■■■■■■■■■■■■■ //【名称】郵便番号管理:チェック1 //【引数】なし //【返却】なし //【処理】this.docPCIの町域名(漢字)の重複を削除 //【めも】 //■■■■■■■■■■■■■■■■■■■■■■■■■■■ PostalCodeManage.prototype.cnv1a = function(){ OutputBar.writeln("○PostalCodeManage.cnv1(郵便番号管理.チェック1:start)"); //■町域の複数行に渡るかっこ書きを削除 //郵便番号の列を取得 this.docPCI.selection.SetActivePoint( eePosCellLogical, 3, 1 ); editor.ExecuteCommandByID(4461);//CSV文書で現在の列をヘディング無しで箱型選択モードで選択 sTmp = "\r\n" + this.docPCI.selection.Text; aPostalCode = new Array(); aPostalCode = sTmp.split("\r\n"); //町域の列を取得 //this.docPCI.Activate();//文書をアクティブにします。 this.docPCI.selection.SetActivePoint( eePosCellLogical, 9, 1 ); editor.ExecuteCommandByID(4461);//CSV文書で現在の列をヘディング無しで箱型選択モードで選択 sTmp = "\r\n" + this.docPCI.selection.Text;//値を[1]以降に格納 aTyou = new Array(); aTyou = sTmp.split("\r\n"); sTmp = null; this.docPCI.selection.Collapse();//選択状態をキャンセルします。 OutputBar.writeln("aPostalCode.length=" + aPostalCode.length); OutputBar.writeln("aTyou.length=" + aTyou.length); //最終行から開始行へ向かって処理を行う fOkikae = false;//フラグ置き換えモード:終わり re1 = new RegExp("^\"[^(]*)\"$");//'('が無く、右端の')'を検索(両端は'"') re2 = new RegExp("^(\".+)(([^)]*)(\")");//'('があり')'が無い行を検索($1:開く括弧の前、$3:'"') for(i = aTyou.length - 1; i >= 1; i--){ if( ( aPostalCode[i] == aPostalCode[i-1] ) || fOkikae){//郵便番号が同じor置き換えモードの場合 this.docPCI.selection.SetActivePoint( eePosCellLogical, 9, i ); if( fOkikae ){ if(aTyou[i].match(re2)){//'('を検索 this.log.push('i',this.docPCI.GetCell( i, 3, eeCellIncludeNone ),RegExp.$1 + RegExp.$3); this.docPCI.SetCell( i , 9, RegExp.$1 + RegExp.$3, eeDontQuote );//"("以降の部分を削除 fOkikae = false;//フラグ置き換えモード:終わり }else{ //'('の行と')'の行の間の行(行をブックマーク) this.docPCI.selection.SelectLine(); this.docPCI.selection.SetBookmark(); } } else { if(aTyou[i].match(re1)){//')'を検索 this.docPCI.selection.SelectLine(); this.docPCI.selection.SetBookmark(); fOkikae = true;//フラグ置き換えモード:開始 } } } } editor.ExecuteCommandByID(4589);//この文書のすべてのブックマークされた行を削除 OutputBar.writeln("○PostalCodeManage.cnv1(郵便番号管理.チェック1:end)"); }; //■■■■■■■■■■■■■■■■■■■■■■■■■■■ //【名称】郵便番号管理:チェック2 //【引数】なし //【返却】なし //【処理】this.docPCIの町域名(漢字)の置き換え //【めも】 //■■■■■■■■■■■■■■■■■■■■■■■■■■■ PostalCodeManage.prototype.cnv2 = function(){ OutputBar.writeln("○PostalCodeManage.cnv2(郵便番号管理.チェック2:start)"); //■町域の文字列を置き換え this.docPCI.selection.SetActivePoint( eePosCellLogical, 9, 1 );//カーソル位置を設定 editor.ExecuteCommandByID(4461);//CSV文書で現在の列をヘディング無しで箱型選択モードで選択します。 //★文字列決め打ちで削除 this.docPCI.selection.Replace("以下に掲載がない場合","",eeFindReplaceSelOnly | eeReplaceAll,0); this.docPCI.selection.Replace("(\".*)((その他|丁目|番地|次のビルを除く|地階・階層不明|.*[、~].*|.*以上|.*以下))(\")","\\1\\3",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0); //★名駅ミッドランドスクエア(高層棟)(地階・階層不明) this.docPCI.selection.Replace("(\".*)(高層棟)(.*\")","\\1\\2",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0); //★三田市の次に番地がくる場合 this.docPCI.selection.Replace("\".*の次に番地がくる場合\"","\"\"",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0); //★土樋(1丁目「11を除く」) => 土樋(1丁目) //★切畑(長尾山「その他」) => 切畑(長尾山) this.docPCI.selection.Replace("(\".*(.*)「(.*を除く|その他)」()\")","\\1\\3",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0); //★音江町(国見その他) => 音江町(国見) this.docPCI.selection.Replace("(\".*(.*)その他()\")","\\1\\2",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0); //★花田町官有地(無番地を除く) //★芦田町福田(376-10を除く) //★津島町下畑地(乙を除く) this.docPCI.selection.Replace("(\".*)(.*を除く)(\")","\\1\\2",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0); //★厚内(全域) => 厚内 this.docPCI.selection.Replace("(\".*)(全域)(\")","\\1\\2",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0); //■町域のかっこ"(" => " "、")" => ""へ document.selection.Replace("(\".*)((.*))(\")","\\1 \\2\\3",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0); OutputBar.writeln("○PostalCodeManage.cnv2(郵便番号管理.チェック2:end)"); }; //■■■■■■■■■■■■■■■■■■■■■■■■■■■ //【名称】郵便番号管理:チェック3 //【引数】なし //【返却】なし //【処理】this.docPCIの町域名(半角カタカナ)の置き換え //【めも】 //■■■■■■■■■■■■■■■■■■■■■■■■■■■ PostalCodeManage.prototype.cnv3 = function(){ OutputBar.writeln("○PostalCodeManage.cnv3(郵便番号管理.チェック3:start)"); //■町域の文字列を置き換え this.docPCI.selection.SetActivePoint( eePosCellLogical, 6, 1 );//カーソル位置を設定 editor.ExecuteCommandByID(4461);//CSV文書で現在の列をヘディング無しで箱型選択モードで選択します。 //★開き括弧のみの場合、括弧以降を削除 re2 = new RegExp("^(\".+)\\(([^\\)]*)(\")");//'('があり')'が無い行を検索($1:開く括弧の前、$3:'"') nLine = this.docPCI.GetLines(); for(i = 1; i < nLine ; i++){ if(this.docPCI.GetCell( i, 6, eeCellIncludeQuotes ).match(re2)){//'('を検索 this.docPCI.SetCell( i , 6, RegExp.$1 + RegExp.$3, eeDontQuote );//"("以降の部分を削除 } } //★文字列決め打ちで削除 this.docPCI.selection.Replace("イカニケイサイガナイバアイ","",eeFindReplaceSelOnly | eeReplaceAll,0); this.docPCI.selection.Replace("(\".*)\\((ソノタ|チョウメ|バンチ|ツギノビルヲノゾク|チカイ・カイソウフメイ|.*[、\\-].*|.*イジョウ|.*イカ)\\)(\")","\\1\\3",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0); //★名駅ミッドランドスクエア(高層棟) this.docPCI.selection.Replace("(\".*)\\(コウソウトウ\\)(.*\")","\\1\\2",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0); //★三田市の次に番地がくる場合 this.docPCI.selection.Replace("\".*ノツギニバンチガクルバアイ\"","\"\"",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0); //★ツチトイ(1チョウメ<11ヲノゾク>) => ツチトイ(1チョウメ) //★キリハタ(ナガオサン<ソノタ>) => キリハタ(ナガオサン) this.docPCI.selection.Replace("(\".*\\(.*)<(.*ヲノゾク|ソノタ)>(\\)\")","\\1\\3",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0); //★オトエチョウ(クニミソノタ) => オトエチョウ(クニミ) this.docPCI.selection.Replace("(\".*\\(.*)ソノタ(\\)\")","\\1\\2",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0); //★ハナダチョウカンユウチ(ムバンチヲノゾク) //★アシダチョウフクダ(376-10ヲノゾク) //★ツシマチョウシモハタジ(オツヲノゾク) this.docPCI.selection.Replace("(\".*)\\(.*ヲノゾク\\)(\")","\\1\\2",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0); //★アツナイ(ゼンイキ) => アツナイ this.docPCI.selection.Replace("(\".*)\\(ゼンイキ\\)(\")","\\1\\2",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0); //■町域(半角カタカナ)のかっこ"(" => " "、")" => ""へ document.selection.Replace("(\".*)\\((.*)\\)(\")","\\1 \\2\\3",eeFindReplaceSelOnly | eeReplaceAll | eeFindReplaceRegExp,0); OutputBar.writeln("○PostalCodeManage.cnv3(郵便番号管理.チェック3:end)"); }; //■■■■■■■■■■■■■■■■■■■■■■■■■■■ //処理開始 //■■■■■■■■■■■■■■■■■■■■■■■■■■■ var startTime = new Date(); OutputBar.Visible = true; OutputBar.Clear(); OutputBar.writeln("○(処理:start)"); Redraw = false;//ウィンドウの再描画:しない docInFile = editor.ActiveDocument; //文書を含むディレクトリのパスをファイル名を付けないで取得します。 sInFilePath = docInFile.Path; //文書のファイル名をパスを付けないで取得します。 aInFileName = docInFile.Name.split("."); //変換後書き出すファイル名 sOutFilePathName = sInFilePath + "\\" + aInFileName[0] + "_cnv.txt"; //ログ用ファイル名を設定 sLogFilePathName = sInFilePath + "\\" + aInFileName[0] + "_LOG.txt"; //CSV モードで列の数を取得します。文書が CSV モードでない場合は、0 を返します。 columnN = docInFile.GetColumns(); if(columnN != 15){ alert( "列数エラー。列数は、" + columnN + "です。" ); Quit(); } //●ログ管理オブジェクトを作成 var log = new Log(); //●郵便番号管理オブジェクトを作成 var pcManage = new PostalCodeManage(docInFile,log); //●町域名が複数行に渡る場合の処理 pcManage.cnv1a();//配列使用版 //pcManage.cnv1b();//match使用版 //●町域名(漢字)の置き換え pcManage.cnv2(); //●町域名(半角カタカナ)の置き換え pcManage.cnv3(); //●変換後のファイルを書き出す pcManage.fWrite(sOutFilePathName,":1,:2,:3,:4,:5,:6,:7,:8,:9,:10,:11,:12,:13,:14,:15"); //pcManage.fWrite(sOutFilePathName,":3,:7,:8,:9"); //必要な列のみ指定する場合 //●ログファイル書き出し log.fWrite(sLogFilePathName); var endTime = new Date(); Redraw = true;//ウィンドウの再描画:する alert("処理終了:" + (endTime - startTime)/1000 + "sec"); Quit();補足
- 町域名(半角カタカナ)を利用しないのであれば、"pcManage.cnv3();"の処理は不要です。
- ログファイル部分の処理は変換処理には関係ありませんので、削除しても問題ありません。
- 出力ファイルの文字コードや改行コードなどを変更する場合は、"pcManage.fWrite()"を変更すればよいです。
- EmEditorでのcsvファイル操作のメモ書きとして
参考情報
- 投稿日:2020-05-16T20:41:04+09:00
【API】郵便番号検索APIを使ってみた
APIを使用したアプリを作ってみる
背景
- APIを使用したアプリケーションが増えてきている
- APIを使用する、作るエンジニアの仕事も増えている
- APIを扱う技術の需要が増えそう
目次
0.環境確認
- OS: Windows10 home
- IDE : Eclipse(Photon 4.8)
- ビルドツール : Gradle
- サーバーサイド言語 : Java(1.8)
- JavaScript ライブラリ : jQuery(3.3.1)
- テンプレートエンジン : thymeleaf
1.APIの確認
以下のサイトからAPIの使用を確認します。
https://zip-cloud.appspot.com/doc/api仕様を確認します。
- https://zip-cloud.appspot.com/api/search をベースにする
- リクエストパラメータ
パラメータ名 項目名 必須 備考 zipcode 郵便番号 ○ 7桁の数字。ハイフン付きでも可。完全一致検索。 callback コールバック関数名 - JSONPとして出力する際のコールバック関数名。UTF-8でURLエンコードした文字列。 limit 最大件数 - 同一の郵便番号で複数件のデータが存在する場合に返される件数の上限値(数字) ※デフォルト:20
- レスポンスパラメータ
フィールド名 項目名 備考 status ステータス 正常時は 200、エラー発生時にはエラーコードが返される message メッセージ エラー発生時に、エラーの内容が返される。 results zipcode(郵便番号)
prefcode(都道府県コード)
address1(都道府県名)
address2(市区町村名)
address3(町域名)
kana1(都道府県名カナ)
kana2 (市区町村名カナ)
kana3(町域名カナ)複数の場合、配列となる
- (例)郵便番号「7830060」で検索する場合
- リクエストURL https://zip-cloud.appspot.com/api/search?zipcode=7830060
- レスポンス
{ "message": null, "results": [ { "address1": "北海道", "address2": "美唄市", "address3": "上美唄町協和", "kana1": "ホッカイドウ", "kana2": "ビバイシ", "kana3": "カミビバイチョウキョウワ", "prefcode": "1", "zipcode": "0790177" }, { "address1": "北海道", "address2": "美唄市", "address3": "上美唄町南", "kana1": "ホッカイドウ", "kana2": "ビバイシ", "kana3": "カミビバイチョウミナミ", "prefcode": "1", "zipcode": "0790177" } ], "status": 200 }2.プロジェクトの作成
別の記事で詳細に紹介しているので、そちらを参照ください。
GradleのSpringBootプロジェクトを作成する3.サーバーエンドの実装
- build.gradle
build.gradle//中略 dependencies { implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' compile("com.fasterxml.jackson.core:jackson-databind") }
- Controllerクラス
FrontController.java@Controller public class FrontController { @Autowired private FrontService frontService; @RequestMapping({ "/", "/index" }) public String index() { return "index"; } @ResponseBody @RequestMapping(value = "/getAddress" ,method = RequestMethod.POST, produces="application/json;charset=UTF-8") public String getAddress(@RequestBody(required = false) AddressForm addressForm) { return frontService.getAddress(addressForm.getZipcode()); } }
- Serviceクラス
FrontService.javapublic interface FrontService { public String getAddress(String zipCode); }FrontServiceImpl.java@Service public class FrontServiceImpl implements FrontService { /** 郵便番号検索API リクエストURL */ private static final String URL = "https://zip-cloud.appspot.com/api/search?zipcode={zipcode}"; @Override public String getAddress(String zipCode) { String zipCodeJson = restTemplate.getForObject(URL, String.class, zipCode); return zipCodeJson; } }
- formクラス
AddressForm.java@Data public class AddressForm { /** 郵便番号 */ String zipcode; }4.フロントエンドの実装
- html
index.html<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>address</title> <script type="text/javascript" th:src="@{/jquery/jquery-3.3.1.js}"></script> <script th:src="@{/js/index.js}"></script> </head> <body> <form name="getAddress"> <input id="zipcode" type="text"> <button type="button" id="getAddressBtn">住所取得</button> <div id="dispAddress"></div> </form> </body> </html>
- JavaScript
index.js$(function() { $('#getAddressBtn').on('click', function() { var params = { "zipcode" : $('#zipcode').val() }; $.ajax({ url : 'getAddress', type: 'POST', contentType: "application/json", data: JSON.stringify(params), dataType : 'json', async: false, success: function (data) { $("#dispAddress").empty(); var dispAddress = document.getElementById("dispAddress"); var table = document.createElement("table"); table.setAttribute("border","2"); table.setAttribute("cellpadding","15"); table.setAttribute("style","margin :15px"); $(data.results).each(function(index, result){ table.appendChild(createRow("郵便番号",result.zipcode)); table.appendChild(createRow("都道府県コード",result.prefcode)); table.appendChild(createRow("都道府県名",result.address1)); table.appendChild(createRow("市区町村名",result.address2)); table.appendChild(createRow("町域名",result.address3)); table.appendChild(createRow("都道府県名カナ",result.kana1)); table.appendChild(createRow("市区町村名カナ",result.kana2)); table.appendChild(createRow("町域名カナ",result.kana3)); }); dispAddress.appendChild(table); } }); }); }); function createRow(header , value){ var tr = document.createElement("tr"); var th = document.createElement("th"); th.append(header); var td = document.createElement("td"); td.append(value); tr.appendChild(th); tr.appendChild(td); return tr; }5.動作確認
6.まとめ
- APIを使用するのは簡単(認証機能付きはもう少し難しい)
- 他APIからデータ活用できる
- いろんなAPIを組み合わせて新しいサービスを作れそう。
- 投稿日:2020-05-16T20:36:45+09:00
【初級編】JavaScript if文についてその2
前回の記事
https://qiita.com/furukouji/items/8d6bdad9d0c2fce011db? 簡単な条件記入例(サラッと書きます)
const price = 99; if (price >= 100){ //priceが100以上であるとき実行する console.log('値段は'+price+'円です'); }; if (price <= 100){ //priceが100以下であるとき console.log('お金が足りません。'); }; if (price != 100){ //!は否定形です。100ではないときコードを実行します。 また!==とすることで型の判定もできるので基本的にはこちらを使いましょう console.log('値段は'+price+'円です'); };AND,OR,三項演算子について
AND = 条件を両方満たす場合実行する。コード上は && と書くと前述と同義となります
OR = どちらかの条件を満たす場合実行する。コード上は || と書きます。(フォントの関係で斜めになってます)
ということでコードを書いていきましょう。const signal_1 = 'red' const signal_2 = 'yellow' if(signal_1 ==='red' && signal_2'yellow'){ console.log('赤と黄色です。'); } if(signal_1'red' || signal_2 'yellow'){ console.log('赤か黄色です'); }AND,ORは基本的には記号が違うだけで使い方は変わりません!!
三項演算子について書いてきます。
三項演算子の基本の形は条件 ? 真 : 偽
このような形になります。文字だけだと意味不明なので、実際にコードを書いていきます。
const point = 100 ; const player = point > 100 ? 'good' : 'not good'; //pointが100より大きかったらgoodが低ければ not good が値として返ります。 console.log(player);このように一文でスッキリと書くことができるのでおすすめです!
今回は短いですがこのへんで!
? まとめ
- AND = && OR = ||
- 三項演算子の基本形は 条件 ? 真: 偽
- 三項演算子のメリットは一文でスッキリ書ける
? 次回予告
次回は、内容未定です!
for文の内容にしようかと思っていますが
Node.jsの内容になるかもしれません!*記事を見ていただいてありがとうございます。
ご指摘有りましたら編集リクエストまでドシドシお願いいたします!*
- 投稿日:2020-05-16T20:04:34+09:00
DenoはNode.jsとどこが違うのか超ざっくりまとめ?
2020年5月13日、ついにver1.0がリリースされたDeno。
Node.jsとの違いをざっくりと解説します。※以下、本記事の内容はDeno公式マニュアルの記述に基づきます
そもそもDenoって何なの?
DenoはJavaScriptおよびTypeScriptの実行環境です。
V8, Rust, およびTokioによって作られています。え、Node.jsがあるじゃん?
Node.jsは2009年に誕生し、すでに10年を超える歴史を有しています。
たとえばNode.jsが生まれたとき、この世にPromiseはありませんでした。
2012年にはTypeScriptが生まれました。この10年で、JavaScriptを取り巻く環境は大きく変わってしまったのです。
そして2020年。
Node.jsの問題を解決し、よりよい開発環境を提供するため、Node.jsの開発者であるライアン・ダールらの手によって生まれたのがDenoなのです(DenoはNodeのアナグラム)。DenoはNode.jsとどこが違うのか
npmを使いません
モジュールは任意のURLまたはファイルパスからダウンロードされます。
import {hoge} from "fuga";というNode.jsでの記述形式は、指定されたモジュールがどこにあるのか特定できないという問題を引き起こしていました。
fuga
モジュールはNPMサーバにあるのかもしれないし、ローカルのnode_modules
フォルダにあるのかもしれません。import {hoge} from "https://your.domain.com/fuga.ts";とすることで、
fuga
ライブラリの場所は一意に定まります。またこれは同時に、
package.json
に相当するしかけを必要としないことをも意味します。URLやファイルパスがそのままバージョンの指定にもなっていれば、わざわざ別ファイルでその依存性情報まで管理する必要はないからです。
TypeScriptを標準でサポートします
追加のライブラリをインストールすることなく、TypeScriptをコンパイルしてくれます。
コンパイルは自動的に行われるので、コマンドを叩く必要もありません。
セキュアです
明示的に許可されない限り、denoはネットワーク接続もファイル読み取りもできません。
例として、標準ライブラリとして公開されている
curl.ts
でURLへのアクセスを試してみるとエラーが返ります。$> deno run https://deno.land/std/examples/curl.ts https://example.com error: Uncaught PermissionDenied: network access to "https://example.com/", run again with the --allow-net flag接続許可するドメインを
--allow-net
フラグに設定することで初めて、URLへのアクセスが可能になります。deno run --allow-net=example.com https://deno.land/std/examples/curl.ts https://example.comよくわからないNPMパッケージを走らせたら、変なURLに接続されたりファイルシステムを触られたりするのではないか……という恐怖ともこれでおさらばです。
詳細: https://deno.land/manual/getting_started/permissions
標準テストランナーがあります
Deno.test
を使えば、ライブラリをインストールすることなくテストを実行できます。Deno.test("Hello deno test", () => { const foo: string = "Hello"; if (foo !== "Hello") { throw ("foo should be Hello."); } });アサーションライブラリも用意されています。
import { assertEquals } from "https://deno.land/std/testing/asserts.ts"; Deno.test("Hello assertion", () => { const foo: string = "Hello"; assertEquals(foo, "Hello"); });実行するときは
deno test [files]
コマンドを叩くだけ。かんたん!詳細: https://deno.land/manual/testing
非同期のアクションはすべてPromiseを返します
Webサーバを記述する場合、Node.jsではこんな感じでしたが
var http = require('http'); http.createServer(function (req, res) { res.write('Hello World!'); res.end(); }).listen(8080);出典: https://www.w3schools.com/nodejs/nodejs_http.asp
Denoでは
await
を使ってこんな感じになります。import { serve } from "https://deno.land/std/http/server.ts"; const s = serve({ port: 8080 }); for await (const req of s) { req.respond({ body: "Hello World!\n" }); }出典: https://deno.land/std/http
予期せぬエラーが発生した場合は"常に"処理を終了します
Uncaught Exceptions in Nodeなどの記事に見られるように、Node.jsでは捕捉できないエラーをどう扱うかは開発者に任せられていました。
処理を終了させてもいいし、もみ消して続行させることもできました。
Denoでは常に処理が終了します。常にです。
おまけ
Node.jsが抱える問題点、およびDenoが目指すゴールについては、ライアン・ダール自らによる以下のプレゼンテーションを見るとわかりやすいです。
Node.jsに関する10の反省点 Ryan Dahl - JSConf EU 2018
- 投稿日:2020-05-16T20:02:47+09:00
JavaScriptにおける配列リテラルとコンストラクターの違いについて[小ネタ]
0.はじめに
こんにちは。
エンジニアしている @gkzz です。JavaScriptのお勉強をしているのですが、Google JavaScript Style Guideを読んでいて気になった箇所がありました。
5.2.2 Do not use the variadic Array constructor
The constructor is error-prone if arguments are added or removed. >Use a literal instead.Disallowed:
const a1 = new Array(x1, x2, x3);
const a2 = new Array(x1, x2);
const a3 = new Array(x1);
const a4 = new Array();そういえば、[]とnew Array()。
違いってあるのだろうか。
気になってしまったので、早速調べて、まとめました。1.非推奨のArrayコンストラクターを使った場合
let data2 = new Array(10); console.log("data2: " + data2); console.log("data2[0]: " + data2[0]); console.log("data2.length: " + data2.length); console.log("#######################"); data2[0] = 10; console.log("data2: " + data2); console.log("data2[0]: " + data2[0]); console.log("data2.length: " + data2.length);data2: ,,,,,,,,, data2[0]: undefined data2.length: 10 ####################### data2: 10,,,,,,,,, data2[0]: 10 data2.length: 10ポイント
new Array(10)とは、lengthが10である配列を定義
- ひとつひとつの要素はカラであり、undefinedとなる
- 要素を定義する場合、data2[0] = 10;とする必要がある
- 一方、
new Array("10")と文字列の場合、lengthが1である配列を定義
- data[0]の値は文字列の10
- 「new Array()の使いどころ」にてコンストラクターの使いどころについて後述
2.推奨されている配列の書き方
左記のGoogle JavaScript Style Guideには、推奨された書き方も紹介されています。
Instead, write
const a1 = [x1, x2, x3];
const a2 = [x1, x2];
const a3 = [x1];
const a4 = [];先ほどと同様、出力結果も確認してみましょう。
let data1 = [10]; console.log("data1: " + data1); console.log("data1[0]: " + data1[0]); console.log("data1.length: " + data1.length); console.log("#######################"); data1[0] = 1; console.log("data1: " + data1); console.log("data1[0]: " + data1[0]); console.log("data1.length: " + data1.length);data1: 10 data1[0]: 10 data1.length: 1 ####################### data1: 1 data1[0]: 1 data1.length: 1ポイント
let data = [10]とは、lengthが1であり、data[0]の値は10ことを定義
- data1[0]がundefinedではなく、格納した値が入っている
3.new Array()の使いどころ
new Array()の使いどころが気になったので、調べたところ、下記の記事が参考になったので、引用させていただきます。
一つずつ要素を入れて配列長をじわじわ伸ばしていくより、最初にまとめて確保しておいた方が軽い
という理屈だ。
古いJavaScriptエンジンでは有効なテクニックだったのだが、エンジンの進歩により、逆転現象が起きている。V8で測定すると[]を使った方が2倍ほど速かった。というわけで、
今の時代、タイプ数の多い new Array をあえて使う必要はない。
[]とnew Array()。
配列を扱いたい場合、[]でOKということですよね。p.s.JavaScriptにおいてメモリが解放されるタイミングはいつなのか
メモリはいつ開放されるのか?という点も気になったので、こちらについても私の理解と参考になった箇所を書き留めておきます。
- メモリが開放されるタイミングは分からない
ある領域にオブジェクトが管理されていてだれからも参照されなくなったら回収される
と理解すればよさそうJavaScript(ECMAScript)の仕様では、いつメモリが解放されるのかは定められていません。GC(ガベージコレクション)がいつ動作するのか、また、そのGCで回収されるのかはJavaScriptエンジンによって異なり、一概には言えません。
(中略)
ある領域にオブジェクトが管理されていてだれからも参照されなくなったら回収されるというのが肝心な点なのでそこだけぶれなければOKじゃないでしょうか。んー。。
分かったようなわからないような汗。
— gakuji tamaki (@gkzvoice) May 16, 2020
>ある領域にオブジェクトが管理されていてだれからも参照されなくなったら回収されるというのが肝心な点なのでそこだけぶれなければOKじゃないでしょうか。
「javascriptの変数のメモリへの割当について」 https://t.co/mDaMOvK55p #teratailP.P.S. Twitterもやってるのでフォローしていただけると泣いて喜びます:)
- 投稿日:2020-05-16T19:43:02+09:00
GitHubでindex.jsだけのライブラリーを使った学習法
はじめに
ある程度プログラミングできるようになったけど、レベルを上げるためにどうやって勉強したらいいのかわからない人向けの勉強法です。
何か困った時、プログラマーならまずライブラリーを探すかと思います。
探したライブラリーのGitHubにコードを見に行った時に⭐️がゼロだったらスルーしてしまうかもしれないですが
それは非常にもったいないです。
具体的にどういう風にもったいないか説明します。
ある日のこと
.svg
なファイルをrequire
したらどうなるのかなって思ってreuqireしたらエラーになりました。$ node Welcome to Node.js v12.7.0. Type ".help" for more information. > var sprites = require('./src/assets/svg-sprites.svg') Thrown: /Users/yukihirop/JavaScriptProjects/sample/src/assets/svg-sprites.svg:1 <svg width="0" height="0" class="hidden" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> ^ SyntaxError: Unexpected token < >なんか読み込めるようにするライブラリーないか探したところ、 inline-svg-registerというものを見つけたのですが、?がゼロでした。(今は一個あります。色々勉強になったので私がつけました。)
「星ないのかー...」って思ったのですが、使い方はシンプルなので使ってみました。
$ yarn -D add inline-svg-register $ node Welcome to Node.js v12.7.0. Type ".help" for more information. > var unhook = require("inline-svg-register") undefined > var sprites = require("./src/assets/svg-sprites.svg") undefined > sprites '<svg width="0" height="0" class="hidden" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <defs> <!-- yukihirop -->エラーも起きずに文字列で出力されるようになりました。
知識の獲得
すぐに仕組みが気になりました。 100行程度の
index.js
しかないしコード見てみるかと思って見たら、ラッキーな事にbabel-register
のコードを参考にして作ったとあったので読んで見ました。そこで以下の知識を得ました。
- nodeの
require
はrequire.extensions
という関数を使って、拡張子で判定してコンパイルできる。
- だが、
require.extensions
は非推奨。- 非推奨どうしたらいいのかstackoverflowを見たのですが、ピシャッとくる回答がない。
するとすぐに次のような興味が湧きました。
現在の
babel-register
では当然非推奨の対応をしてあるだろう。もしかしたら、babel-register
での非推奨対応のPRを見つけたら勉強になるかもしれないぞと思って、babel-registerのコードのコードを見に行きました。?実際にbabel-registerのコードが差し替えられた時のPRはこちら
すると現在では、
require.extensions
が使われておらず、pirates というものが使われている事を知りました。
pirates
って何だろうって思ってコードを見に行ったらこれまたラッキーな事にindex.js
しかなく100行程度だったので読んでみました。そこで以下の知識を得ました。
- nodeのビルトインModuleの
Module._extensions
をうまく使って拡張子で判定してコンパイルできるようにしている事を知りました。
- ビルトインモジュールに機能拡張しているライブラリーを見たのは初めてでした。新鮮でした。
使い方が簡単だったので、すぐに
inline-svg-register
のコードに還元してやろうと思いました。知識の還元
得た知識はすぐに使ったほうが定着がいいと思ったのですぐに知識の還元を行いました。
まとめ
「
svgファイルをrequireで読み込んで見たい
」って思っただけで思わぬ収穫がありました。知識獲得フェーズ
babel-register
というコードが何をするのか想像ができた。require.extensions
という関数で拡張子毎にコンパイルを変えれるが非推奨である。require.extensions
の代わりにpirates
というライブラリーが使われているようである。知識還元フェーズ
いつもこんなラッキーが続くとは思いませんが、たまには
index.js
だけのライブラリーを読んでみるもの面白いかなって思います。以上です。
- 投稿日:2020-05-16T19:19:55+09:00
Youtubeの好きな動画の好きな部分だけをリスト再生するWebページの作り方
はじめまして。ひょんなことからVtuberにハマり、色々な物を書いている夕星(ゆうづつ)と申します。
この度、推しのプレイリストが欲しいなぁという欲望の結果、簡素ながらもそれなりに動く物が出来たので備忘録を兼ねて記します。まず完成品がこちらになります。
加賀美ハヤト「ロックコンピ」プレイヤー|夕星の長い話置き場
(にじさんじ所属Vtuber「加賀美ハヤト」の配信から楽曲を抜粋したリストです)概要
構成は静的HTML+ピュアJavascript(Youtube Iframe API使用)。
ネットに繋がってさえいればローカルでも動作しますので、自分用かWebサイトに載せて仲間内で楽しむ等の用途が想定されます。
なおAPIの利用規約はご自身で最新版をご確認ください。(基本は非営利利用限定の様子?)動画の再生数は本家にカウントされ、広告も再生されます。
「切り抜き動画では本家に還元されない」「プレイリストだと動画丸ごとしか再生出来ない」「作業BGMとして良い場面だけリピート再生したい」という悩みやニーズに応えてます。
(あと上手くやるとスマホでバックグラウンド再生出来たりもします)ソースコード
ほぼ最小構成が以下です(リストが必要無いなら更に削れます)。デザイン等はご自由に調整ください。
かなり雑でDOM構成もやっつけなので、実際に使用する場合はご利用中の環境に合わせて適宜編集推奨です。
少し長いので折り畳んでいます
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>ページタイトル</title> <style type="text/css"> #playlist { background-color: black; color: #aaaaaa; } .playparts { padding: 10px; cursor: pointer; } .playparts:not(:last-child) { border-bottom: 1px #aaa solid; } .songtitle { color:white; } </style> </head> <body> <div id="player"></div> <div id="playlist"></div> <script> // 動画リスト var playList = [ {videoId:'',startSeconds:0, endSeconds:0, title:'', artist:''}, ]; // Iframe APIの準備 var tag = document.createElement('script'); tag.src = "https://www.youtube.com/iframe_api"; var firstScriptTag = document.getElementsByTagName('script')[0]; firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); // プレイヤーの設定 var player; function onYouTubeIframeAPIReady() { player = new YT.Player('player', { height: '360', width: '640', events: { 'onReady': onPlayerReady, 'onStateChange': onPlayerStateChange } }); } // プレイヤーの準備完了後の処理(最初の動画のセット) var isReady = false; var nowPlaying = 0; var isNextVideo = false; function onPlayerReady(event) { player.cueVideoById(playList[nowPlaying]); document.getElementById("song"+nowPlaying).style.color = 'red'; isReady = true; } // プレイヤーの状態が変化した時の処理(次の動画へ進む) function onPlayerStateChange(event) { if (event.data == YT.PlayerState.ENDED && !isNextVideo) { document.getElementById("song"+nowPlaying).style.color = 'white'; nowPlaying += 1; if (playList.length <= nowPlaying) nowPlaying = 0; isNextVideo = true; player.loadVideoById(playList[nowPlaying]); document.getElementById("song"+nowPlaying).style.color = 'red'; } else if (event.data == YT.PlayerState.PLAYING && isNextVideo) { isNextVideo = false; } } // リストの表示 function showList () { var parent = document.getElementById("playlist"); for (let i = 0; i < playList.length; i++) { // 動画の時間計算 var min = Math.floor((playList[i].endSeconds - playList[i].startSeconds) / 60); var sec = ( '00' + (playList[i].endSeconds - playList[i].startSeconds) % 60 ).slice( -2 ); // 要素を記述 var parts = document.createElement("div"); parent.appendChild(parts); var html = '<p><span class="songtitle" id="song'+i+'">'; html += playList[i].title; html += '</span><span class="time">('+min+':'+sec+')</span></p><p class="artist">'; html += playList[i].artist; html += '</p>'; parts.innerHTML += html; parts.classList.add("playparts") parts.addEventListener("click", () => { pushList(i+""); }, false); } } showList(); // リストをクリックされた時の処理 function pushList (id) { if (!isReady) return; document.getElementById("song"+nowPlaying).style.color = 'white'; nowPlaying = Number(id); player.loadVideoById(playList[nowPlaying]); document.getElementById("song"+nowPlaying).style.color = 'red'; } </script> </body> </html>動画リストの設定
playList
の中にオブジェクトを突っ込みます。
videoId
,startSeconds
,endSeconds
はAPIで必要な情報なのでプロパティ名変更不可。
それ以外はリストで表示する為の情報なのでお好きに変更可能です。
プロパティ名 内容 videoId 動画のID。動画のURLに含まれています。(もしURLが「 https://youtu.be/fGvYwVW38mc
」なら「fGvYwVW38mc」がIDです)startSeconds 開始地点の秒数。 endSeconds 終了時点の秒数。動画まるごとの場合は「0」指定でも動きます。 以下、設定例です。
var playList = [ {videoId:'fGvYwVW38mc',startSeconds:0, endSeconds:262, title:'WITHIN', artist:'加賀美ハヤト'}, {videoId:'SSY5z5vUgHA',startSeconds:462, endSeconds:616, title:'どうにもとまらない', artist:'9mm Parabellum Bullet(山本リンダ)'}, {videoId:'Wq8i7lJVYGw',startSeconds:1062,endSeconds:1330, title:'ソラニン', artist:'ASIAN KUNG-FU GENERATION'}, {videoId:'MpBnxbMzaXA',startSeconds:0,endSeconds:260, title:'シュガーソングとビターステップ(with SMC組)', artist:'UNISON SQUARE GARDEN'} ];その他技術的な話
基本的に全部公式リファレンスに書いてあります。というか半分ここのコピペです。
iframe 組み込みの YouTube Player API リファレンス | YouTube IFrame Player API但し動画のデータがオブジェクト型でないとendSecondsが使えません。上記ページのコピペだと最初の動画をオブジェクト型で渡せませんでした。
その為にnew YT.Player
時点では動画を指定せず、onPlayerReady
でcueVideoById
を使っています。
今回は開始時にワンクリック挟みたかったのでわざとcueVideoById
としていますが、loadVideoById
にすると勝手に再生が始まります。最後に
分かってしまえばとても手軽に使えますので、自分の推しのも作りたい!と思った方はご参考にして頂けるととても嬉しいです。
(Twitterで「使ったよ!」と一声頂けたら喜び勇んで見に行きます)
- 投稿日:2020-05-16T18:19:38+09:00
TypeScriptで学ぶデザインパターン〜Memento編〜
対象読者
- デザインパターンを学習あるいは復習したい方
- TypeScriptが既に読めるあるいは気合いで読める方
- いずれかのオブジェクト指向言語を知っている方は気合いで読めると思います
- UMLが既に読めるあるいは気合いで読める方
環境
- OS: macOS Mojave
- Node.js: v12.7.0
- npm: 6.14.3
- TypeScript: Version 3.8.3
本シリーズ記事一覧(随時更新)
- TypeScriptで学ぶデザインパターン〜Iterator編〜
- TypeScriptで学ぶデザインパターン〜Adapter編〜
- TypeScriptで学ぶデザインパターン〜Template Method編〜
- TypeScriptで学ぶデザインパターン〜Factory Method編〜
- TypeScriptで学ぶデザインパターン〜Singleton編〜
- TypeScriptで学ぶデザインパターン〜Prototype編〜
- TypeScriptで学ぶデザインパターン〜Builder編〜
- TypeScriptで学ぶデザインパターン〜Abstract Factory編〜
- TypeScriptで学ぶデザインパターン〜Bridge編〜
- TypeScriptで学ぶデザインパターン〜Strategy編〜
- TypeScriptで学ぶデザインパターン〜Composite編〜
- TypeScriptで学ぶデザインパターン〜Decorator編〜
- TypeScriptで学ぶデザインパターン〜Visitor編〜
- TypeScriptで学ぶデザインパターン〜Chain of Responsibility編〜
- TypeScriptで学ぶデザインパターン〜Facade編〜
- TypeScriptで学ぶデザインパターン〜Mediator編〜
- TypeScriptで学ぶデザインパターン〜Observer編〜
- TypeScriptで学ぶデザインパターン〜Memento編〜
Mementoパターンとは
オブジェクトのある時点における状態の保存や復元を可能にするためのパターンです。
DBのリストアやundoのようなイメージをしておくとわかりやすいと思います。
サンプルコード
Mementoパターンで作られたクラス群がどんなものになるのか確認していきましょう。
今回は、題材として"undoができるエディタ"を想定します。GitHubにも公開しています。
modules/Memento.ts
エディターの状態を表現するクラスです。
Memento.tsexport default class Memento { protected text: string; protected constructor(text: string) { this.text = text; } public getText(): string { return this.text; } }全てのクラスから状態を操作できないようにするため
getText
以外はprotectedになっています。modules/Editor.ts
エディターを表現するクラスです。Mementoを継承しています。
Editor.tsimport Memento from "./Memento"; export default class Editor extends Memento { constructor(text: string) { super(text); } setText(text: string): void { this.text = text; } createMemento(): Memento { const memento: Memento = new Memento(this.text); return memento; } restoreMemento(memento: Memento): void { this.text = memento.getText(); } }
setText
ではエディターでいうところの文字を入力している状態をイメージしてください。
createMemento
では呼び出し時点におけるエディターの状態をMementoに保存しています。
restoreMemento
ではMementoが保持している状態で上書きしてundoを実現します。Main.ts
どの時点のエディターの状態を保持するか等の指示をエディターに出す処理が書かれています。
Main.tsimport Editor from "./modules/Editor"; import Memento from "./modules/Memento"; const editor: Editor = new Editor('こんにちは'); const memento: Memento = editor.createMemento(); console.log(editor.getText()); editor.setText('こんにちは!!!'); console.log(editor.getText()); editor.restoreMemento(memento); console.log(editor.getText());まず、"こんにちは"とエディターに(擬似的に)入力します。次にこの状態でMementoを作成して保存します。
次に、"こんにちは!!!"とエディターに入力します。当然エディアーには"こんにちは!!!"と(擬似的に)表示されています。
最後に、restoreMemento
を呼び出して"こんにちは"という状態に戻します。クラス図
ここまでMementoパターンで作られたクラス群を1つずつ確認してきました。次にクラス図を示します。Mementoパターンの全体像を整理するのにお役立てください。
- Memento: サンプルコードでは
Memento
クラスが対応- Originator: サンプルコードでは
Editor
クラスが対応- Caretaker: サンプルコードでは
Main
が対応※LucidChartを使用して作成
2点補足をします。
1点目はMementoの
getProtectedInfo
とgetPublicInfo
についてです。MementoはCaretakerから一部メソッドを隠蔽しないといけません。たとえば、Mementoの状態を自由自在に変更できてしまうと困ります。以前の状態を保持しておきたいのにその以前の状態がどこからでも操作できてしまうと以前の状態とは言えない状態になってしまうからです。そういう意味で、Mementoに値を設定する処理(サンプルコードだとconstructor
)はOriginatorからしか行えないようになっています。そういった処理はgetProtectedInfo
としてカプセル化します。一方で、Caretakerからアクセスされても問題ない処理(サンプルコードだとgetText
)はpublicで実装しても問題ありません。そういった処理はgetPublicInfo
として定義します。2点目はサンプルコードとクラス図の差異です。サンプルコードではMementoをEditorは継承しています。一方で、クラス図では継承という関係にはなっていません。これには言語の違いが背景にあります。たとえばJavaでは継承を使わずとも「あるパッケージ内からアクセス可能」というアクセス制御が可能です。こういったアクセス制御が可能であることが意味することは、
getProtectedInfo
とgetPublicInfo
を継承を用いずとも分けることができるのです(クラス図通りになる)。1点目であげたアクセス制御は本デザインパターンにとって非常に重要なのでこれが実現できないと困ります。一方で、TypeScriptではJavaであげたように「あるクラスからしかアクセスさせない」といったアクセス制御ができません(※1)。そのため、継承を用いることによってアクセス制御を実現しているのです。(※1)
十分調査したつもりではありますがもし私が誤解していることがあったら修正いたします。解説
最後に、このデザインパターンの存在意義を考えます。
クラス図のところでほとんど解説してしまったのですが、カプセル化を実現しつつ保存や複製が可能になるというメリットがあります。ある状態を保存して復元しようとしたら対象クラスの様々なメソッドがpublicになってしまって、カプセル化の破壊を引き起こしてしまうことがあります。それを避けつつ復元ができるようになるのです。
補足
サンプルコードの実行方法はこちらと同様です。
参考
あとがたり
これ考えた人すごいな。
- 投稿日:2020-05-16T17:15:35+09:00
Vue.jsでアップロードされた画像の中心から正方形にくり抜いてexif情報処理してFirebaseStorageにアップする
自分用
詰め込みすぎだけど色々なアプリで使うため<input type="file" accept="image/*" style="display:none;" @change="upload"/> <canvas id="canvas" hidden></canvas>upload(ev) { const TRIM_SIZE = 512 let blob = null const storageRef = firebase.storage().ref() if (!ev) return const file = ev.target.files[0] if (file.type !== 'image/jpeg' && file.type !== 'image/png') { return } const image = new Image() const reader = new FileReader() reader.onload = e => { image.onload = () => { let width, height, xOffset, yOffset if (image.width > image.height) { height = TRIM_SIZE width = image.width * (TRIM_SIZE / image.height) xOffset = -(width - TRIM_SIZE) / 2 yOffset = 0 } else { width = TRIM_SIZE height = image.height * (TRIM_SIZE / image.width) yOffset = -(height - TRIM_SIZE) / 2 xOffset = 0 } const canvas = $('#canvas') .attr('width', TRIM_SIZE) .attr('height', TRIM_SIZE) const ctx = canvas[0].getContext('2d') ctx.clearRect(0, 0, width, height) let orientation = '' EXIF.getData(file, () => { orientation = file.exifdata.Orientation switch (orientation) { case 2: ctx.transform(-1, 0, 0, 1, width, 0) break case 3: ctx.transform(-1, 0, 0, -1, width, height) break case 4: ctx.transform(1, 0, 0, -1, 0, height) break case 5: ctx.transform(0, 1, 1, 0, 0, 0) break case 6: ctx.transform(0, 1, -1, 0, height, 0) break case 7: ctx.transform(0, -1, -1, 0, height, width) break case 8: ctx.transform(0, -1, 1, 0, 0, width) break default: break } ctx.drawImage(image, xOffset, yOffset, width, height) const base64 = canvas.get(0).toDataURL('image/jpeg') const bin = atob(base64.split('base64,')[1]) const len = bin.length const barr = new Uint8Array(len) let i = 0 while (i < len) { barr[i] = bin.charCodeAt(i) i += 1 } blob = new Blob([barr], { type: 'image/jpeg' }) ctx.clearRect(0, 0, width, height) const uploadRef = storageRef.child(//パス ) uploadRef.put(blob).then(() => { uploadRef.getDownloadURL().then(url => { //URLをどうにかする処理 }) }) }) } image.src = e.target.result } reader.readAsDataURL(file) },
- 投稿日:2020-05-16T16:37:54+09:00
Vue.js文法チートシート
Vue.jsとは
Vue.jsはUI構築のためのJavaScriptプログレッシブフレームワークのこと
Vue.js公式サイト基本的な書き方
HTML<div id="app"> <!-- Vue.js適用範囲 --> </div>Vuevar vm = new Vue({ el: '#app' });オプション
el
Vue.jsの機能を適用するDOMの指定
See the Pen
Vue.js_el by engineerhikaru (@engineerhikaru)
on CodePen.
data
データを保持する(データの変数化)。{{ 変数名 }}で出力可能
See the Pen
Vue.js_data by engineerhikaru (@engineerhikaru)
on CodePen.
methods
v-onのイベントハンドラとして使用
See the Pen
Vue.js_ methods by engineerhikaru (@engineerhikaru)
on CodePen.
computed
算出プロパティ。データの自動更新をし、計算結果はキャッシュに保存。getでは必ず戻り値が必要
See the Pen
Vue.js_ computed by engineerhikaru (@engineerhikaru)
on CodePen.
watch
監視プロパティ。特定のプロパティの値の変更を監視し、変更時に設定した関数を実行
See the Pen
Vue.js_watch by engineerhikaru (@engineerhikaru)
on CodePen.
template
文字列,HTMLコードをテンプレート化する。コンポーネント機能と共に使用
See the Pen
Vue.js_template by engineerhikaru (@engineerhikaru)
on CodePen.
props
コンポーネント機能でデータを参照する
See the Pen
Vue.js_props by engineerhikaru (@engineerhikaru)
on CodePen.
created
インスタンスが作成された後(DOMは生成されていない)に実行するオプション
See the Pen
Vue.js_created by engineerhikaru (@engineerhikaru)
on CodePen.
mounted
DOMが生成された後に実行するオプション
See the Pen
Vue.js_mounted by engineerhikaru (@engineerhikaru)
on CodePen.
ディレクティブ
v-text
テキストデータを出力
See the Pen
Vue.js_v-text by engineerhikaru (@engineerhikaru)
on CodePen.
v-html
HTMLデータを出力
See the Pen
Vue.js_v-html by engineerhikaru (@engineerhikaru)
on CodePen.
v-show
条件分岐(createdフックは最初のみ実行)
See the Pen
Vue.js_v-show by engineerhikaru (@engineerhikaru)
on CodePen.
v-if, v-if-else, v-else
条件分岐
See the Pen
Vue.js_v-if by engineerhikaru (@engineerhikaru)
on CodePen.
v-for
ループする
See the Pen
Vue.js_v-for by engineerhikaru (@engineerhikaru)
on CodePen.
v-on, @
イベントを処理する
See the Pen
Vue.js_v-on by engineerhikaru (@engineerhikaru)
on CodePen.
v-bind, :
HTMLの属性を指定する
See the Pen
Vue.js_v-bind by engineerhikaru (@engineerhikaru)
on CodePen.
v-model
入力データと指定したデータをリンクする
See the Pen
Vue.js_v-model by engineerhikaru (@engineerhikaru)
on CodePen.
v-pre
{{}}をそのまま表示する
See the Pen
Vue.js_v-pre by engineerhikaru (@engineerhikaru)
on CodePen.
v-cloak
{{}}が表示されるのを防ぐ(最初に表示される事がある)
See the Pen
Vue.js_v-cloak by engineerhikaru (@engineerhikaru)
on CodePen.
v-once
レンダリングを一度だけ実行(データの変更を適用しない)
See the Pen
Vue.js_v-once by engineerhikaru (@engineerhikaru)
on CodePen.
グローバル設定
silent
警告,エラーメッセージの表示,非表示を指定する
VueVue.config.silent = true;この内容について
この内容は、私が運営しているサイトに、より見やすく掲載しているので、よければそちらもご活用ください。
Vue.jsチートシート | コレワカ
- 投稿日:2020-05-16T16:31:34+09:00
【超基礎】PythonとJavaとJavaScriptを比較します(変数, if文, while文, for文)
目的
- 今週から本配属だった
- 結局Javaを勉強することになった
- 記憶が新しいうちにJavaとPythonでちがったことまとめておこう
- ついでに少し勉強したことがあるJavaScriptも引っ張り出して比較しよう
この記事で比較する内容
- 変数
- if文
- while文
- for文
変数
Python
- データ型を宣言する必要はありません
- 変数の中身によって勝手に型を指定してくれます
- セミコロンもいりません
asobi.pypy_str = "Python" py_int = 23 py_float = 23.5 # データ型確認 print(type(py_str)) print(type(py_int)) print(type(py_float))コンソール<class 'str'> <class 'int'> <class 'float'>
- 変数のデータ型を書きかえることもできます
asobi.pypy_str = "Python" py_int = 23 print(type(py_str)) py_str = 10 print(type(py_str)) py_cal = py_str * py_int print(py_cal)コンソール<class 'str'> <class 'int'> 230Java
- データ型を宣言しなければなりません
- float型もありますが、主にdouble型を使うようです
- 直接的にデータ型を確認する方法は見つかりませんでした(instanceofというメソッドがあるようですが、intやdoubleには使えません)
- Javaのデータ型についてはこちらのサイトがわかりやすいです
- セミコロンを忘れずに
asobi.javapublic class asobi { public static void main(String[] args) { String javaStr = "Java"; int javaInt = 23; double javaFloat = 23.5; } }JavaScript
- データ型の宣言は必要ありませんが、変数名の前にvarをつける必要があります
- int型やfloat型ではなくどちらもnumber型になるんですね…
- セミコロンもいります
asobi.html<html> <script> var jsStr = "JavaScript"; var jsInt = 23; var jsFloat = 23.5; // データ型確認 console.log(typeof(jsStr)); console.log(typeof(jsInt)); console.log(typeof(jsFloat)); </script> </html>コンソールstring number number
if文
Python
- if, elif, elseが使われます
- 条件の末尾に:を付けます
- 実行する文のインデントは必須です
asobi.pyage = 23 if age >= 65: print("定年です") elif age >= 20: print("成人です") else: print("未成年です")Java
- if, else if, elseが使われます
- それぞれの条件を(), 実行する文を{}で囲います
asobi.javapublic class asobi { public static void main(String[] args) { int age = 23; if (age >= 65) { System.out.println("定年です"); }else if (age >= 20) { System.out.println("成人です"); }else{ System.out.println("未成年です"); } } }JavaScript
- if, else if, elseが使われます
- それぞれの条件を(), 実行する文を{}で囲います
- if文の形自体はJavaと同じですね
asobi.html<html> <script> var age = 23; if (age >= 65) { console.log("定年です"); }else if (age >= 20){ console.log("成人です"); }else{ console.log("未成年です"); } </script> </html>while文
- それぞれの言語で以下のようにコンソール表示させてみましょう
コンソール0です 1です 2です 3です 4です
Python
- int型であるiと文字列をそのままつなげることができないので、iをstr型にします
asobi.pyi = 0 while i < 5: print(str(i) + "です") i += 1Java
- 例によって条件を()、実行文を{}で囲います
- インクリメントでi++という形を使うことができます
asobi.javapublic class asobi { public static void main(String[] args) { int i = 0; while (i < 5){ System.out.println(i + "です"); i++; } } }JavaScript
- 例によってwhile文の形自体はJavaと同じです
asobi.html<html> <script> var i = 0; while (i < 5){ console.log(i + "です"); i++; } </script> </html>for文
- それぞれの言語で以下のようにコンソール表示させてみましょう
コンソール0です 1です 2です 3です 4です
Python
- iが0から4までの5回の範囲で繰り返されます
asobi.pyfor i in range(5): print(str(i) + "です")Java
- for文の繰り返し条件が(初期値; 範囲; 増減)で表されます
- 初期値を格納する変数はデータ型を宣言する必要があります
asobi.javapublic class asobi { public static void main(String[] args) { for (int i = 0; i < 5; i++){ System.out.println(i + "です"); } } }JavaScript
- 例によってfor文の形自体はJavaと同じです
- 初期値を格納する変数名の前にはvarを付ける必要があります
asobi.html<html> <script> for (var i = 0; i < 5; i++){ console.log(i + "です"); } </script> </html>感想
- 複数言語を勉強するとき、比較しながら勉強すると覚えやすい
- JavaとJavaScript、インドとインドネシア並にちがうといいつつ、書き方結構似てるんだな
- やっぱりPythonがいちばん書きやすい!
- 投稿日:2020-05-16T15:55:38+09:00
Vue.jsで東京都の今日の天気を表示するWebサイトを作るコピペサンプル
簡単なサンプルです。
Vue.jsとaxiosで簡単なデータ連携をします。
作るもの
こんな感じでボタンを押すと今日の天気を表示するWebサイトを作ります。
使うAPI
OpenWhethermapのAPIを作います。無料である程度(雑)使えます。
https://openweathermap.org/api
APIキーを取得しましょう。
ログイン後ここにアクセスするとキーが表示されます。
コピペ用コード
一箇所だけ、変更が必要です。
appid=ここにOpenWeathermapのAPIキーを指定
と書いているところに自身で取得したAPIキーを指定しましょう。index.html<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div id="app"> <p>今日の天気は{{ weather }}で、気温は{{ temp }}度です。</p> <button v-on:click="getData()">今日の東京の天気をAPIで取得!</button> <p> <a href="https://openweathermap.org/api">openweathermapから取得しています。</a> </p> </div> <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script> <script src="https://unpkg.com/axios/dist/axios.min.js"></script> <script> var app = new Vue({ el: '#app', data: { weather: 'xxxx', temp: 'yyyy' }, methods: { getData: async function(){ const URL = `https://api.openweathermap.org/data/2.5/weather?id=1850147&units=metric&appid=ここにOpenWeathermapのAPIキーを指定`; const response = await axios.get(URL); this.weather = response.data.weather[0].main; this.temp = response.data.main.temp; // console.log(response.data); }, }, // mounted: function(){ // } }) </script> </body> </html>試す。
ローカルのindex.htmlをブラウザで開いたり、live severやどこかにデプロイしたりして挙動確認をしましょう。
- 投稿日:2020-05-16T15:14:06+09:00
javascriptメモ
概要
javascript初めてなので、公式?を見ながら基本文法の確認した。
保存
//モジュール化されたJavaScriptファイル
const { NearestScanner } = require('@toio/scanner')// 制御フロー
// https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Loops_and_iteration
function test_1a(){
let x = 0;
console.log(ループ開始前のxの値: ${x}
);
while (x < 10) {
console.log(x);
x += 1;
}
console.log(ループ終了後のxの値: ${x}
);for (let step = 0; step < 5; step++) {
// 値が 0 から 4 まで計 5 回実行される
console.log('一歩西に歩く');
}
}function test_1b(){
fruittype ='Apples'
switch (fruittype) {
case 'Oranges':
console.log('Oranges are $0.59 a pound.');
break;
case 'Apples':
console.log('Apples are $0.32 a pound.');
break;
default:
console.log(Sorry, we are out of ${fruittype}.
);
}
console.log("Is there anything else you'd like?");}
function test_1c()
{
let condition = true
let t_val = 10
if (condition) {
t_val = 1
}else if (condition == false) {
t_val = 2
}else {
t_val = 3
}
}function test_1d()
{
let x = 0;
let z = 0;labelCancelLoops:
while (true) {
console.log('外側のループ: ' + x);
x += 1;
z = 1;
while (true) {
console.log('内側のループ: ' + z);
z += 1;
if (z === 3 && x === 3) {
break labelCancelLoops;
} else if (z === 3) {
break;
}
}
}}
function test_1(){
test_1a()
console.log('------------------------------');
test_1b()
console.log('------------------------------');
test_1c()
console.log('------------------------------');
test_1d()
console.log('------------------------------');}
//https://www.sejuku.net/blog/61660
//【JavaScript入門】exitの代わりにtry catchでプログラムを終了する
function test2(){const flag = true;
try {
if (flag) {
throw new Error('終了します');
}
console.log('実行されないコード');
} catch (e) {
console.log(e.message);
}
}//--------------------------------------------
//変数//構造体の代わり
function Position(x, y) {
this.x = x;
this.y = y;
}/*
複数行のコメントアウト:入れ子のコメントは できない。JavaScript は大文字と小文字を区別し、また Unicode 文字セットを使用しています。
文が単独の行で書かれている場合、文の後にセミコロンは必要ではありません。
*/
function test_3a(){
// strict モードも存在する
var val1 = 1
const flag = true
const val100 = 100let local_val = 100; //ブロックスコープのローカル変数を宣言し
let szName ="APPLE"
let szNameKANJI ="漢字UTFらしい"
console.log(szNameKANJI);//配列
const arr = [3, 5, 7];
// inとofの違い
for (let i in arr) {
console.log(i); // "0", "1", "2", "foo" が出力される
}
for (let i of arr) {
console.log(i); // 3, 5, 7 が出力される
}
//配列
var array = [];
array.push('hoge')
array.push('fuga')//DICT,連想配列
var mycar = {make: "Honda", model: "Accord", year: 1998};
console.log(mycar);
console.log(mycar['make'] +" "+ mycar['model'] +" "+ mycar['year'] );var obj = {};
obj['hoge'] = 'moge';
console.log(obj['hoge']);// //構造体
console.log("構造体") // Position {x: 0, y: 1}//JavaScriptには俗に「構造体」と呼ばれるものはありません。(ないよね?)
console.log(new Position(0, 1)); // Position {x: 0, y: 1}
let t_pos = new Position(9, 99)
console.log(t_pos.x)//console.log(Position(0, 1)); // undefined
}
function test_3(){
test_3a()
}//--------------------------------------------
// 他の関数は非 Strict Mode になる。
// 'use strict';
//
/* Strict Mode 関数 */
function strict() {
// 関数レベル strict mode 文法
'use strict';
function nested() { return "ここでも Strict Mode."; }
return "Strict Mode 関数 " + nested();
}function test_4(){
let result = strict()
console.log(result)
//notStrict()
}//--------------------------------------------
function square(number) {
return number * number;
}function test_5a(val) {
val = val + 1
}
function test_5b(arr) {
arr[0] = 99
}function test_5c(theObject) {
theObject.make = "Toyota";
}//関数呼び出し
function test_5b(){
var myFunc;
//local関数定義
myFunc = function(theObject) {
theObject = "Toyota"
return theObject
}console.log(myFunc());
}
function loop(x) {
if (x >= 10) // "x >= 10" が終了条件 ("!(x < 10)" と同等)
return x;
// 何らかの処理を行う
return loop(x + 1); // 再帰呼出し
}function multiply(a, b = 1) {
return a*b;
}// https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Functions
function Person() {
var self = this; //self
の代わりにthat
を選ぶ人もいます。
// どちらか一方を選び、そちらだけを使うようにしましょう。
self.age = 0;// タイマー処理みたい
setInterval(function growUp() {
// このコールバックは、その値が期待通りのオブジェクトを指す
// 変数self
を参照している。
self.age++;
console.log(self.age )}, 1000);
}function test_5a(){
console.log("square " + square(2))// プリミティブなパラメータ(数値など)は値渡しで関数に渡されます。
let t_val = 2
test_5a(t_val)
console.log("引数の参照、値渡しの確認: 2を関数内で+1してreturn -> " + t_val)const arr = [3, 5, 7];
console.log("関数呼び出し前" + arr)
test_5b(arr)
console.log("関数で配列の値を変更" + arr)// デフォルト引数
multiply(5); // 5}
function test_5c(){
var p = new Person();
//p.setInterval()
console.log("new Person " + p.age )}
// 関数呼び出し
function test_5(){
test_5b()t_x = loop(0) // 再帰
console.log("再帰の結果 " + t_x)test_5c()
}
//--------------------------------------------
// 数の表現、NULL
function JSClock() {
var time = new Date();
var hour = time.getHours();
var minute = time.getMinutes();
var second = time.getSeconds();
var temp = '' + ((hour > 12) ? hour - 12 : hour);
if (hour == 0)
temp = '12';
temp += ((minute < 10) ? ':0' : ':') + minute;
temp += ((second < 10) ? ':0' : ':') + second;
temp += (hour >= 12) ? ' P.M.' : ' A.M.';
return temp;
}function test_6a()
{
//1E3 // 1000
//2e6 // 2000000
//0.1e2 // 10let val = 0XA
console.log( val )
val = 1E3
console.log( val )
val = 0.1e2 //
console.log( val )
val = 2e3 // 2x 10^3
console.log( val )// MAX_MIN
var biggestNum = Number.MAX_VALUE;
var smallestNum = Number.MIN_VALUE;
var infiniteNum = Number.POSITIVE_INFINITY;
var negInfiniteNum = Number.NEGATIVE_INFINITY;
var notANum = Number.NaN;
console.log( biggestNum )// 文字列から数字
val = Number.parseInt("5155")
console.log( val )//deg=rad∗(180/π)
//rad = deg * PI / 180console.log( Math.PI )
let rad_val = 0
rad_val = 90 * Math.PI / 180
console.log( Math.sin(rad_val) )//日付
var Xmas95 = new Date("December 25, 1995");
var today = new Date();
console.log( today )}
function test_6()
{
test_6a()
console.log( JSClock() )
}function test_7a()
{
var arr = [42]; // 42という数の要素を
// 1個だけ持つ配列が作られる。var arr = Array(42); // 要素がなく、arr.length が
// 42に設定された配列が作られる。
// 以下のコードと同様。
var arr = [];
arr.length = 42;let myVar = "moge"
var myArray = new Array('Hello', myVar, 3.14159);
console.log( myArray )var cats = [];
cats[30] = ['Dusty'];
console.log("length")
console.log(cats.length); // 31}
// 多次元配列
function test_7b()
{
var a = new Array(4);
for (i = 0; i < 4; i++) {
a[i] = new Array(4);
for (j = 0; j < 4; j++) {
//a[i][j] = '[' + i + ', ' + j + ']';
a[i][j] = i*4+j
}
}
console.log(a); // 31}
//https://developer.mozilla.org/ja/docs/Web/JavaScript/Guide/Indexed_collections
//型付き配列
function test_7c()
{}
function test_7()
{test_7a()
test_7b()
test_7c()
}// map
function test_8a()
{
var sayings = new Map();
sayings.set('dog', 'woof');
sayings.set('cat', 'meow');
sayings.set('elephant', 'toot');
sayings.size; // 3
sayings.get('fox'); // undefined
sayings.has('bird'); // false
sayings.delete('dog');
sayings.has('dog'); // falsefor (var [key, value] of sayings) {
console.log(key + ' goes ' + value);
}
// "cat goes meow"
// "elephant goes toot"sayings.clear();
sayings.size; // 0}
function test_8()
{test_8a()
}//プロパティの列挙
function showProps(obj, objName) {
var result = '';
for (var i in obj) {
// obj.hasOwnProperty() はオブジェクトのプロトタイプチェーンからプロパティを絞り込むために使用しています
if (obj.hasOwnProperty(i)) {
//result += objName + '.' + i + ' = ' + obj[i] + '\n';
result += i + ",";
}
}
return result;
}
// Object型
function test_9a()
{
var myCar = new Object();
myCar.make = 'Ford';
myCar.model = 'Mustang';
myCar.year = 1969;
//console.log(myCar)let result = showProps(myCar, "Mustang");
console.log("--")
console.log(result)}
//コンストラクタ関数
function Car(make, model, year) {
this.make = make;
this.model = model;
this.year = year;
}function test_9b()
{
var mycar = new Car('Eagle', 'Talon TSi', 1993);
console.log(mycar)
}function test_9c()
{
console.log("------------- test_9c -------------")function Employee() {
this.name = '';
this.dept = 'general';
}function Manager() {
Employee.call(this);
this.reports = [];
}
Manager.prototype = Object.create(Employee.prototype);
Manager.prototype.constructor = Manager;function WorkerBee() {
Employee.call(this);
this.projects = ["A", "B", "C"];
}
WorkerBee.prototype = Object.create(Employee.prototype);
WorkerBee.prototype.constructor = WorkerBee;var jim = new Employee;
var bee = new WorkerBee;
var boss = new Manager;console.log(jim)
console.log(bee)
console.log(boss)}
//クラスとはちがうのか -> 「JavaScript は、クラスではなく、プロトタイプに基づいたオブジェクトベースの言語です」
function test_9()
{
try {
test_9a()
test_9b()
test_9c()console.log("------------- end of test9 -------------") let val = 1 val = val / 0 //例外発生しないみたい。 throw new Error('終了します');} catch (e) {
console.log("in catch");
console.log(e.message);
}
}
async function main() {
console.log(in main -----------------
)
test_9()
console.log(out main ---------------------
)
}main()
- 投稿日:2020-05-16T14:09:26+09:00
GitHubスター数ランキング上位プロジェクトの一口解説 2020年5月
GitHubRanking
ふと思い立って、2020年5月16日時点のGitHubのスター数上位レポジトリを出したところ、結構知らないレポジトリが多かったので軽く調べてみました。
何か気づいたことがあれば修正依頼していただけると幸いです。
1位 freeCodeCamp/freeCodeCamp JavaScript star 310,728
無料のコードキャンプ
フルスタックWeb開発者になるためのコースが揃っている。Learn to code at home | freeCodeCamp.org
2位 996icu/996.ICU Rust star 249,552
中国の労働問題を訴えるOSS活動
最終的に集中治療室(ICU)送りになるリスクを伴いながら午前9時から午後9時まで、週6日働く
中国の長時間労働にスタートアップが反撃 | TechCrunch Japan
3位 vuejs/vue JavaScript star 164,049
SPAを作るためのJavaScriptライブラリ/フレームワーク
TypeScriptとの相性の悪さが指摘されがち。
アリババが支援しているので中国での人気が高い。
中国でも求人数は React > Vue4位 facebook/react JavaScript star 148,705
Hooks
という機能が2019年に追加された。SPAのデファクトスタンダードNPMでのダウンロード数ではSPA御三家で圧倒的一位
ReactはウェブやHTMLとは特に関係のないライブラリです - Qiita
Reactはウェブ向けライブラリとして登場した
なんかReact Nativeとかいうのも出てきた
React NativeとうまくやるためにReact自体は2015年にウェブから完全に独立した
ウェブ向け機能はreact-domってライブラリがやるようになった
Reactに残った機能はコンポーネントのツリー管理とか差分検出システムぐらい
ウェブとかHTMLに依存してないからなんでも作れるようになったReact使っている人の方が強そう(小並感)
5位 EbookFoundation/free-programming-books - star 147,578
無料で利用できる学習リソースまとめ。GitHub Pagesで公開されている。
日本語リソースはこれ:Index | free-programming-books-ja
6位 tensorflow/tensorflow C++ star 144,362
Google開発の機械学習・ディープラーニング用ライブラリ
ブラウザで動くTensorFlow.jsもある
7位 twbs/bootstrap JavaScript star 140,787
世界で最も人気のあるCSSフレームワーク
Bootstrap 5のリリースはもうすぐみたい!注目の新機能、jQueryは削除、IE10のサポートは終了へ | コリス
8位 sindresorhus/awesome - star 133,119
役に立つ情報や素晴らしいライブラリのリスト。
フレームワークやライブラリの候補を洗い出す作業を短縮できる。
フォーマットが決まっているので二次利用しやすい。9位 getify/You-Dont-Know-JS - star 121,215
JavaScriptのテキスト
JavaScriptのレベル別書籍のまとめ10位 jwasham/coding-interview-university - star 115,174
コーディング面接に必要な知識と学習リソースへのリンクのリスト
完全習得すればAmazonで働けるかも -> I've Been Acquired by Amazon
10位以下で気になったもの
19位 d3/d3 JavaScript star 91,500
データドリブンでインタラクティブなSVGグラフを描画するJavaScriptライブラリ
Pythonの可視化ツールにも使われていることが多い(Plotly、Bokehとか)12位 kamranahmedse/developer-roadmap - star 108,176
毎年1月ごろに更新される、Web開発者向けロードマップ
Web技術・ツールを俯瞰できるのでサラッと読むと面白いバックエンドエンジニアのロードマップ in2020 - Qiita
私からあなたへ 一人前のJavaエンジニアになるためのロードマップを送ろう - Qiitaまとめ系
- 13位 github/gitignore - star 101,595
- gitignoreのテンプレ
- 25位 public-apis/public-apis Python star 82,044
- 無料で使えるWebAPIをカテゴリごとにまとめています
中国すごい
- 14位 CyC2018/CS-Notes Java star 101,029
- 技術面接対策用のテキストサイト(中国語)
- 22位 jackfrued/Python-100-Days Jupyter
- Pythonを習得するための100日分の教材とソースコード(中国語)
- 26位 Snailclimb/JavaGuide Java star 78,774
- Javaの学習教材(中国語)
中国語しか対応してなくてもこのスター数。総人口10億人越えのパワーをみた。
多言語対応教材(日本語もあり)
- 17位 donnemartin/system-design-primer Python star 95,326
- システム設計の学習資料。圧倒的な量。親切にも暗記カード付き。
- 28位 jlevy/the-art-of-command-line - star 75,368
- コマンドラインを使いこなすためのさまざまな情報やTipsをまとめたGitHub上のドキュメント
クロスプラットフォーム
- 18位 flutter/flutter Dart star 92,484
- 21位 facebook/react-native JavaScript star 87,215
いつの間にかFlutterがRNを抜いていたんですね。
ReactはウェブやHTMLとは特に関係のないライブラリです - Qiita
全体ランキング
- 投稿日:2020-05-16T14:03:45+09:00
非同期通信の初回挙動不良の対処方法
なんで動かないの!?
アプリ作成をしていてjavaScriptで非同期やインクリメンタルサーチをしたけど、なぜか初回の挙動だけがうまくいってくれない、、!なんてなことないですか??
今回は同じ事象で困っている人のために解消方法を紹介します。
turbolinksを停止しよう
turbolinksとはgemとしてRailsアプリケーションに導入されている機能です。
今回の挙動の動作不良は手作業で作成したAjaxとturbolinksが競合してしまい、うまく作動しない可能性が考えられます。1.Gemfileからturbolinksの部分をコメントアウトする
gem 'turbolinks', '~> 5' < --- コメントアウトしましょう --- > # gem 'turbolinks', '~> 5'bundleinstallも忘れず実行する
$ bundle install2.application.html.hamlからturbolinksの関連部分を削除する
application.html.haml!!! %html %head %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/ %title PracticeApp = csrf_meta_tags / 修正前 = stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' ← このオプションを消す = javascript_include_tag 'application', 'data-turbolinks-track': 'reload' ← このオプションを消す / 修正後 = stylesheet_link_tag 'application', media: 'all' = javascript_include_tag 'application' %body = render "layouts/notifications" = yield3.application.jsからturbolinksの関連部分を削除する
//= require jquery //= require jquery_ujs //= require turbolinks <--- こいつを消してあげる //= require_tree .以上
- 投稿日:2020-05-16T13:34:32+09:00
テンプレートリテラル記法を用いるとデプロイ時にエラーが出る
背景
個人アプリをデプロイしようとしたが、エラーが出てうまくいかない。。。
javaScriptでテンプレートリテラル記法を用いた記述をするとどうやらエラーが出るみたい。解決策
修正前
config/environments/production.rbconfig.assets.js_compressor = :uglifier修正後
config/environments/production.rb# config.assets.js_compressor = :uglifier
Uglifierというgemがあり、これはJavaScriptを軽量化するためのものです。
今回のアプリ中では、JavaScriptで使用しているテンプレートリテラル記法(`)に対応していません。
そのため、デプロイ時にエラーの原因となります。
上記部分をコメントアウトすることで解決します。以上
- 投稿日:2020-05-16T13:07:58+09:00
javascriptでradioボタンにハマった話
結論
radioボタンのプロパティは最初にtypeを設定しよう。
経緯
javascriptではcreateElementを使ってコンテンツを作る事はよくあると思う。
例えばこんなのradio.js//フォームを生成 var eForm1 = document.createElement('form'); //ラジオボタンを生成 var eRadio1 = document.createElement('input'); eRadio1.value = '男'; eRadio1.type = 'radio'; eRadio1.name = 'gender'; eRadio1.checked = true; var eRadio2 = document.createElement('input'); eRadio2.value = '女'; eRadio2.type = 'radio'; eRadio2.name = 'gender'; var eRadio3 = document.createElement('input'); eRadio3.value = 'その他'; eRadio3.type = 'radio'; eRadio3.name = 'gender'; eForm1.appendChild(eRadio1); eForm1.appendChild(eRadio2); eForm1.appendChild(eRadio3); document.body.appendChild(eForm1);次のようにするとチェックを入れた部分の内容(value)を取得できる。
//elementsを取得 var elements = document.getElementsByName( "gender" ) ; //valueを取得 for ( var output = "", i = elements.length; i--; ) { if ( elements[i].checked ) { var output = elements[i].value ; break ; } } alert(output);性別がonでは困る。
ナゼだろう?
しばらく試行錯誤してtypeの位置が悪い事が分かった。
検証コードを書いて詳しく調べるとtypeを設定する前にvalueを設定するとvalueが無視される事が分かった。radio1.jsfunction init(){ //フォームを作る var eForm = document.createElement('form'); //vtnc var eRadio = document.createElement('input'); eRadio.value = 'vtnc'; eRadio.type = 'radio'; eRadio.name = 'vtnc'; eRadio.checked = true; eForm.appendChild(eRadio); //vtcn var eRadio = document.createElement('input'); eRadio.value = 'vtcn'; eRadio.type = 'radio'; eRadio.checked = true; eRadio.name = 'vtcn'; eForm.appendChild(eRadio); //vntc var eRadio = document.createElement('input'); eRadio.value = 'vntc'; eRadio.name = 'vntc'; eRadio.type = 'radio'; eRadio.checked = true; eForm.appendChild(eRadio); //vnct var eRadio = document.createElement('input'); eRadio.value = 'vnct'; eRadio.name = 'vnct'; eRadio.checked = true; eRadio.type = 'radio'; eForm.appendChild(eRadio); //vctn var eRadio = document.createElement('input'); eRadio.value = 'vctn'; eRadio.checked = true; eRadio.type = 'radio'; eRadio.name = 'vctn'; eForm.appendChild(eRadio); //vcnt var eRadio = document.createElement('input'); eRadio.value = 'vcnt'; eRadio.checked = true; eRadio.name = 'vcnt'; eRadio.type = 'radio'; eForm.appendChild(eRadio); //tvnc var eRadio = document.createElement('input'); eRadio.type = 'radio'; eRadio.value = 'tvnc'; eRadio.name = 'tvnc'; eRadio.checked = true; eForm.appendChild(eRadio); //tvcn var eRadio = document.createElement('input'); eRadio.type = 'radio'; eRadio.value = 'tvcn'; eRadio.checked = true; eRadio.name = 'tvcn'; eForm.appendChild(eRadio); //tncv var eRadio = document.createElement('input'); eRadio.type = 'radio'; eRadio.name = 'tncv'; eRadio.checked = true; eRadio.value = 'tncv'; eForm.appendChild(eRadio); //tnvc var eRadio = document.createElement('input'); eRadio.type = 'radio'; eRadio.name = 'tnvc'; eRadio.value = 'tnvc'; eRadio.checked = true; eForm.appendChild(eRadio); //tcvn var eRadio = document.createElement('input'); eRadio.type = 'radio'; eRadio.checked = true; eRadio.value = 'tcvn'; eRadio.name = 'tcvn'; eForm.appendChild(eRadio); //tcnv var eRadio = document.createElement('input'); eRadio.type = 'radio'; eRadio.checked = true; eRadio.name = 'tcnv'; eRadio.value = 'tcnv'; eForm.appendChild(eRadio); //nvtc var eRadio = document.createElement('input'); eRadio.name = 'nvtc'; eRadio.value = 'nvtc'; eRadio.type = 'radio'; eRadio.checked = true; eForm.appendChild(eRadio); //nvct var eRadio = document.createElement('input'); eRadio.name = 'nvct'; eRadio.value = 'nvct'; eRadio.checked = true; eRadio.type = 'radio'; eForm.appendChild(eRadio); //ntcv var eRadio = document.createElement('input'); eRadio.name = 'ntcv'; eRadio.type = 'radio'; eRadio.checked = true; eRadio.value = 'ntcv'; eForm.appendChild(eRadio); //ntvc var eRadio = document.createElement('input'); eRadio.name = 'ntvc'; eRadio.type = 'radio'; eRadio.value = 'ntvc'; eRadio.checked = true; eForm.appendChild(eRadio); //ncvt var eRadio = document.createElement('input'); eRadio.name = 'ncvt'; eRadio.checked = true; eRadio.value = 'ncvt'; eRadio.type = 'radio'; eForm.appendChild(eRadio); //nctv var eRadio = document.createElement('input'); eRadio.name = 'nctv'; eRadio.checked = true; eRadio.type = 'radio'; eRadio.value = 'nctv'; eForm.appendChild(eRadio); //cvtn var eRadio = document.createElement('input'); eRadio.checked = true; eRadio.value = 'cvtn'; eRadio.type = 'radio'; eRadio.name = 'cvtn'; eForm.appendChild(eRadio); //cvnt var eRadio = document.createElement('input'); eRadio.checked = true; eRadio.value = 'cvnt'; eRadio.name = 'cvnt'; eRadio.type = 'radio'; eForm.appendChild(eRadio); //ctvn var eRadio = document.createElement('input'); eRadio.checked = true; eRadio.type = 'radio'; eRadio.value = 'ctvn'; eRadio.name = 'ctvn'; eForm.appendChild(eRadio); //ctnv var eRadio = document.createElement('input'); eRadio.checked = true; eRadio.type = 'radio'; eRadio.name = 'ctnv'; eRadio.value = 'ctnv'; eForm.appendChild(eRadio); //cnvt var eRadio = document.createElement('input'); eRadio.checked = true; eRadio.name = 'cnvt'; eRadio.value = 'cnvt'; eRadio.type = 'radio'; eForm.appendChild(eRadio); //cntv var eRadio = document.createElement('input'); eRadio.checked = true; eRadio.name = 'cntv'; eRadio.type = 'radio'; eRadio.value = 'cntv'; eForm.appendChild(eRadio); document.body.appendChild(eForm); echoRadio('vtnc'); echoRadio('vtcn'); echoRadio('vntc'); echoRadio('vnct'); echoRadio('vctn'); echoRadio('vcnt'); echoRadio('tvnc'); echoRadio('tvcn'); echoRadio('tncv'); echoRadio('tnvc'); echoRadio('tcvn'); echoRadio('tcnv'); echoRadio('nvtc'); echoRadio('nvct'); echoRadio('ntcv'); echoRadio('ntvc'); echoRadio('ncvt'); echoRadio('nctv'); echoRadio('cvtn'); echoRadio('cvnt'); echoRadio('ctvn'); echoRadio('ctnv'); echoRadio('cnvt'); echoRadio('cntv'); } function echoRadio(elementName){ //要素を取得 var elements = document.getElementsByName( elementName ) ; //内容を取得 var output; for ( output = "", i = elements.length; i--; ) { if ( elements[i].checked ) { output = elements[i].value ; break ; } } //内容を出力 document.body.insertAdjacentHTML ( 'beforeend', elementName + ' = '+ output + '<br>'); } window.addEventListener('load',init,false);radioボタン以外はどうだろうね?要検証。
- 投稿日:2020-05-16T12:43:16+09:00
Denoのマニュアルで気になった所を試してみる
Deno
最近よく目にするので、何はともあれ触ってみようかという事で
マニュアルを元にザッと気になる所を試して見たいと思います。環境構築
Denoの公式Docker(?)を見つけたのでそちらで環境を構築して見たいと思います。
https://hub.docker.com/r/hayd/denoワンランナーでさくっと試してみます。
$ docker run --rm -it hayd/alpine-deno run https://deno.land/std/examples/welcome.ts Welcome to Deno ?色々試してみる
簡単なサンプル
公式にも載っていた以下サンプルをローカルに
server.ts
として作成し、起動させてみます。import { serve } from "https://deno.land/std@0.50.0/http/server.ts"; const s = serve({ port: 8000 }); console.log("http://localhost:8000/"); for await (const req of s) { req.respond({ body: "Hello World\n" }); }$ docker run --rm -it --init -p 8000:8000 -v $PWD:/app hayd/alpine-deno run /app/server.ts ... error: Uncaught PermissionDenied: network access to "0.0.0.0:8000", run again with the --allow-net flagPremissionが無いと怒られました。Denoはデフォルトでファイル、ネットワーク、環境変数等のアクセスが
許可されてないので、--allow-net
を付けて再度実行してみます。$ docker run --rm -it --init -p 8000:8000 -v $PWD:/app hayd/alpine-deno run --allow-net /app/server.ts今度は実行でき
http://localhost:8000/
にアクセスするとちゃんとHello World
が返ってきましたファイル読み込み
こちら を試して見たいと思います。
cat.ts
として以下を作成します。const filenames = Deno.args; for (const filename of filenames) { const file = await Deno.open(filename); await Deno.copy(file, Deno.stdout); file.close(); }読み込み用のファイル
sample.txt
を以下の内容で作成。Deno is a simple, modern and secure runtime for JavaScript and TypeScript that uses V8 and is built in Rust.デフォルトだとファイル読み込みもPremissionが無いので
--allow-read
を付けて実行します。$ docker run --rm -it --init -v $PWD:/app hayd/alpine-deno run --allow-read /app/cat.ts /app/sample.txt > Deno is a simple, modern and secure runtime for JavaScript and TypeScript that uses V8 and is built in Rust.ちゃんと読み込めてそうです
dependency inspector
とformatter
Denoには
dependency inspector
とformatter
がビルドインされているらしいので試してみます。
dependency inspector
$ docker run --rm -it --init hayd/alpine-deno info DENO_DIR location: "/deno-dir/" Remote modules cache: "/deno-dir/deps" TypeScript compiler cache: "/deno-dir/gen"remoteから取得したmoduleとTypescriptコンパイル時のキャッシュ場所を教えてくれてます
formatter
$ docker run --rm -it --init -v $PWD:/app hayd/alpine-deno fmtテスト
こちらもビルドインでtest runnerが入っているらしいです。至れり尽くせり
https://deno.land/manual/testingsimple.test.tsimport { assertEquals } from "https://deno.land/std/testing/asserts.ts"; Deno.test("simple", () => { const x = 1 + 2; assertEquals(x, 3); });実行!
$ docker run --rm -it --init -v $PWD:/app hayd/alpine-deno test /app/simple.test.ts running 1 tests test simple ... ok (3ms) test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out (4ms)Debugger
https://deno.land/manual/tools/debugger
V8 Inspector Protocolをサポートしている先ほどのファイル読み込みのサンプルをDebugして見たいと思います。
--inspect-brk
を付けて最初の1行目でブレークする様にします。$ docker run --rm -it --init -p 9229:9229 -v $PWD:/app hayd/alpine-deno run --inspect-brk=0.0.0.0:9229 --allow-read /app/cat.ts /app/sample.txt Debugger listening on ws://127.0.0.1:9229/ws/xxxxxChromeで
chrome://inspect
にアクセスして
↑のinspectをクリックすると
デバッグできました
また、試せては無いですが、VSCode上でもデバッグ可能らしいです。
https://deno.land/manual/tools/debugger#vscodeVSCodeの拡張機能
Deno - Visual Studio Marketplace
バッドノウハウ
- Debugger使用時にchromeでinspectが表示されない
- docker使用する場合だと
--inspect-brk
だけではダメで--inspect-brk=0.0.0.0:9229
と指定しないといけない感想
- ビルドインで色々入っているので便利
- 外部パッケージをURLでインポートできるのはサクッと試せるので便利
- いきなりNodeのプロジェクトをDenoに置き換えるのは厳しそうだが、新規のプロジェクトや単独の小さなモジュールとかで使ってみたい
参考になったURL
- 投稿日:2020-05-16T11:56:35+09:00
javascriptのthisとbind、apply、callの使い方
参考
thisの違いはこちらが分かりやすい。
JavaScriptの「this」は「4種類」??bind()
Function.prototype.bind()自分なりのまとめ
javascriptのfunction内のthisが示すものはは関数の呼び出し方によって異なる。
関数呼び出し時にthisを明示するために用いるのが下記の関数
- call
- apply
- bind
call、apply
関数呼び出し時にthisを指定する。
callとapplyの違いは引数の取り扱いが違うくらい。bind
関数のthisに予め任意のものを指定した関数を返す。
bindを使って受け取った関数はthisが固定されているためcallとapplyで呼び出し時に上書きすることは不可能。確認
https://paiza.io/projects/ZJir-GpI4yZHafpMuZMmcQ?locale=en-us
process.stdin.resume(); process.stdin.setEncoding('utf8'); // Your code here! value = 'global'; var myObject = { value: 'myObject', show: function() { console.log(this.value); return this.value; } } myObject.show(); // myObject var show = myObject.show; show(); // global show.call(myObject); // myObject show.apply(myObject); // myObject show = myObject.show.bind(myObject); show(); // myObject show.call(global); // myObject bindしたため無効 show.apply(global); // myObject bindしたため無効 show = myObject.show.bind(global); show(); // global show.call(myObject); // global bindしたため無効 show.apply(myObject); // global bindしたため無効
- 投稿日:2020-05-16T11:22:27+09:00
【Rails】remote:true形式でAjax通信を行う(ブックマーク機能のajax化)
Ajaxとは
Ajaxとは、Webブラウザ上で
非同期通信
を行い、ページ全体の再読み込み無しにページを更新する方法のことです。同期通信について
同期通信
では、クライアントはwebページ全体の情報(HTMLとそれに紐づくcss,js,imageなどのアセット)をサーバーから受け取って、ページを一から作り直します。
例えばページの一部を変更するだけなのに、他の部分も組み立て直すってことはその分ページの表示に時間がかかっちゃいます。(サーバー側の処理を待つことになる)しかも、このリクエスト〜レスポンスの処理を行っている間は、他の処理を行わずにサーバーからレスポンスが返ってくるのを待ち続ける必要があります(よくあるのが画面が真っ白になって何もできない状態)。
そこでAjaxのような非同期通信を使用すれば、ページ遷移無しに、高速で更新処理を行い、尚且つ、リクエスト〜レスポンスの処理を行っている間も他の処理が行えます。
非同期通信の方法は2種類
この便利なAjaxによる非同期通信を行う方法としては、
①remote:true形式
②ajax関数を使った形式
の2パターンが存在しますが、今回はremote:true形式について以下に記していきます。仕組みだけ知りたいよって方は、コードの説明は読み飛ばしちゃっても大丈夫です。
コードの説明
今回作るもの
掲示板のブックマーク(いいね)ボタンを押した時に、ブックマークの登録、解除を行うという仕組みをajax化させていきます。
ルーティングの設定
ブックマークの登録、削除を行うために必要なルーティングの設定を行う。
config/routes.rbresources :boards do resources :bookmarks, only: %i[create destroy], shallow: true endモデルの設定
モデルでUsersテーブル、Boardsテーブル、Bookmarksテーブルの関連付を行う。
app/models/user.rbclass User < ApplicationRecord has_many :boards, dependent: :destroy has_many :bookmarks, dependent: :destroy has_many :bookmark_boards, through: :bookmarks, source: :board # ブックマーク関連のインスタンスメソッド # ブックマークをする def bookmark(board) bookmark_boards << board end # ブックマークを解除する def unbookmark(board) bookmark_boards.destroy(board) end # ブックマークをしているかどうかを判定する def bookmark?(board) bookmarks.where(board_id: board.id).exists? end endapp/models/board.rbclass Board < ApplicationRecord belongs_to :user has_many :bookmarks, dependent: :destroy endapp/models/bookmark.rbclass Bookmark < ApplicationRecord belongs_to :user belongs_to :board validates :user_id, uniqueness: { scope: :board_id } endコントローラの設定
AjaxによるHTTP通信を行うには、formに
remote:true
オプションを設定する必要がある。
form_withメソッドでAjax通信を利用しない場合(
local: true
オプション)
bookmarksコントローラのcreateアクション実行の際に、bookmarks/create.html.erb
というファイルをレンダリングしようとするため、別のページへリダイレクトさせていた。Ajax通信を利用する場合(
remote: true
オプション)
remote: true
の記述によって、AjaxでHTTPリクエストを送信するように設定される。
更に、html.erb
ファイルではなくjs.erb
ファイルをレンダリングしてくれる。そして、このjs.erbファイルをjsのコードに変換した文字列が、レスポンスボディとしてブラウザに返される(詳細は後述)。app/controllers/bookmarks_controller.rbclass BookmarksController < ApplicationController # js.erbファイルで変数を使用するため、インスタンス変数を設定 def create @board = Board.find(params[:board_id]) current_user.bookmark(@board) end def destroy @board = current_user.bookmark_boards.find(params[:id]) current_user.unbookmark(@board) end endブックマークボタンを切り替えるためのビュー
bookmarks/_bookmark_area.html.erb
ファイルで、ログイン中のユーザーが掲示板をブックマークしているかどうかによって呼び出すテンプレートを分ける。
- ブックマークしていない場合は
_bookmark.html.erb
を呼び出す。
- ブックマークボタンは色無しの状態
- ブックマークする機能
- ブックマークしている場合は
_unbookmark.html.erb
を呼び出す。
- ブックマークボタンは色付きの状態
- ブックマークを削除する機能
app/views/bookmarks/_bookmark_area.html.erb<% if current_user.bookmark?(board) %> <%= render 'bookmarks/unbookmark', { board: board } %> <% else %> <%= render 'bookmarks/bookmark', { board: board } %> <% end %>ブックマークしていない場合のボタンを実装
_bookmark.html.erb
ファイルを作成
- ブックマークするので、HTTPメソッドはpost
。対応するコントローラがcreate.js.erb
を呼び出す。
- id属性を付与(どのボタンをクリックしたか判別するため、各レコードのidを使用し、一意性を保つ)
-remote: true
オプションを付与。app/views/bookmarks/_bookmark.html.erb<%= link_to board_bookmarks_path(board), id: "js-bookmark-button-for-board-#{board.id}", method: :post, remote: true do %> <%= icon 'far', 'star' %> <% end %>ブックマークしている場合のボタンを実装
_unbookmark.html.erb
ファイルを作成
- ブックマークを削除するので、HTTPメソッドはdelete
。destroy.js.erb
を呼び出す。
- id属性を付与。
-remote: true
オプションを付与。app/views/bookmarks/_unbookmark.html.erb<%= link_to bookmark_path(board), id: "js-bookmark-button-for-board-#{board.id}", method: :delete, remote: true do %> <%= icon 'fas', 'star' %> <% end %>js.erbファイルを作成
js.erbファイルは以下2つの記述が可能。
1. jsの処理
2. rubyの記述(erbファイルだから)以下のjs.erbファイルによって、画面上に表示するブックマークボタンをAjax通信で切り替えられるようにします。
【
create.js.erb
ファイルを作成】
create.js.erbでhtml()メソッドを用い、指定したセレクタのhtml部分(指定したid属性を持つ部分)を置き換える。_unbookmark.html.erb
に置き換えるよう記述。app/views/bookmarks/create.js.erb$("#btn-bookmark-<%= @board.id %>").html("<%= j(render('boards/unbookmark', board: @board)) %>");【
destroy.js.erb
ファイルを作成】
create.js.erbと逆の内容を記述する。app/views/bookmarks/destroy.js.erb$("#btn-bookmark-<%= @board.id %>").html("<%= j(render('boards/bookmark', board: @board)) %>");ここまでがコードの細かい話!!
ブックマークボタンを押した時のHTTPレスポンスについて
上記の実装によって、なぜブックマークボタンをAjax通信で切り替えられるのか、その仕組みについて以下で説明します。
先に結論を述べると、それはサーバーからレスポンスボディとしてJavaScriptのコードを返し、そのコードに対する処理をクライアント側が実行してくれているからです。HTTPレスポンスの中身とクライアントの処理は?
- ブックマークボタンを押した時のHTTPレスポンスの中身はどうなっているのか?
- それに対してクライアント(ブラウザ)側はどのような処理を行うのか?
の2点を押さえれば、ブックマークボタンをAjax通信で切り替えられる仕組みを理解できるはずです。ブックマークボタンを押した時のHTTPレスポンスの中身は?
erbファイルをJS形式のコード(この段階ではただの文字列!)に変換したものが、レスポンスボディとしてクライアントに返されます。
つまり、erbファイルをそのままクライアントに返すのではなく、サーバー側でjs.erbファイルのrubyの記述(
j render
とか@board
とか)を事前に実行し、HTMLのコードとして展開した結果を、クライアント側に返しているのです。
一言で表すなら、クライアント側が読める内容に変換してから返している、ということです。レスポンスに対するクライアント側の処理
これに対し、クライアントはサーバーから返ってきたレスポンスボディを見て、「これはjs形式のものだな」と判断し、そこでようやくレスポンスボディの文字列に対してJavaScriptを実行してくれる、といった感じです。
検証ツールのネットワークタブを確認
- HTTPレスポンスのContent-Type(どういうコンテンツの種類か)が text/javascriptになっている。
→ajax通信に設定しているから、RailsがJS形式でレスポンスを送ってくれている。
→クライアント側はContent-typeを見て「JSで処理するんだな」と判断している。$("#js-bookmark-button-for-board-152").replaceWith("<a id=\"js-bookmark-button-for-bo ard-152\" data-remote=\"true\" rel=\"nofollow\" data-method=\"delete\" href=\"/bookma rks/152\">\n <i class=\"fas fa-star\"><\/i>\n<\/a>");おわりに
以上でremote:true形式でAjax通信を行う方法の説明を追えます。
なにか説明部分について誤りがございましたら、ご指摘頂きたく思います。
- 投稿日:2020-05-16T10:30:12+09:00
高階関数とコールバック関数
この記事はコールバック関数の以下の内容について解説します。
- 高階関数とは
- コールバック関数とは
- コールバック関数の例
- forEachメソッドをコールバック関数で実装してみる
- まとめ
高階関数
コールバック関数に入る前に、高階関数について説明します。
高階関数とは、関数を引数にとる関数の事を指します。
以下が例です。samplefunction hello(){ console.log('hello'); } function hai(callback){ callback(); } hi(hello);hi関数はhello関数を引数としているので、高階関数となります。
上の例がどのような流れなのかについて説明します。
- hi(hello);により、hi関数が実行
- hi関数内のcallback();より、hello関数が実行
- hello関数内のconsole.log('hello');が実行
- hi関数に戻るが何も処理がないのでそのまま終了
次にコールバック関数について説明します。
コールバック関数
コールバック関数とは、高階関数に引数として渡された関数の事を指します。
上の例ですと、hello関数が該当します。コールバック関数の例
ここまでコールバック関数について、ざっくりとした説明をしました。
上記の内容だけでもコールバック関数が利用できるようにはなりますが、JavaScriptには関数宣言、関数式、アロー関数があるためコールバック関数も上記の書き方以外にも書き方はあります。
更にコールバック関数は引数をとることができます。関数なので当たり前なのですが、ここら辺で躓く人が多いと思います。
そこで、ここからは様々なコールバック関数の書き方を例としてあげ、コールバック関数への理解を深めていきたいと思います。例1 関数宣言以外でコールバック関数をコーディング
コールバック関数は関数なので、勿論関数式でもアロー関数でも定義することができます。
以下は、高階関数を関数式で、コールバック関数をアロー関数で定義した例です。
因みに関数宣言、関数式、アロー関数が分からないという方は、こちらへどうぞconst func1 = function(callback) { console.log('例1 一番最初に実行される'); callback(); console.log('callbackが呼ばれた後に実行される'); }; // func1に引数として呼ばれている側なので、callback1関数はコールバック関数 const callback1 = () => { console.log('コールバック1'); }; func1(callback1);func1(callback1)でcallback1関数を引数としたfunc1関数を実行しているので、func1関数は高階関数、callback1関数はコールバック関数となります。
この例を実行した結果は、実行結果1のようになります。
例2 引数をもつコールバック関数をコーディング
コールバック関数は関数なので、勿論引数をもつことができます。
以下は、引数をもつコールバック関数の例です。const msg = 'コールバック2'; const func2 = function (msg, callback) { console.log('例2 一番最初に実行される'); callback(msg); console.log('callbackが呼ばれた後に実行される'); }; const callback2 = function (message) { console.log(message); }; func2(msg, callback2);この例で行われている処理の流れを説明します。
文字列(msg)と関数式2つ(func2, callback2)を定義しています。この時点では定義しているだけですので、実行しても何も出力されません。
func2(msg,callback)にて、func2関数が呼び出されています。引数は、最初に定義した文字列(msg)とコールバック関数です。
func2関数が呼び出されたので、func2関数内の一番最初にコーディングされたconsole.log(例2 一番最初に実行される');が実行されます。
次にcallback(msg)が実行されます。callbackにはcallback2関数が代入されているので、ここでの処理はmsgを引数に持つcallback2関数が呼び出されている事になります。
4.でcallback2関数が呼び出されたので、callback2関数内の一番最初にコーディングされたconsole.log(message);を実行します。
callback2関数内には他に処理がないので、終了となります。
callback(msg);が終了したので、func2関数内の次の処理にうつります。console.log('callbackが呼ばれた後に実行される');が実行されます。
func2関数内には他に処理がないので、終了となります。
よって、実行結果は、以下の実行結果2となります。
実行結果2例3 高階関数を呼び出すときにコールバック関数をコーディング
コールバック関数には他にもコーディングの仕方はたくさんありますが、ここでは最後に理解しにくい例をあげます。
以下、その例です。const msg = 'コールバック3'; const func3 = function (msg, callback) { console.log('一番最初に実行される'); callback(msg); console.log('callbackが呼ばれた後に実行される'); }; func3(msg, (message) => { console.log(message); });func3関数は高階関数です。コールバック関数はどこへいったかといいますと、
(message) => {
console.log(message);
}
がコールバック関数となっています。
例2を省略してコーディングしたものですね。省略しただけで処理の流れは同じです。
そのため、実行結果は以下の実行結果3です。
実行結果3forEachメソッドをコールバック関数で実装
JavaScriptにはforEachメソッドがあります。
forEachメソッドは配列の中の要素を一つ一つ実行します。これと同じことをするものをコールバック関数で実装していきます。
forEachについて知らない、もっと詳しく知りたいという方は、こちらへどうぞ。const array = ['コールバック関数', 'で', 'forEachメソッドを', '作ってみる']; const forEach = function (array, callback) { for (let i = 0; i < array.length; i++) { callback(array[i], i); } }; forEach(array, function(item, index) { console.log(`コールバック関数の実行${index}回目`, item); });コード内容について説明します。
1. 最初にforEachメソッドで使うための配列を用意します。
2. 次に高階関数(forEach())をコーディングしています。高階関数ではforEachメソッドの配列の要素を一つ一つ実行する、というところをfor文を使うことで実装しています。for文で配列の各要素とインデックス番号を引数とした関数を呼び出しています。
3. コールバック関数(function(item, index))を実行し、処理が全て終了すると、for文に戻ります。
4. for文の中は他に何も処理がないので、for文の外へ出ます。for文後も何も処理がないので処理は終了となります。このコードの実行結果は以下の実行結果4になります。
実行結果4まとめ
今回はコールバック関数の以下の内容について説明しました。
- 高階関数とは、関数の引数をもつ関数のこと
- コールバック関数とは、高階関数に引数として渡される関数のこと
- コールバック関数の例を3つ紹介
- forEachメソッドをコールバック関数で実装
- 投稿日:2020-05-16T10:04:12+09:00
ニコ生チャット取得
コメントビューア用。
OAuthが要るようなAPIは使わず、未登録で取得できるコメントを取得。
あくまで執筆時点の情報です。仕様が変わる可能性もあるのであしからず。前提として、ニコ生チャット取得はWebSocketの接続を2本行う必要があることに留意。
コミュニティIDを元に、配信ページを取得
https://live2.nicovideo.jp/watch/{コミュニティID}
- 例:
https://live2.nicovideo.jp/watch/co4930985
ページに埋め込まれた放送情報を取得する
const embeddedData = JSON.parse(document.getElementById("embedded-data").getAttribute("data-props"));
- 以下の場合は配信が終了してる
- embeddedData.program.status === 'ENDED'
- embeddedData.program.endTime < 現在時刻
const broadcastId = embeddedData.program.broadcastId || embeddedData.program.reliveProgramId; const audienceToken = embeddedData.player.audienceToken; const frontendId = embeddedData.site.frontendId;スレッドIDを取得する
WebSocketで以下に接続
const url = `wss://a.live2.nicovideo.jp/unama/wsapi/v2/watch/${broadcastId}?audience_token=${audienceToken}&frontend_id=${frontendId}`メッセージのやり取りは以下の通り。簡単のためにJSONで表記しているが、実際は文字列化して送受信される。
send{ "type": "startWatching", "data": { "stream": { "quality": "high", "protocol": "hls", "latency": "low", "chasePlay": false }, "room": { "protocol": "webSocket", "commentable": true }, "reconnect": false }, }send{ "type": "getAkashic", "data": { "chasePlay": false } }receive{ "type":"room", "data":{ isFirst: boolean, messageServer: { type: string, /** コメントサーバのWebSocketURL */ uri: string, }, name: string, /** スレッドID */ threadId: string, waybackkey: string, } }
- ここで抜き出すべき要素は以下。
- data.messageServer.uri
- data.threadId
チャットサーバに接続してスレッドIDを送信
先程取得したコメントサーバのURLにWebSocket接続。
WebSocketのリクエストヘッダの指定が必要。const ws = new WebSocket(wsUrl, 'niconama', { headers: { 'Sec-WebSocket-Extensions': 'permessage-deflate; client_max_window_bits', 'Sec-WebSocket-Protocol': 'msg.nicovideo.jp#json', }, });以下WebSocketでの送受信。
send[ { "ping": { "content": "rs:0" } }, { "ping": { "content": "ps:0" } }, { "thread": { "thread": "★スレッドID★", "version": "20061206", "user_id": "guest", "res_from": -150, "with_global": 1, "scores": 1, "nicoru": 0 } }, { "ping": { "content": "pf:0" } }, { "ping": { "content": "rf:0" } } ]チャット情報の受信
コメントサーバのWebSocketにスレッドIDを送信したら、コメント情報が返ってくる。
receive開いた時点までのチャット情報がドカッと来る。 obj.ping.content: 'rf:0'のものを受信したらおしまい。以降は下記のchatオブジェクトを含んだメッセージを受信し続ける。
receive{ chat: { anonymity?: number; /** チャットコメント */ content: string; /** Dateを数値化したやつ */ date: number; date_usec: number; /** 匿名の時は184が入ってる */ mail?: string; /** コメント番号 */ no: number; /** * 謎のフラグ * - 1: プレミアム会員 * - 3: 配信者自身 */ premium?: number; score?: number; thread: string; user_id: string; vpos: number; }; }また、定期的にWebSocketに対してpingを打つ必要がある。特にメッセージを含める必要はない。
これをしないと途中でメッセージが来なくなる。
JavaScriptなら、WebSocketインスタンスが持っているpingをsetInterval実行するだけでOK。ws.ping();
- 投稿日:2020-05-16T09:42:44+09:00
JavaScript 学習記録 #1 Dateオブジェクト
わかりやすく日時を表示する方法コードの書き方
①プログラムを書く準備
date.js<section> <p>最終アクセス日時:<span id="time"></span></p> </section>最終アクセス日時:
②「年/月/日 時:分」のかたちで表示
そのためには「年、月、日、時、日、分」を個別に取得する必要がある。date.js<script> 'use strict'; const now = new Date(); const year = now.getFullYear(); const month = now.getMonth(); const date = now.getDate(); const hour = now.getHours(); const min = now.getMinutes(); const output = `${year}/${month + 1}/${date} ${hour}:${min}`; document.getElementById('time').textContent = output; </script>③Dateオブジェクト
日時を扱うためのオブジェクトであり、次のようなことができる
1.現在日時を取得する
2.過去や未来の日時を設定する
3.日時の計算をするDateオブジェクトは初期化(インスタンス化)する必要がある
new Date();
※いくつかのオブジェクトは、newを使って初期化してから使う
月を取得するgetMonthメソッドは「実際の月-1」の数字が取得されるため、+1を忘れずに
- 投稿日:2020-05-16T08:44:01+09:00
【Rails】テーブルを分けて複数の画像をアップロードする
複数枚の写真を一度に保存する機能の実装において、
【itemテーブル】と【item_imageテーブル】の二つに
分けて複数枚の写真を保存する機能を実装した際の手順をまとめたのでご紹介します。完成形
○ 商品写真は最大で10枚まで投稿可能。
○ 一つのファイルフィールドに一つの写真でアップロードしていく。
○ 5枚投稿時点で下段に切り替わる。
1. HTMLの用意
sample.haml~ 省略 ~ .image-container .image-container_box .form-title %span.box-form-explanaion 商品画像 %span.indispensable 必須 %p.image-container_box_message 最大10枚までアップロードできます .image-container_box_alart-10 ※ 1枚目は必須です .image-container_unit-man 【写真が順にプレビューされていく箱】 .item-image-container__unit.preview-0 【写真一枚が入る箱 ※投稿ごとに生成されていく】 = f.fields_for :item_images do |i| .image-container__unit--guide = i.file_field :image, class: 'img-man', id:"image-label-0",type: 'file' .have-image %i.fas.fa-camera ~ 省略 ~◉【写真一枚が入る箱】に2つクラスがあるのは、2枚目以降では毎回クラス名を
変えていくためです。
◉この仕様では、1つのinputに写真は1つなので、multiple属性は付けていません。2. CSSの用意
sample.scss.image-container { padding: 40px; border-bottom: solid 1px rgb(235, 235, 235); &_box { &_message { height: 19px; margin: 16px 0 5px; font-size: 14px; line-height: 1.4em; display: block; } &_alart-10 { margin-bottom: 4px; font-size: 14px; font-weight: bold; color:red; } } &_unit-man { min-height: 152px; display: flex; flex-direction: row; flex-wrap: wrap; .item-image-container__unit { align-content: center; align-items: center; background-color: rgb(245, 245, 245); display: flex; justify-content: flex-start; height: 150px; width: 118px; margin-left: 5px; margin-bottom: 2px; justify-content: center; position: relative; border-width: 1px; border-style: dashed; border-color: rgb(204, 204, 204); border-image: initial; .have-image { position: absolute; left: 32px; top: 40px; z-index: 0; cursor: pointer; font-size: 16px; .fas.fa-camera { margin-left: 16px; font-size: 1.2rem; } .fas.fa-camera:hover { cursor: pointer; transform: scale(1.3, 1.3); transition: all 0.1s ease 0s; } } .item-image-container__unit__image { z-index: 1; height: 145px; width: 100%; margin: 0 3px; background-color: #ffffff; position: relative; img { width: 100%; height: auto; } .image-option__list--tag { width: 100%; background-color: lightblue; text-align: center; position: absolute; bottom: 0; left: 0; } .image-option__list--tag:hover { cursor: pointer; transform: scale(1.0, 1.0); transition: all 0.1s ease 0s; background-color: #ea352d; color:#ffffff; } } } .item-image-container__unit { input{ display: none; } } } }[flex-direction: row;]と[flex-wrap: wrap;]により、
写真が既定の幅まで投稿されたら下段に折り返してくれます。3. JSでプレビューさせる
sample.js$(function(){ ~ 省略 ~ var dataBox = new DataTransfer(); //データ用の箱を作る $(document).on('change', '.img-man', function(){ //inputの中身が変化したら発火する $.each(this.files, function(i, file){ var fileReader = new FileReader(); dataBox.items.add(file) fileReader.readAsDataURL(file); //ファイルの読み込み fileReader.onloadend = function() { //読み込み完了すると発火 var num = $('.item-image-container__unit').length //写真の枚数をnumに代入 var src = fileReader.result //写真のデータをsrcに代入 var html = `<div class="item-image-container__unit__image"> <img src="${src}"> <div class="image-option__list--tag btn-${num}">削除</div> </div>` var field = `<div class="item-image-container__unit preview-${num}"> <div class="image-container__unit--guide"> <label for="image-label-${num}"> <input class="img-man" id="image-label-${num}" type="file" name="item[item_images_attributes][${num}][image]"> <div class="have-image"> <i class="fas fa-camera"></i> </div> </label> </div> </div>` $(html).appendTo(`.preview-${num - 1}`) //枚数で該当するクラスに写真を追加する if (num < 10 ) { //10枚分まで新しいinputの生成を行う $(field).appendTo('.image-container_unit-man') } }; }); }); //削除機能 $(document).on("click", ".image-option__list--tag", function(){ //削除ボタンクリックで発火 var num = $('.item-image-container__unit').length var field_2 = `<div class="item-image-container__unit preview-0"> <div class="image-container__unit--guide"> <label for="image-label"> <input class="img-man" id="image-label-0" type="file"> <div class="have-image"> <i class="fas fa-camera"></i> </div> </label> </div> </div>` $(this).parent().parent().remove(); //写真が入っている箱ごと削除 $(".item-image-container__unit").removeClass(`preview-${num - 1}`) $(".item-image-container__unit").addClass(`preview-${num - 2}`) if(num == 1) { //全部削除した時に新たに1つフィールドを生成する $(field_2).appendTo('.image-container_unit-man') } }); ~ 省略 ~◉変数【num】は、写真の読み込みごとに代入され、その度に横に生成される
【preview-${num}】クラスが書き換わって横に生成されるようになっています。◉ 下部の10枚制限の記述によって、10枚までアップされるとinputの生成が止まります。
また、multiple属性が付いていないので写真の一括選択はできなくなります。◉削除機能に関しても、クラス名の書き換えを行う必要があります。
登録枚数に関しては、モデルにも別でバリーデーションは書いてあります。
sample.rbdef item_images_number errors.add(:item_images, "は10枚までです") if item_images.size > 10 end
以上で終了です。
ご覧いただきありがとうございました。
- 投稿日:2020-05-16T01:57:13+09:00
toio を Web Bluetooth API で制御(「通知・読み出し・書き込み」を行う)
はじめに
この記事は、Web Bluetooth API を使ってロボットトイ「toio」を制御した際の過程・プログラムをメモしたものです。
具体的には以下の 4種類の処理・制御を試しました。
- 通知の ON/OFF(モーションセンサーの値を受け取る)
- デバイスの切断・再接続
- 値の書き込み(ランプの点灯の制御)
- 値の読み出し(読み取りセンサーの情報を受け取る)
制御に利用する情報は、以下の公開情報から得ています。
●通信概要 · toio™コア キューブ 技術仕様
https://toio.github.io/toio-spec/docs/ble_communication_overview.html以前試した内容
Web Bluetooth API による toio の制御は、以前も利用したことがありました。
その時に行った内容は、2台の toio に同時に接続してモーターの制御を行ったりするもの等で、試した内容は記事に書きました。
- toio を音で制御してみた(Audio用の Teachable Machine でベルやタンバリンの音を機械学習) - Qiita
- #toio の目標指定付きモーター制御の仕様を確認して使ってみる( #GWアドベントカレンダー 5/3 ) #おうちでロボット開発 - Qiita
- toio を Web Bluetooth API で制御した際の応答を得る(目標指定付きモーター制御) - Qiita
このとき、値の読み出しや通知の停止・再開といった内容を試してなかったため、今回はそれらを含めて通信周りを一通り試してみます。
参考にしたサイト
今回、下記にたくさんあるサンプルの中の 4つを参照して、参照したソースコードの中の不要な部分を削ったり、toio用の処理に合わない部分を書きかえたり、ということをやりました。
●samples/web-bluetooth at gh-pages · GoogleChrome/samples
https://github.com/GoogleChrome/samples/tree/gh-pages/web-bluetooth参照したソースは以下の4つです。
- samples/notifications-async-await.js
- samples/device-disconnect-async-await.js
- samples/write-descriptor-async-await.js
- samples/read-characteristic-value-changed-async-await.js
作成した 4つのソースコード
以下で、toio を制御するためのソースコードを掲載します。
この記事では、ソースコードに関する補足は割愛しています。また、全てのソースコードでCSSフレームワークの「Bulma」を読み込んでいます。通知の ON/OFF
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Web Bluetooth API で通知</title> <link rel="stylesheet" href="./bulma.min.css"> </head> <body> <section class="section"> <div class="container"> <h1 class="title"> 操作用ボタン1 </h1> <div class="buttons"> <button class="button is-success is-light" type="button" onclick="onStartButtonClick()">接続+通知ON</button> <button class="button is-danger is-light" type="button" onclick="onStopButtonClick()">通知OFF</button> <button class="button is-info is-light" type="button" onclick="onStartNotificationsButtonClick()">通知ON</button> </div> </div> </section> <script> const TOIO_SERVICE_UUID = '10b20100-5b3b-4571-9508-cf3efcd7bbae'; const MOTION_CHARACTERISTIC_UUID = '10b20106-5b3b-4571-9508-cf3efcd7bbae'; let myCharacteristic; async function onStartButtonClick() { let serviceUuid = TOIO_SERVICE_UUID; let characteristicUuid = MOTION_CHARACTERISTIC_UUID; try { console.log('Requesting Bluetooth Device...'); const device = await navigator.bluetooth.requestDevice({ filters: [{services: [serviceUuid]}]}); console.log('Connecting to GATT Server...'); const server = await device.gatt.connect(); console.log('Getting Service...'); const service = await server.getPrimaryService(serviceUuid); console.log('Getting Characteristic...'); myCharacteristic = await service.getCharacteristic(characteristicUuid); await myCharacteristic.startNotifications(); console.log('> Notifications started'); myCharacteristic.addEventListener('characteristicvaluechanged', handleNotifications); } catch(error) { console.log('Argh! ' + error); } } async function onStopButtonClick() { if (myCharacteristic) { try { await myCharacteristic.stopNotifications(); console.log('> Notifications stopped'); myCharacteristic.removeEventListener('characteristicvaluechanged', handleNotifications); } catch(error) { console.log('Argh! ' + error); } } } async function onStartNotificationsButtonClick() { try { console.log('Starting Notifications...'); await myCharacteristic.startNotifications(); myCharacteristic.addEventListener('characteristicvaluechanged', handleNotifications); console.log('> Notifications started'); } catch(error) { log('Argh! ' + error); } } function handleNotifications(event) { let value = event.target.value; let a = []; for (let i = 0; i < value.byteLength; i++) { a.push('0x' + ('00' + value.getUint8(i).toString(16)).slice(-2)); } console.log('> ' + a.join(' ')); } </script> </body> </html>デバイスの切断・再接続
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Web Bluetooth API で接続・切断</title> <link rel="stylesheet" href="./bulma.min.css"> </head> <body> <section class="section"> <div class="container"> <h1 class="title"> 操作用ボタン1 </h1> <div class="buttons"> <button class="button is-success is-light" type="button" onclick="onStarButtonClick()">接続</button> <button class="button is-light" type="button" onclick="onReadButtonClick()">読み込み</button> </div> </div> </section> <script> const TOIO_SERVICE_UUID = '10b20100-5b3b-4571-9508-cf3efcd7bbae'; const ID_SENSOR_CHARACTERISTICS_UUID = '10b20101-5b3b-4571-9508-cf3efcd7bbae'; let bluetoothDevice; let idSensorCharacteristic; async function onStarButtonClick() { try { if (!bluetoothDevice) { await requestDevice(); } } catch(error) { console.log('Argh! ' + error); } } async function requestDevice() { let serviceUuid = TOIO_SERVICE_UUID; let characteristicUuid = ID_SENSOR_CHARACTERISTICS_UUID; try { console.log('Requesting Bluetooth Device...'); bluetoothDevice = await navigator.bluetooth.requestDevice({ filters: [{services: [serviceUuid]}]}); console.log('Connecting to GATT Server...'); const server = await bluetoothDevice.gatt.connect(); console.log('Getting Service...'); const service = await server.getPrimaryService(serviceUuid); console.log('Getting Characteristic...'); idSensorCharacteristic = await service.getCharacteristic(characteristicUuid); } catch(error) { console.log('Argh! ' + error); } } async function onReadButtonClick() { if (idSensorCharacteristic) { try { console.log('Reading ...'); await idSensorCharacteristic.readValue().then(value => { let a = []; for (let i = 0; i < value.byteLength; i++) { a.push('0x' + ('00' + value.getUint8(i).toString(16)).slice(-2)); } console.log('> ' + a.join(' ')); }); } catch(error) { console.log('Argh! ' + error); } } } </script> </body> </html>値の書き込み
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Web Bluetooth API で書き込み</title> <link rel="stylesheet" href="./bulma.min.css"> </head> <body> <section class="section"> <div class="container"> <h1 class="title"> 操作用ボタン1 </h1> <div class="buttons"> <button class="button is-success is-light" type="button" onclick="onReadButtonClick()">接続</button> <button class="button is-info is-light" type="button" onclick="onWriteButtonClick()">書き込み</button> </div> </div> </section> <script> const TOIO_SERVICE_UUID = '10b20100-5b3b-4571-9508-cf3efcd7bbae'; const LIGHT_CHARACTERISTICS_UUID = '10b20103-5b3b-4571-9508-cf3efcd7bbae'; const light_buf = new Uint8Array([ 0x03, 0x00, 0x01, 0x01, 0x00, 0xFF, 0x00 ]); let myDescriptor; let characteristic; async function onReadButtonClick() { let serviceUuid = TOIO_SERVICE_UUID; let characteristicUuid = LIGHT_CHARACTERISTICS_UUID; try { console.log('Requesting Bluetooth Device...'); const device = await navigator.bluetooth.requestDevice({ filters: [{services: [serviceUuid]}]}); console.log('Connecting to GATT Server...'); const server = await device.gatt.connect(); console.log('Getting Service...'); const service = await server.getPrimaryService(serviceUuid); console.log('Getting Characteristic...'); characteristic = await service.getCharacteristic(characteristicUuid); console.log('Getting Descriptor...'); myDescriptor = await characteristic.getDescriptor('gatt.characteristic_user_description'); const value = await myDescriptor.readValue(); let decoder = new TextDecoder('utf-8'); console.log('> Characteristic User Description: ' + decoder.decode(value)); } catch(error) { console.log('Argh! ' + error); } } async function onWriteButtonClick() { if (!characteristic) { return; } let value = light_buf; try { await characteristic.writeValue(value); } catch(error) { console.log('Argh! ' + error); } } </script> </body> </html>値の読み出し
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Web Bluetooth API で読み込み</title> <link rel="stylesheet" href="./bulma.min.css"> </head> <body> <section class="section"> <div class="container"> <h1 class="title"> 操作用ボタン1 </h1> <div class="buttons"> <button class="button is-success is-light" type="button" onclick="onStarButtonClick()">接続</button> <button class="button is-light" type="button" onclick="onReadButtonClick()">読み込み</button> </div> </div> </section> <script> const TOIO_SERVICE_UUID = '10b20100-5b3b-4571-9508-cf3efcd7bbae'; const ID_SENSOR_CHARACTERISTICS_UUID = '10b20101-5b3b-4571-9508-cf3efcd7bbae'; let bluetoothDevice; let idSensorCharacteristic; async function onStarButtonClick() { try { if (!bluetoothDevice) { await requestDevice(); } } catch(error) { console.log('Argh! ' + error); } } async function requestDevice() { let serviceUuid = TOIO_SERVICE_UUID; let characteristicUuid = ID_SENSOR_CHARACTERISTICS_UUID; try { console.log('Requesting Bluetooth Device...'); bluetoothDevice = await navigator.bluetooth.requestDevice({ filters: [{services: [serviceUuid]}]}); console.log('Connecting to GATT Server...'); const server = await bluetoothDevice.gatt.connect(); console.log('Getting Service...'); const service = await server.getPrimaryService(serviceUuid); console.log('Getting Characteristic...'); idSensorCharacteristic = await service.getCharacteristic(characteristicUuid); } catch(error) { console.log('Argh! ' + error); } } async function onReadButtonClick() { if (idSensorCharacteristic) { try { console.log('Reading ...'); await idSensorCharacteristic.readValue().then(value => { let a = []; for (let i = 0; i < value.byteLength; i++) { a.push('0x' + ('00' + value.getUint8(i).toString(16)).slice(-2)); } console.log('> ' + a.join(' ')); }); } catch(error) { console.log('Argh! ' + error); } } } </script> </body> </html>まとめ
無事に、Web Bluetooth API を使っての値のやりとり(通知、書き込み、読み出し)を行うことができました。
この後は、センサー等の値の読み出しで取得した結果や、通知されたセンサー等の値の変化によって、toio の挙動を変えたりするようなプログラムを作ってみようと思います。
- 投稿日:2020-05-16T00:55:22+09:00
Firebase Local Emulator SuiteのFirestoreに外部のスクリプトからアクセスしてモックデータを突っ込む
※こちらに非常に良い記事があります。まだ読んでおられない方は是非!
環境変数の
FIRESTORE_EMULATOR_HOST
に適切な値を設定することで外部のスクリプトからエミュレーターにアクセスできます。(他言語のSDKでも同様だそうです)import { Firestore } from '@google-cloud/firestore'; Object.assign(process.env, { GCLOUD_PROJECT: '[プロジェクトID]', FIRESTORE_EMULATOR_HOST: '0.0.0.0:[Firestoreのポート番号]' }); const firestore = new Firestore(); (async () => { // admin権限が必要な操作も可能 console.log(await firestore.listCollections()); })();なお、
GCLOUD_PROJECT
に関しては環境変数を使わなくても適当なメソッドで代替できます。実際に存在するプロジェクトIDである必要はないことに注意です!Firestoreのエミュレータは並列実行しても1インスタンスなので、プロジェクトIDが被ると共通の領域が使用されるようになります。これに気づくまでに数時間を要しましたが、冒頭にも書いた通り、既に偉大な先人の方が記事を書かれていたようです。こちらの記事は十分に読み込んだつもりになっていましたがまだまだ甘かった・・・。
- 投稿日:2020-05-16T00:06:28+09:00
EJSで「moment」というライブラリーを使用する方法
ウェブアプリを開発する際にEJSページで時間のフォーマットを指定したい場合はどうすればいいでしょうか?
ネット上で検索してみたが、いろいろな方法が出た。
でも、やってみた後で「moment」ライブラリーの使い方がいいと思う
じゃ、説明いたします分かりやすいためにSails.jsフレームワークを使う
最初、「moment」をプロジェックトにインストールする
npm install momentSails.jsのアクションファイルでmomentを呼び出します
fn: async function () { var postId = this.req.param('id'); var postInfo = await Post.findOne({ id: postId }); return { post: postInfo, moment: momentInfo }; }ESJページにmoment変数を渡すために
return { moment: momentInfo };
を使う
そうした後でESJページでmoment変数がある最後、moment変数をESJページで呼び出します
作成日 <%= moment(post.createdAt).format('YYYY/MM/DD') %>結果は「
作成日 2020/05/15
」をブラウザで表示される
※「post.createdAt」はデータベースに「1589531502042」を格納する。以上です