20211016のJavaScriptに関する記事は29件です。

C#とNode.jsを連携する

C#とNode.jsを連携する EdgeJsを利用する EdgeJsを利用しNode.jsからC#通信ができます。その逆もできます。.net4.6のみ。まだ.netCoreには未対応のようです。 Node.js側 const edge = require('edge-js'); const helloWorld = edge.func('cs', function () {/* async (input) => { return ".NET Welcomes " + input.ToString(); } */}); helloWorld('Node.js', function (error, result) { if (error) throw error; console.log(result); }); C#側 NodeJsからC#のメソッドを実行しデーターを渡します edgeJsライブラリ 現在は更新されていません。メンテナンスを引き継いだプロジェクトが別にあります。 npm install edge edgeで読み込む const edge = require('edge'); フォークされたもの 現在も保守されている npm install edge-js edgeの後ろに-jsを付ける const edge = require('edge-js'); 説明 Node.jsにインストールされたモジュールを表示する npm list npm -v 環境構築 DockerFileの内容をみて必要なライブラリをインストールします。 Monoは必要ないです。 RiderのエディタからedgeJSモージュールをインストールする npm install後にエディタ上で 波線をクリックするとnode_moduleフォルダが生成され、edgeモジュールがインストールされる 動作に必要な設定 コマンド 有無 値 備考 EDGE_USE_CORECLR 必須 1 .NetCore用のedgeモジュールを利用する EDGE_NATIVE 必須 edge-js/build/Release/edge_coreclr.node 相対パスで設定する 絶対パスだとエラーが出る EDGE_DEBUG オプション 1 デバッグ表示 EDGE_APP_ROOT オプション Publishフォルダへのパス 外部Dll Nugetライブラリを利用する時に使う 相対パスで設定する 環境変数の設定の仕方 ターミナルから設定し実行する EDGE_NATIVE=edge-js/build/Release/edge_coreclr.node EDGE_USE_CORECLR=1 EDGE_DEBUG=1 node test.js RiderのNode.js設定画面から設定する EnviromentValueで設定します。 JSプログラムの中に書く var path = require('path'); const baseNetAppPath = path.join(__dirname, '/bin/Release/netstandard2.0/publish/'); process.env.EDGE_USE_CORECLR = 1; process.env.EDGE_DEBUG=1; process.env.EDGE_NATIVE='edge-js/build/Release/edge_coreclr.node'; //process.env.EDGE_APP_ROOT = baseNetAppPath; var edge = require('edge-js'); サンプルを実行 node_modules/edge-jsフォルダの中にSampleファイルがあり、いろいろ試せます。 デバッグ 3種類 C#側のデバッグ 実行ボタンでデバッグ エラーが緑文字で出る。C#側のCSファイルビルド状態、Dllファイルが読み込まれたかどうか Dllファイルがあるかアセンブリが読み込まれたかどうかなどいろいろ確認できます。 Node.js側のデバッグ 虫のアイコンでNode.js側がデバッグできます。Js側でブレイクポイントなどをしてデバッグします。 edge.jsを追加するとブレイクポイントでデバッグできるようになる ブレイクポイント追加する Debugger ConsoleとProcess Consoleでエラーログを見ることができます。 よく出るエラー Uncaught TypeError: edge.initializeClrFunc is not a function 一見JavaScript側のエラーのように見えるがC#側のエラーであることが多いです。C#側を確認するとよいです。 Error occurred during CoreCLR initialization node:internal/modules/cjs/loader:1168 return process.dlopen(module, path.toNamespacedPath(filename)); パスのどれかに絶対パスが含まれていると出るエラーになります。相対パスに直すと通ります。 環境変数の設定などで起きる ダイナミックライブラリの中に外部DllやNugetライブラリを利用する設定 プロジェクトフォルダに以下の値を追加する。 <Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>netcoreapp2.0</TargetFramework> <PreserveCompilationContext>true</PreserveCompilationContext> <CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies> </PropertyGroup> <ItemGroup> <Reference Include="EdgeJs"> <HintPath>..\..\node_modules\edge-js\lib\bootstrap\bin\Release\netcoreapp1.1\EdgeJs.dll</HintPath> <Private>true</Private> </Reference> </ItemGroup> </Project> .NET5のところをクリックしAdd FromからもDllを追加できます Publishを設定する Publishを実行すると依存ファイル(dllなど)、Nugetから入れたDLLをすべて同じフォルダに書き出せます。 Publishフォルダへパスを通す。相対がよいです。 process.env.EDGE_APP_ROOT = ''; Nugetから必要ライブラリをインストール Microsoft.NETCore.DotNetHost Microsoft.NETCore.DotNetHostPolicy 現在の状態 Dllファイルへのアクセス サンプルファイルのDllへはアクセスができましたが、DLLプログラムのの中にNugetのライブラリを入れると読み込まれない状態です。 DapperやMicoroSoftのSqliteライブラリなどが読み込まれない。NewtonSoftのJesonのライブラリは読み込め使えました。 EDGE_APP_ROOTにPublishまでの相対パスを通すとエラーが起きます。 読み込み方法がわかる方ご教授ください。 参考 続く
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript活用】リベンジしてみた!!→QiitaAPIでバイクが好きがいるか調べてみた

バイクが好きだ!と公言し、Qiita初投稿テーマとして「【JavaScript活用】QiitaAPIを活用してバイク好きがいるか調べてみた」を投稿しましたが、「さいごに」に記載したとおり、英語圏ではオートバイを指す言葉は[bike]ではなく [Motorcycle] なのだということがわかりました。 それならやることはひとつよね~。ということで [Motorcycle] でリベンジ しますッ リベンジ内容 せっかくのリベンジなので、少しチャレンジングな内容もやってやろうじゃないか! ということで、前回からの処理条件の追加・変更箇所を赤字として記載したとおりに実行します。 なお、処理の順番も変更していますがわかりにくくなるので赤字にしていません。 (条件変えただけでCsv書き出しを追加したくらいしか変わっていないのはご容赦ください) 【検索するタグおよびキーワード】  ・ [Motorcycle] 【処理】  1.タグのフォロワー数をそれぞれ取得  2.タグが設定された記事数をそれぞれ取得  3.キーワードが含まれた記事のタイトル、URL、 LGTM(=いいね!) を10件分を一覧化  4. Csv書き出し (キーワードが含まれた記事のタイトル、URL、LGTM(=いいね!)を100件数分) 環境、言語等 ・Visual Studio Code ・Qiita API ・JavaScript ・Node.js 処理コードおよび処理結果 「タグをフォローしている人の数」「タグを含む記事の数」の処理がエラーが出て、どうにもこうにも動かない。単語を変えてやると問題なく動くのに、[Motorcycle]でやるとエラーが出ます。 もしかして?タグの存在がそもそも存在しないとエラーになるのかな?と思い、エラー回避「try~catch文」を実装することにしました。 Csvへ書き出す処理もチャレンジします! ★参考:【JavaScript】例外処理try~catch文の使い方 ★参考:JSONの基本 データ形式 タグのフォロー数、タグを含む記事数、キーワード記事10件分 処理コード //定数を各種定義し代入 const axios = require('axios'); const accessT = '自分のアクセストークンを指定'; const NameEnglish = 'Motorcycle'; //関数mainを定義 async function main() { //★[Motorcycle]タグをフォローしている人の数 try{ let response1 = await axios.get('https://qiita.com/api/v2/tags/Motorcycle', { headers: { 'Authorization': `Bearer ` + accessT } }); console.log('1[' + NameEnglish + ']タグのフォロワー数:'+ response1.data.followers_count); } catch(e) { } //★[Motorcycle]タグを含む記事の数 try{ let response2 = await axios.get('https://qiita.com/api/v2/tags/Motorcycle', { headers: { 'Authorization': `Bearer ` + accessT } }); console.log('2[' + NameEnglish + ']タグを含む記事数:'+ response2.data.items_count); } catch(e) { } //★[Motorcycle]をキーワードとして含む記事の一覧20件分(Title,URL,LGTM数) let response3 = await axios.get('https://qiita.com/api/v2/items?per_page=10&page=1&query='+ NameEnglish, { headers: { 'Authorization': `Bearer ` + accessT } }); console.log('| Title | URL | LGTM数 |'); console.log('|:-|:-|:-:|'); for (let i = 0; i < response3.data.length; i++) { //ログを表示させるのみ console.log('|'+ response3.data[i].title +'|'+ response3.data[i].url +'|'+ response3.data[i].likes_count +'|'); } } main(); 処理結果 [Motorcycle]タグは存在していないようですね・・・ エラー回避が作用処理はうまく作動しましたのでよかったです! そのあとの[Motorcycle]キーワードの一覧もコンソールログ表示もうまく動きました。 [Motorcycle]タグをフォローしている人の数 1[Motorcycle]タグのフォロワー数:0 [Motorcycle]タグを含む記事の数 2[Motorcycle]タグを含む記事数:0 [Motorcycle]をキーワードとして含む記事の一覧10件分(Title、URL、LGTM数) 10件分 Title URL LGTM数 【JavaScript活用】QiitaAPIを活用してバイク好きがいるか調べてみた https://qiita.com/yui-kouy/items/ddcf699b743adedb32db 3 【物体検出】YOLOv5 で動画のモザイク(ぼかし)処理を自動化してみる【動画編集】 https://qiita.com/hkwsdgea_ttt2/items/ba2e5dddc11d84da0428 1 MLP-MixerのすごさをPython Pytorch実装しながら体感してみる。 https://qiita.com/keiji_dl/items/9ce952c8bb121eda94c8 3 GCP Video Intelligence: ローカルにある動画ファイルの読込みは、input_uriではなくinput_content引数を使う https://qiita.com/electronics_diy721/items/bc7992345a07a154c346 0 予約サイトの設計 https://qiita.com/touringstay/items/83d8975278e37336b016 0 【YOLOv5 ②】Google ColabでYOLOv5とtorch hubを使って物体検出の検出座標を出力する https://qiita.com/hkwsdgea_ttt2/items/879c91a4bb5f1a0bb513 4 Salesforce で Custom Tab を作成するときに指定できる motif タグの選択肢全て https://qiita.com/itouchitachi/items/7fc0df4ba40a77f52fb5 0 ディープラーニングの活用:コンピュータビジョン (Semantic Segmentation / Object Detection) https://qiita.com/taichinakabeppu/items/f6838b954d3a8464333f 2 【MMDetection】 データセットのカスタマイズ編 https://qiita.com/bellpond/items/84c718608adeeca2a2f0 0 OKI AIエッジコンピューター「AE2100」で動く物体検出プログラムを作ろう (1) ―基礎編― https://qiita.com/TWAT/items/0cbf6286e921f7c5664e 4 Csv書き出し(キーワードが含まれた記事のタイトル、URL、LGTM(=いいね!)を100件数分) 一から書いてみようと思いましたが、やっぱり難しく以下記事を参考にさせていただきました。 前回できなかったCsv書き出しが無事に動いたので、いったんリベンジ完了です ★参考:JavaScriptとQiitaAPIで取得した人気タグ情報をplotlyで散布図表示してみる 処理コード //package require const axios = require("axios"); const fs = require("fs"); const csvStr = require("csv-stringify/lib/sync"); const csvParse = require('csv-parse/lib/sync'); const NameEnglish = 'Motorcycle'; const accessT = '自分のアクセストークンを指定'; //QiitaAPIでデータ取得・csvに出力 async function getArticle(query) { //csvに変換する用list let outcsv = []; //csvのヘッダー設定 let columns = ['Title','URL','LGTM']; outcsv.push(columns); let response //リクエストが失敗した時の処理 response = await axios.get('https://qiita.com/api/v2/items?per_page=100&page=1&query=' + NameEnglish, { headers: { Authorization: `Bearer ` + accessT, } } ).catch(err => { return err.response }); if (response.status != 200) { console.log("APIエラーとは・・・?") } //Csv書き出し準備 for (let i = 0; i < response.data.length; i++) { //listに格納 let record = []; record.push(response.data[i].title,response.data[i].url,response.data[i].likes_count); outcsv.push(record); } //Csvとして出力 fs.writeFileSync('./list_'+ NameEnglish +'.csv', csvStr(outcsv)); } //情報の取得 var query = "node.js"; getArticle(query); 処理結果 Visual Studio Codeで中を開くと、Csv形式になっていましたわーい!(画像上は途中省略しています。) 100件上限にしましたが67件しかないことが判明しました Explorer上Csvファイルが保存されていることも確認できました。 Csvファイルを開くと文字化けしておりました。リベンジ完了と書きましたが、文字化け回避リベンジが必要ですねうおぉぉぉ!!! 考察 10件分のリスト化されたURL先の文書を見ての考察といたします。 ・AIのディープラーニングの返り値がバイク=[Motorcycle]となっているので[Motorcycle]がヒットしているだけの記事が多数存在。 ・Motorcycleクラスという謎の単語が出てきた。 ・[Motorcycle]=バイクが好きだということで投稿している文書は見つからなかった。 ・日本語の記事として[バイク]というキーワードを用いることが多いと推測。 ・バイクが好きな人はキーワードとしては[バイク]を使っていると推測。 さいごに 日本語としては[Motorcycle]という言葉を文章内に使うことが少ないことがわかりました。 バイクが好きな人はキーワードとしては[バイク]を使っているのかな、と思いました。 今回は単純にCsv書き出しをリベンジ しましたが、文字化け回避はリベンジしたい・・・ 色々チャレンジしながらステップアップしたいです! ついでに折り畳みもリベンジできました 記事の書き方で参考にしたURL 記事名(サイト表題) URL Qiitaの投稿で装飾したいときによく使うHTMLタグ https://qiita.com/7note/items/9c0763d5491e43edb552 絵文字チートシート https://www.webfx.com/tools/emoji-cheat-sheet/ WEB色見本 原色大辞典 - HTMLカラーコード https://www.colordic.org/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

javascript演習 1日目/30日

覚えたことたち window.addEventListener('keydown',function(e){ キーを押した時の動作 }) htmlのaudioタグについて const audio = document.querySelector(`audio[data-key="${e.keyCode}"]`); audio.currentTime = 0; audio.play(); トランジションが終わったらクラスを外したりする keys.forEach(key => key.addEventListener('transitionend',function)) e.keyCodeとかも知らなかった window.addEventListener('keydown',function(e){ キーを押した時の動作 })
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JSのコンテキストとスコープ

JSの実行環境ってどうなっているの? JSは主にブラウザで動作しています。 そのブラウザの中にJavaScript Engineが入っており、その中で動作するようになっています。 JavaScript Engine JSエンジンにはいくつか種類がありますが今シェアが多いのはV8というエンジンみたいです。 そのJSエンジンの中には何があるのか? ECMAScriptとWebAPIsが含まれています。 ・ECMAScript  JSの言語仕様 ・WebAPIs  DOMAPIやFetchAPIなどのJSの便利な機能が提供されている このWebAPIsを通してブラウザを操作している 実行前にJSが用意するもの ・コード  あなたが書いたもの ・グローバルオブジェクト(Windowオブジェクト)  この中にWebAPIが含まれる ・this  オブジェクトへの参照を保持しているもの(コンテキストによって変化する) JSの実行コンテキスト コードを実行する際の文脈や状況のこと 要するにブラウザで実行する際のコード、グローバルオブジェクト(Windowオブジェクト)、thisを全部まとめたもの, だそうです。 ・グローバルコンテキスト(一番外側のコード) 実行中のコンテキスト内の変数と関数 + グローバルオブジェクト + this ・関数コンテキスト(関数内のコード) 実行中のコンテキスト内の変数と関数 + arguments + super + this + 外部変数(関数の外側の変数) ・モジュールコンテキスト 実行中のコンテキスト内の変数と関数 + グローバルオブジェクト  thisが使えないとこに注意 コールスタック 処理がたどってきたコンテキストの積み重ねのこと 処理の進行する際にコンテキストが生成されるようになります。 function a(){ console.log('a'); b(); } function b(){ console.log('b'); c(); } function c(){ console.log('c'); } a(); このような場合、a()⇨function a()⇨function b()⇨function c()の順番で実行されます。 そのため、こんなイメージです。 処理は最後から行われますので、一番上のfunction c()から処理され消滅していきます。 ホイスティング コンテキスト内で宣言した変数や関数の定義をコード実行前にメモリに配置することをいいます これにより、呼び出し元より下に変数や関数を定義することができるようになります a(); function a(){ console.log('a') } これは「a」が出力されます varホイスティングの初期値には、undefinedになります。 console.log(b); var b = 'b' これは「undefined」が出力されます let・const ホイスティングの初期値が初期化されません。 console.log(c); console.log(d); let c = 'c' const d = 'd' これは「エラー」が出る 変数の場合、変数の定義(宣言)はホイスティングされるが、値の代入はコード上で行なっています。 そのため、「undefined」や「エラー」が表示されることになるのです。 つまり、入れ物だけが先に用意されているイメージですね。 スコープ グローバルスコープ <script> var a = 'a'; function b(){ } </script> scriptタグの直下(一番外側)のvar変数や関数は、グローバルスコープのwindowオブジェクトのプロパティとして保持されます。 スクリプトスコープ <script> let a = 'a'; const b = 'b'; </script> scriptタグの直下(一番外側)のlet変数やconstはスクリプトスコープとなります。 関数スコープ 有効範囲:関数内 function a(){ let b = 'b'; cosole.log(b); } a(); これは、bが出力されます。 関数a()の中で変数bを定義し、出力しています。 function a(){ let b = 'b'; } cosole.log(b); これはエラーになります。 関数の外から関数内で定義された変数bを呼ぼうとした為です。 ブロックスコープ 有効範囲:{}の中(if文など) { let a = 'a'; const b = 'b'; cosole.log(a); cosole.log(b); } aとbが出力されます { let a = 'a'; const b = 'b'; } cosole.log(a); cosole.log(b); これはエラーになります ちなみに、var変数や関数はブロックスコープにならない為{}の外から呼び出しても使用できてしまう事には注意です。 関数にブロックスコープを適用するには関数式を活用しましょう const a = function (){ let b = 'b'; console.log(b); } モジュールスコープ モジュールとはソースコードを機能ごとに分割し、メンテナンスしやすくする仕組みのことですね。 んでモジュールスコープとは、モジュールを使用した際のスクリプトスコープに当たる部分だそうです。 つまり、モジュール内で定義された変数や関数はそのモジュール内からしか参照できない。という事です。 レキシカルスコープ コードを書く場所によって参照できる変数が変わるスコープ 記述した時点で範囲が決定する為、静的スコープともいう。。。 こんな感じに自身のスコープより外側のスコープの変数は参照できます。(外部スコープとも言う) しかし、内側はできません。 また、今回の例のようにスコープが複数階層で連なっている状態をスコープチェーンといいます。 スコープチェーンでは、内側のスコープから順に変数を探していくことになります。 スコープと実行コンテキストの関係 実行コンテキスト:実行する環境 スコープ:実行中のコードから見える範囲 グローバルコンテキストの中には グローバルスコープ、グローバルオブジェクト、this が含まれます。 関数コンテキストの中に 実行中のコンテキスト内の変数・関数、arguments、super、this、外部変数(レキシカルスコープ) が含まれています。 クロージャー レキシカルスコープの変数を関数が使用している状態のことです。 このクロージャーの考え方を使い、プライベート変数や動的な関数の生成を行う事ができるようになります。 スコープの使い方応用編とでも言えばいいでしょうか ①プライベート変数の定義 外部からはアクセスできない変数のことです。 まずは普通の変数から見ていきます。 let num = 0; function increment(){ num = mum++; console.log(num); } increment(); //1が出力される increment(); //2が出力される この場合、変数numはグローバル変数となるため、何処からでも変更が可能になります。 では、関数スコープの中で変数を生成してみましょう。 function increment(){ let num = 0; num = mum++; console.log(num); } increment(); //1が出力される increment(); //1が出力される 関数の中に変数numを入れるだけでは、関数increment()を呼び出すたびに変数numを初期化してしまいます。 それでは、クロージャーの考え方を使用し、プライベート変数を生成します。 function incrementFactory(){ let num = 0; function increment(){ num = mum++; console.log(num); } return increment; } const increment = incrementFactory(); increment(); //1が出力される increment(); //2が出力される これで、変数numは外部からアクセスできず、関数increment()からでしか変更できなくなりました。 どのように処理されるでしょうか? 先ず、const increment = incrementFactory(); この関数式で関数incrementFactory()を呼び出し、実行します。 関数式は定義したタイミングで一度だけ実行され、その返り値が、変数に格納されます。 つまり、const increment = incrementFactory();を実行すると、 関数incrementFactory()が処理され、変数numが生成されます。 さらに、関数increment()が返され、const incrementに格納されます。 レキシカルスコープの説明で、自身のスコープより外側のスコープの変数は参照できるが内側はできない、とあったように関数スコープ内で定義された変数numは外側から参照することはできません。 つまり、関数incrementFactory()内からでしか変更できないと言うことです。 そして、const incrementは関数incrementFactory()から返却された関数increment()が入っています。 よってconst incrementを実行する事で関数incrementFactory()内の関数increment()を実行することになるため、変数numを変更する事ができるわけです。 ②動的な関数の生成 関数を生成する関数に渡す値によって、生成される関数が変化する function incrementFactory(num){ function increment(val){ return num + val; console.log(num); } return increment; } const incrementA = incrementFactory(10); incrementA(10); //20が出力される const incrementB = incrementFactory(100); increment(100); //200が出力される 関数incrementFactory()を呼び出す際に引数で渡す値によって定義されるプライベート変数を別々に管理できるようになります。 即時関数 関数定義と同時に一度だけ実行される関数 (function (引数){ 処理 })(実引数) 基本はこのように記述するようになります。 let AAA = (function (num){ return num * 2; })(10) 関数式で書くとこうなります。 ちなみにどうなっているのかと言うと function AAA(num){ return num * 2; } AAA(10); この関数AAA()を呼び出している部分を()で囲んで(AAA)(10)としているようなものです。 こうすることで、()で囲われた部分は一つの塊として扱うようになります。 宣言  再宣言 再代入 スコープ 初期化 let ❌ ⭕️ ブロックスコープ ❌ const ❌ ❌ ブロックスコープ ❌ var ⭕️ ⭕️ 関数スコープ undefined
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Azure Web PubSub と Node.js を組み合わせた軽いお試し

この記事は、以下の続きにあたる内容です。 ●Azure Web PubSub を試してみるための下調べ(JavaScript を利用する方向で) - Qiita  https://qiita.com/youtoy/items/c1c9c2893e0e0fe29703 記事執筆時点でプレビュー版の「Azure Web PubSub」を使ってみる話で、Node.js のプログラムと組み合わせてみたという内容になっています。 サンプルプログラムを使ったお試し 前の記事でも引用していた以下の記事の手順を見つつ、サンプルアプリを使った動作確認を行っていきます。 ●Azure Web PubSub でリアルタイムメッセージングアプリを作ろう #Azure リレー | cloud.config Tech Blog  https://tech-blog.cloud-config.jp/2021-05-11-azure-web-pubsub-azure/ 少し UI の見た目が違う部分などはありましたが、上記の手順通りに進めていき、以下のサンプルアプリを使うところまで進めていきます。 ●A Simple Client-Side WebSocket Chat  https://azure.github.io/azure-webpubsub/demos/clientpubsub.html 1つだけ最初に軽くハマった点があり、「クライアント URL ジェネレーター」の「ハブ」の部分にデフォルトの日本語の文字列が入っていた状態で、「クライアント アクセス URL」を生成すると、その後の「Simple Client-Side WebSocket Chat」を使うところの接続周りで失敗しました。 そのハブの名前の部分を英語の文字列に変えて試したところ、以下のようにうまく動作させられました。 こちらのサンプルを使って、PubSub のお試しができた!●A Simple Client-Side WebSocket Chat https://t.co/guE4kW368s pic.twitter.com/zxkeu64TLB— you (@youtoy) October 16, 2021 Node.js のプログラムと組み合わせる 上記の手順で Azure側の動作確認ができたので、今度は Node.js のプログラムと Azure との間のメッセージングを試していきます。 前回の記事にも掲載していた、以下の公式サンプルを使っていきます。 ●azure-webpubsub/samples/javascript/pubsub at main · Azure/azure-webpubsub  https://github.com/Azure/azure-webpubsub/tree/main/samples/javascript/pubsub サンプルプログラムで「ws と @azure/web-pubsub」または「@azure/web-pubsub」が使われています。 とりあえず、これらをインストールしておきます。 $ npm i @azure/web-pubsub ws そして、サンプルに少し変更を加えたものを動かしてみます。 とりあえずのお試しなので接続文字列等をソースコード中に直書きをしてしまってますが、もちろん、このあたりは別で読み込むようにしたほうが良いです。 const WebSocket = require('ws'); const { WebPubSubServiceClient } = require('@azure/web-pubsub'); const connectionString = '【Endpointで始まる「接続文字列」】'; const hubName = '【ハブの名前】'; async function main() { let serviceClient = new WebPubSubServiceClient(connectionString, hubName); let token = await serviceClient.getAuthenticationToken(); let ws = new WebSocket(token.url); ws.on('open', () => console.log('connected')); ws.on('message', data => console.log(data));; } main(); const { WebPubSubServiceClient } = require('@azure/web-pubsub'); const connectionString = '【Endpointで始まる「接続文字列」】'; const hubName = '【ハブの名前】'; const message = 'Hello!!'; let serviceClient = new WebPubSubServiceClient(connectionString, hubName); serviceClient.sendToAll(message, { contentType: "text/plain" }); ちなみに、接続文字列は Azureポータル上で、以下に表示されているものになります。 上記のプログラムを動作させた結果がこちらです。 Node.js のプログラムでの PubSub も成功!先ほどのサンプルアプリ上にもメッセージが表示された!(Node.js のプログラムのほうはテキストになってないけどw) pic.twitter.com/tSqi8X2cy9— you (@youtoy) October 16, 2021 Node.js のプログラムでの出力が「Buffer ...」となってしまっているので、元のソースコードの出力部分を以下のように書きかえました。 ws.on('message', data => console.log(data.toString())); Node.js の Subscribe のプログラム出力を、toString で文字列にして、こちらも想定通りの出力に。 pic.twitter.com/q5IoLbBhOC— you (@youtoy) October 16, 2021 おわりに 今回、Azure Web PubSub と Node.js を組み合わせて PubSub を軽く試してみて、無事に動かすことができました。 この後は、Node.js のプログラムに手を加えてサービス間・デバイス間連携を何か試すか、以下のサンプルの「functions」で気になった「/client/nodeclient/」あたりを試せればと思っています。 ●azure-webpubsub/samples at main · Azure/azure-webpubsub  https://github.com/Azure/azure-webpubsub/tree/main/samples
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptでHTML要素の高さを取得する方法

JavaScript で HTMLの要素の高さ / 幅を取得しようと思った時、outer やら avail やら offset やら、オブジェクトにやたらとプロパティが生えていて混乱したのでまとめました。 検証環境 iMac / iPad / iPhone にて 2021年10月時点での Google Chrome 最新版で検証しました。デスクトップ版の Chrome は v94 でした。 プロパティを全列挙するとごちゃごちゃして見づらくなるため、高さに絞って説明しています。幅を得る時は Height を Width に読み替えてください。 ディスプレイのサイズ - screen オブジェクト デバイスのディスプレイのサイズを得るには screen オブジェクト を参照します。 const h1 = window.screen.height; const h2 = window.screen.availHeight; height は画面の高さをピクセル数で表します。macOS の Dock を除いた領域は availHeight で得る事ができます。 ブラウザのサイズ - window オブジェクト ブラウザのサイズを得るには window オブジェクト を参照します。ブラウザが画面いっぱいに広がるタブレット・スマートフォンでは、ビューポートすなわち画面全体のサイズと同じです。 const h1 = window.outerHeight; const h2 = window.innerHeight; outerHeight はブラウザの高さそのものです。innerHeight はタブバーやブックマークバーを除いた HTML 文書の表示領域の高さです。 HTML の領域のサイズ - document.documentElement オブジェクト HTML の領域のサイズを得るには document.documentElement オブジェクト を参照します。このオブジェクトはブラウザではルートの <html> 要素を示します。 const h1 = document.documentElement.offsetHeight; const h2 = document.documentElement.scrollHeight; const h3 = document.documentElement.clientHeight; 図の緑色の枠は、文書などのコンテンツがレンダリングされた領域を示しています。 offsetHeight は線やスクロールバーを含む高さを整数で得る事ができます。コンテンツが伸びて縦スクロールが発生する場合は、伸びた分の高さがセットされます。 scrollHeight は線・マージン・スクロールバーを含まない内側のサイズです。コンテンツが伸びて縦スクロールが発生する場合は、伸びた分の高さがセットされます。 clientHeight は線・マージンを含まない内側のサイズです。コンテンツの伸び縮みは影響しません。 コンテンツ領域のサイズ - body オブジェクト コンテンツ領域のサイズを得るには body オブジェクト を参照します。 左の図は中身のコンテンツに合わせて body が伸び縮みする例を示しています。右の図は height:100vh など高さを制限した例を示しています。 const h1 = document.body.offsetHeight; const h2 = document.body.scrollHeight; const h3 = document.body.clientHeight; offsetHeight は線やパディングを含む高さを整数で得る事ができます。body の高さそのものを表した値で、中身のコンテンツがはみ出た分は考慮されません。 scrollHeight は線を含まない内側のサイズです。コンテンツが伸びて縦スクロールが発生する場合は、伸びた分の高さがセットされます。 clientHeight は線を含まない内側のサイズです。body の高さそのものを表した値で、中身のコンテンツがはみ出た分は考慮されません。 サンプル screen / window / document / body の数値を書き出すサンプルページを作ってみました。 手元の実機で試した結果は以下の通りです。 device screen.height screen.availHeight window.outerHeight window.innerHeight iMac 27 inch全画面モード 1,440 1,415 1,440 1,440 iPad Pro 12.9 inch 5th 1,366 1,024 1,024 649 iPhone 7 Plus 736 736 736 679 iMac は解像度と一致しました。 https://support.apple.com/kb/sp689?locale=ja_JP iPad (横向きに置いています) は @GENZ_INC さんの iPad画面サイズ、ピクセル数早見表 の「CSSピクセル」と一致しました。 iPhone は @tomohisaota さんの iPhone/iPad/Apple Watch解像度(画面サイズ)早見表 の「ポイント」と一致しました。 ブラウザの開発者ツールでデバイスをエミュレートすると、任意のサイズのディスプレイでブラウザを画面いっぱいに広げた扱いになるようです。 要素のサイズ - Element 基底クラス HTML の要素の基底クラス Element は getBoundingClientRect メソッドを所有しています。このメソッドはビューポートからの位置のジオメトリである DomRect を返します。 const domRect = document.getElementById("foo").getBoundingClientRect(); /* domRect: DomRect { x: 65 y: 65 width: 430 height: 430 top: 65 right: 495 bottom: 495 left: 65 } */ top は要素の上位置を表します。height は線を含む要素そのものの高さです。bottom は要素の下端の位置を表します。 要素の中のコンテンツのサイズを得るには Element のプロパティを参照します。 const h1 = document.getElementById("foo").offsetHeight; const h2 = document.getElementById("foo").scrollHeight; const h3 = document.getElementById("foo").clientHeight; offsetHeight は線を含む要素そのものの高さを表します。この数値は getBoundingClientRect().height と一致します。 scrollHeight はコンテンツ全体の表示領域の高さを表します。コンテンツが伸びて縦スクロールが発生する場合は、伸びた分の高さも含みます。 clientHeight はコンテンツの表示領域のうち視認できる部分の高さを表します。 まとめ こうして整理してみるとなかなかに複雑ですね。outer / avail / offset などプロパティのプレフィクスは計算の意味を示したものとなっているようです。どのオブジェクトからどの数値を得るかを掴んでおけば混乱せずに目的の数値にたどり着く事ができそうです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

予約管理システム(LINE BOT+GAS)開発メモ

目的 突然予約管理システムを作ってみることになったので、調べながらやってみることにした。備忘録としてここに残していくよ。 GAS(Google Apps Script)とは? Google Apps Script(GAS)とは、Googleが開発・提供しているプログラミング言語で、JavaScriptをベースに作成されている。 GASでは複数のGoogle提供サービス(Google Apps)を連携し、いろんなことができる。 GASが連携できるのは以下のGoogleのサービス。 Gmail Googleドライブ Googleカレンダー Googleドキュメント Googleスプレッドシート Googleスライド Googleマップ Googleフォーム Goole翻訳 また、Google以外にも、各Webサービスが提供しているAPI等を利用すれば、それらのツールとも連携ができる。 たとえば、チャットツール(Slack、Microsoft Teams、Chatworkなど)と連携すれば、「特定の相手からのメールをSlackで通知する」、「今日のGoogleカレンダーでの予定をTeamsで通知する」といった活用もできる。 LINE messaging API + GAS 最低限、LINEで動くチャットボットを作るには、以下の3つが必要。 LINE Buisinessアカウント httpsに対応したサーバー ユーザーのメッセージに返信するためのプログラム 今回は、このGASを利用してサーバとプログラムを用意する。 GASはいくつか関数を用意すると、PaaSのようにウェブサーバとして利用することができる。 LINE Messaging APIを利用するには、ユーザからのメッセージを受け取るサーバーを用意する必要があるので、このサーバーをGASで用意してWebhook URLとして設定する。 機能 コード var CHANNEL_ACCESS_TOKEN = "チャネルアクセストークン"; var ERROR_SHEET_ID = "スプレッドシートのID"; //日付、時刻のフォーマット設定 var dateExp = /(\d{2})\/(\d{2})\s(\d{2}):(\d{2})/; var dayExp = /(\d+)[\/月](\d+)/; var hourMinExp = /(\d+)[:時](\d+)*/; function doPost(e) { try { handleMessage(e); } catch(error) { logging("ToCalendarFromLineBot"); logging(JSON.stringify(e)); logging(JSON.stringify(error)); var replyToken = JSON.parse(e.postData.contents).events[0].replyToken; reply(replyToken, error.message); } } function logging(str) { var sheet = SpreadsheetApp.openById("スプレッドシートのID").getActiveSheet(); var ts = new Date().toLocaleString("japanese", {timeZone: "Asia/Osaka"}); sheet.appendRow([ts, str]); } function handleMessage(e) { var replyToken = JSON.parse(e.postData.contents).events[0].replyToken; var lineType = JSON.parse(e.postData.contents).events[0].type if (typeof replyToken === "undefined" || lineType === "follow") { return; } var userMessage = JSON.parse(e.postData.contents).events[0].message.text; var cache = CacheService.getScriptCache(); var type = cache.get("type"); if (type === null) { if (userMessage === "予定追加") { cache.put("type", 1); reply(replyToken, "予定日を教えてください!\n「06/17, 6月17日」などの形式なら大丈夫です!"); } else if (userMessage === "一週間の予定確認") { reply(replyToken, getEventss()); } else { reply(replyToken, "リッチメニューの「予定追加」で予定追加を、「一週間の予定確認」で一週間の予定参照ができるので気軽に話しかけてくださいね!"); } } else { if (userMessage === "キャンセル") { cache.remove("type"); reply(replyToken, "キャンセルしました!"); return; } switch(type) { case "1": // 予定日 var [matched, month, day] = userMessage.match(dayExp); //dayexpの型に合う文字列を取り出す。 cache.put("type", 2); cache.put("month", month); cache.put("day", day); reply(replyToken, month + "/" + day + "ですね! 次に開始時刻を教えてください。「13:00, 13時, 13:20, 13時20分」などの形式なら大丈夫です!"); break; case "2": // 開始時刻 var [matched, startHour, startMin] = userMessage.match(hourMinExp); cache.put("type", 3); cache.put("start_hour", startHour); if (startMin == null) startMin = "00"; cache.put("start_min", startMin); reply(replyToken, startHour + ":" + startMin + "ですね! 次に終了時刻を教えてください。"); break; case "3": // 終了時刻 var [matched, endHour, endMin] = userMessage.match(hourMinExp); cache.put("type", 4); cache.put("end_hour", endHour); if (endMin == null) endMin = "00"; cache.put("end_min", endMin); reply(replyToken, endHour + ":" + endMin + "ですね! 最後に予定名を教えてください!"); break; case "4": // 予定名 cache.put("type", 5); cache.put("title", userMessage); var [title, startDate, endDate] = createEventData(cache); reply(replyToken, toEventFormat(title, startDate, endDate) + "\nで間違いないでしょうか? よろしければ「はい」をやり直す場合は「いいえ」をお願いいたします!"); break; case "5": // 確認の回答がはい or いいえ cache.remove("type"); if (userMessage === "はい") { var [title, startDate, endDate] = createEventData(cache); CalendarApp.getDefaultCalendar().createEvent(title, startDate, endDate); reply(replyToken, "追加しました!\nお疲れ様でした!"); } else { reply(replyToken, "お手数ですがもう一度お願いいたします!"); } break; default: reply(replyToken, "申し訳ありません。\n形式に誤りがないか確認してみて、なければ「キャンセル」で予定入力をキャンセルすることができるので、そちらを試していただけますか?"); break; } } } function createEventData(cache) { var year = new Date().getFullYear(); var title = cache.get("title"); var startDate = new Date(year, cache.get("month") - 1, cache.get("day"), cache.get("start_hour") - 9, cache.get("start_min")); var endDate = new Date(year, cache.get("month") - 1, cache.get("day"), cache.get("end_hour") - 9, cache.get("end_min")); return [title, startDate, endDate]; } function toEventFormat(title, startDate, endDate) { var start = Utilities.formatDate(startDate, "JST", "MM/dd HH:mm"); var end = Utilities.formatDate(endDate, "JST", "MM/dd HH:mm"); var str = title + ": " + start + " ~ " + end; return str; } function reply(replyToken, message) { var url = "https://api.line.me/v2/bot/message/reply"; UrlFetchApp.fetch(url, { "headers": { "Content-Type": "application/json; charset=UTF-8", "Authorization": "Bearer " + CHANNEL_ACCESS_TOKEN, }, "method": "post", "payload": JSON.stringify({ "replyToken": replyToken, "messages": [{ "type": "text", "text": message, }], }), }); return ContentService.createTextOutput(JSON.stringify({"content": "post ok"})).setMimeType(ContentService.MimeType.JSON); } function get_Calendar() { var arrCals=[]; arrCals.push(CalendarApp.getCalendarById('使いたいGoogleカレンダーのアカウントのメールアドレス')); return arrCals; } //1週間の予定を取得するメインの関数 function get_Week_Schedule(){ var arrCals = get_Calendar(); var dateNow = new Date(); var date = new Date(); var strBody =''; var tmpBody =''; var strIntro = "おつかれさまです。\n1週間の予定です。\n" ; for (var j = 1; j < 7 ; j++ ){ date.setDate(dateNow.getDate()+j); for (var i = 0 ; i < arrCals.length ; i++){ tmpBody = tmpBody + getEvents(arrCals[i],date); } if (tmpBody){ strBody = strBody + date.getDate() + '日\n' + tmpBody; } tmpBody = ''; } return (strIntro + strBody); } //予定を取得する関数 function getEvents(Cals,getDate){  var arrEvents = Cals.getEventsForDay(getDate);//カレンダーの予定取得 var strEvents =''; for (var i=0; i<arrEvents.length; i++){ var strTitle = arrEvents[i].getTitle(); var strStart = _HHmm(arrEvents[i].getStartTime()); var strEnd = _HHmm(arrEvents[i].getEndTime()); if (strStart == strEnd){ strEvents = strEvents + '終日イベント:' + strTitle + '\n'; }else{ strEvents = strEvents + strStart + '~' + strEnd+ ':' + strTitle + '\n'; } } return strEvents; } //予定確認時の関数 function getEventss() { var events = CalendarApp.getDefaultCalendar().getEventsForDay(new Date()); var body; if (events.length === 0) { body = "今週の予定はありません!"; return body; } else { body = get_Week_Schedule(); return body; } } function toHHmm(date){ return Utilities.formatDate(date, "JST", "HH:mm"); } function _HHmm(str){ return Utilities.formatDate(str,'JST','HH:mm'); } 解説 function doPost(e) { try { handleMessage(e); } catch(error) { logging("ToCalendarFromLineBot"); logging(JSON.stringify(e)); logging(JSON.stringify(error)); var replyToken = JSON.parse(e.postData.contents).events[0].replyToken; reply(replyToken, error.message); } } doPost関数 WebアプリにPOSTリクエストが送られた時に実行される関数。 Postが発生した際に、POSTデータをeに格納する。 try...catch try { try_statements } catch (exception_var) { catch_statements } finally { finally_statements } ・try_statements 実行される文です。 ・catch_statements try ブロックの中で例外が発生した場合に実行される文です。 ・exception_var 関連する catch 節に対して例外オブジェクトを保持する識別子です。 ・finally_statements try 文が完了した後に実行される文です。これらの文は、例外が発生されたり捕捉されたりしたかどうかに関係なく実行されます。 工夫したところ new Date()に時間を入れると、日本時間に直そうと+9時間してくるので、createEventData関数を var startDate = new Date(year, cache.get("month") - 1, cache.get("day"), cache.get("start_hour")-9, cache.get("start_min")); var endDate = new Date(year, cache.get("month") - 1, cache.get("day"), cache.get("end_hour")-9, cache.get("end_min")); このように、cache.get("start_hour")を-9時間している。 これが正しい直し方かは分からない。 ちな、cache.get("month") - 1は、月は0~11で1~12月を表しているから -1してる。 改善点 参考記事 https://qiita.com/dekamintv/items/a4cb9a195f80ef4fbdfa#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB https://qiita.com/gawawa124/items/384496ac31fac531f1f9#%E5%AE%8C%E6%88%90%E5%93%81 https://qiita.com/bow_arrow/items/32ac5d2b4c67bd0c1dc2#%E7%9B%AE%E7%9A%84 https://devpixiv.hatenablog.com/entry/2016/11/14/150000 https://www.jisakeisan.com/timezone/utc/ https://neos21.net/blog/2020/12/09-01.html
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

予約システム(LINE BOT+GAS)開発メモ

目的 ある理由で予約システムを作ってみることになったので、調べながらやってみることにした。備忘録としてここに残していくよ。 今回は(https://qiita.com/dekamintv/items/a4cb9a195f80ef4fbdfa#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB ) こちらの記事のソースコードを使わせて頂き、実装する実験のメモです。 次回以降はオリジナルで作ったものを紹介できればと思います! GAS(Google Apps Script)とは? Google Apps Script(GAS)とは、Googleが開発・提供しているプログラミング言語で、JavaScriptをベースに作成されている。 GASでは複数のGoogle提供サービス(Google Apps)を連携し、いろんなことができる。 GASが連携できるのは以下のGoogleのサービス。 Gmail Googleドライブ Googleカレンダー Googleドキュメント Googleスプレッドシート Googleスライド Googleマップ Googleフォーム Goole翻訳 また、Google以外にも、各Webサービスが提供しているAPI等を利用すれば、それらのツールとも連携ができる。 たとえば、チャットツール(Slack、Microsoft Teams、Chatworkなど)と連携すれば、「特定の相手からのメールをSlackで通知する」、「今日のGoogleカレンダーでの予定をTeamsで通知する」といった活用もできる。 LINE messaging API + GAS 最低限、LINEで動くチャットボットを作るには、以下の3つが必要。 LINE Buisinessアカウント httpsに対応したサーバー ユーザーのメッセージに返信するためのプログラム 今回は、このGASを利用してサーバとプログラムを用意する。 GASはいくつか関数を用意すると、PaaSのようにウェブサーバとして利用することができる。 LINE Messaging APIを利用するには、ユーザからのメッセージを受け取るサーバーを用意する必要があるので、このサーバーをGASで用意してWebhook URLとして設定する。 機能 コード var CHANNEL_ACCESS_TOKEN = "チャネルアクセストークン"; var ERROR_SHEET_ID = "スプレッドシートのID"; //日付、時刻のフォーマット設定 var dateExp = /(\d{2})\/(\d{2})\s(\d{2}):(\d{2})/; var dayExp = /(\d+)[\/月](\d+)/; var hourMinExp = /(\d+)[:時](\d+)*/; function doPost(e) { try { handleMessage(e); } catch(error) { logging("ToCalendarFromLineBot"); logging(JSON.stringify(e)); logging(JSON.stringify(error)); var replyToken = JSON.parse(e.postData.contents).events[0].replyToken; reply(replyToken, error.message); } } function logging(str) { var sheet = SpreadsheetApp.openById("スプレッドシートのID").getActiveSheet(); var ts = new Date().toLocaleString("japanese", {timeZone: "Asia/Osaka"}); sheet.appendRow([ts, str]); } function handleMessage(e) { var replyToken = JSON.parse(e.postData.contents).events[0].replyToken; var lineType = JSON.parse(e.postData.contents).events[0].type if (typeof replyToken === "undefined" || lineType === "follow") { return; } var userMessage = JSON.parse(e.postData.contents).events[0].message.text; var cache = CacheService.getScriptCache(); var type = cache.get("type"); if (type === null) { if (userMessage === "予定追加") { cache.put("type", 1); reply(replyToken, "予定日を教えてください!\n「06/17, 6月17日」などの形式なら大丈夫です!"); } else if (userMessage === "一週間の予定確認") { reply(replyToken, getEventss()); } else { reply(replyToken, "リッチメニューの「予定追加」で予定追加を、「一週間の予定確認」で一週間の予定参照ができるので気軽に話しかけてくださいね!"); } } else { if (userMessage === "キャンセル") { cache.remove("type"); reply(replyToken, "キャンセルしました!"); return; } switch(type) { case "1": // 予定日 var [matched, month, day] = userMessage.match(dayExp); //dayexpの型に合う文字列を取り出す。 cache.put("type", 2); cache.put("month", month); cache.put("day", day); reply(replyToken, month + "/" + day + "ですね! 次に開始時刻を教えてください。「13:00, 13時, 13:20, 13時20分」などの形式なら大丈夫です!"); break; case "2": // 開始時刻 var [matched, startHour, startMin] = userMessage.match(hourMinExp); cache.put("type", 3); cache.put("start_hour", startHour); if (startMin == null) startMin = "00"; cache.put("start_min", startMin); reply(replyToken, startHour + ":" + startMin + "ですね! 次に終了時刻を教えてください。"); break; case "3": // 終了時刻 var [matched, endHour, endMin] = userMessage.match(hourMinExp); cache.put("type", 4); cache.put("end_hour", endHour); if (endMin == null) endMin = "00"; cache.put("end_min", endMin); reply(replyToken, endHour + ":" + endMin + "ですね! 最後に予定名を教えてください!"); break; case "4": // 予定名 cache.put("type", 5); cache.put("title", userMessage); var [title, startDate, endDate] = createEventData(cache); reply(replyToken, toEventFormat(title, startDate, endDate) + "\nで間違いないでしょうか? よろしければ「はい」をやり直す場合は「いいえ」をお願いいたします!"); break; case "5": // 確認の回答がはい or いいえ cache.remove("type"); if (userMessage === "はい") { var [title, startDate, endDate] = createEventData(cache); CalendarApp.getDefaultCalendar().createEvent(title, startDate, endDate); reply(replyToken, "追加しました!\nお疲れ様でした!"); } else { reply(replyToken, "お手数ですがもう一度お願いいたします!"); } break; default: reply(replyToken, "申し訳ありません。\n形式に誤りがないか確認してみて、なければ「キャンセル」で予定入力をキャンセルすることができるので、そちらを試していただけますか?"); break; } } } function createEventData(cache) { var year = new Date().getFullYear(); var title = cache.get("title"); var startDate = new Date(year, cache.get("month") - 1, cache.get("day"), cache.get("start_hour") - 9, cache.get("start_min")); var endDate = new Date(year, cache.get("month") - 1, cache.get("day"), cache.get("end_hour") - 9, cache.get("end_min")); return [title, startDate, endDate]; } function toEventFormat(title, startDate, endDate) { var start = Utilities.formatDate(startDate, "JST", "MM/dd HH:mm"); var end = Utilities.formatDate(endDate, "JST", "MM/dd HH:mm"); var str = title + ": " + start + " ~ " + end; return str; } function reply(replyToken, message) { var url = "https://api.line.me/v2/bot/message/reply"; UrlFetchApp.fetch(url, { "headers": { "Content-Type": "application/json; charset=UTF-8", "Authorization": "Bearer " + CHANNEL_ACCESS_TOKEN, }, "method": "post", "payload": JSON.stringify({ "replyToken": replyToken, "messages": [{ "type": "text", "text": message, }], }), }); return ContentService.createTextOutput(JSON.stringify({"content": "post ok"})).setMimeType(ContentService.MimeType.JSON); } function get_Calendar() { var arrCals=[]; arrCals.push(CalendarApp.getCalendarById('使いたいGoogleカレンダーのアカウントのメールアドレス')); return arrCals; } //1週間の予定を取得するメインの関数 function get_Week_Schedule(){ var arrCals = get_Calendar(); var dateNow = new Date(); var date = new Date(); var strBody =''; var tmpBody =''; var strIntro = "おつかれさまです。\n1週間の予定です。\n" ; for (var j = 1; j < 7 ; j++ ){ date.setDate(dateNow.getDate()+j); for (var i = 0 ; i < arrCals.length ; i++){ tmpBody = tmpBody + getEvents(arrCals[i],date); } if (tmpBody){ strBody = strBody + date.getDate() + '日\n' + tmpBody; } tmpBody = ''; } return (strIntro + strBody); } //予定を取得する関数 function getEvents(Cals,getDate){  var arrEvents = Cals.getEventsForDay(getDate);//カレンダーの予定取得 var strEvents =''; for (var i=0; i<arrEvents.length; i++){ var strTitle = arrEvents[i].getTitle(); var strStart = _HHmm(arrEvents[i].getStartTime()); var strEnd = _HHmm(arrEvents[i].getEndTime()); if (strStart == strEnd){ strEvents = strEvents + '終日イベント:' + strTitle + '\n'; }else{ strEvents = strEvents + strStart + '~' + strEnd+ ':' + strTitle + '\n'; } } return strEvents; } //予定確認時の関数 function getEventss() { var events = CalendarApp.getDefaultCalendar().getEventsForDay(new Date()); var body; if (events.length === 0) { body = "今週の予定はありません!"; return body; } else { body = get_Week_Schedule(); return body; } } function toHHmm(date){ return Utilities.formatDate(date, "JST", "HH:mm"); } function _HHmm(str){ return Utilities.formatDate(str,'JST','HH:mm'); } 解説 function doPost(e) { try { handleMessage(e); } catch(error) { logging("ToCalendarFromLineBot"); logging(JSON.stringify(e)); logging(JSON.stringify(error)); var replyToken = JSON.parse(e.postData.contents).events[0].replyToken; reply(replyToken, error.message); } } doPost関数 WebアプリにPOSTリクエストが送られた時に実行される関数。 Postが発生した際に、POSTデータをeに格納する。 try...catch try { try_statements } catch (exception_var) { catch_statements } finally { finally_statements } ・try_statements 実行される文です。 ・catch_statements try ブロックの中で例外が発生した場合に実行される文です。 ・exception_var 関連する catch 節に対して例外オブジェクトを保持する識別子です。 ・finally_statements try 文が完了した後に実行される文です。これらの文は、例外が発生されたり捕捉されたりしたかどうかに関係なく実行されます。 工夫したところ new Date()に時間を入れると、日本時間に直そうと+9時間してくるので、createEventData関数を var startDate = new Date(year, cache.get("month") - 1, cache.get("day"), cache.get("start_hour")-9, cache.get("start_min")); var endDate = new Date(year, cache.get("month") - 1, cache.get("day"), cache.get("end_hour")-9, cache.get("end_min")); このように、cache.get("start_hour")を-9時間している。 これが正しい直し方かは分からない。 ちな、cache.get("month") - 1は、月は0~11で1~12月を表しているから -1してる。 改善点 参考記事 ・LINE Developers設定などに関して https://udemy.benesse.co.jp/development/app/line-bot-making.html ・参考にしたソースコード https://qiita.com/dekamintv/items/a4cb9a195f80ef4fbdfa#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB ・オウム返し https://qiita.com/tetrapod117/items/e7b48485c98f6b88f311 ・リッチメニュー作成 https://qiita.com/bow_arrow/items/32ac5d2b4c67bd0c1dc2#%E7%9B%AE%E7%9A%84 ・GASについて https://devpixiv.hatenablog.com/entry/2016/11/14/150000 ・JavaScript日本時間について https://www.jisakeisan.com/timezone/utc/ https://neos21.net/blog/2020/12/09-01.html
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptのコンテキストについて

コンテキスト まずは言葉の定義から説明すると、あるコードが実行される際の文脈や状況のことを実行コンテキストといいます。 例えばブラウザ上でコードを実行した場合は、windowオブジェクトとthisがJavaScriptエンジンによって準備されるのでこれらが実行可能となります。 実行コンテキストには3つの種類があります。 1. グローバルコンテキスト 2. 関数コンテキスト 3. evalコンテキスト しかし、3のeval関数はES6から非推奨になっています。 グローバルコンテキスト グローバルコンテキストでは宣言した変数と関数、グローバルオブジェクト、thisの3つが利用できます。 お使いのエディタで適当なindex.htmlとmain.jsを作成した時main.jsの直下に書かれたコードが実行される環境がグローバルコンテキストとなります。 関数コンテキスト 関数コンテキストでは宣言した変数と関数と「arguments」,「super」,[this],[外部変数]の4つがJavaScriptエンジンによって準備され利用できます。 実行結果 this(window),arguments,a(0)の3つがコンソールに出力されています。 参考 Udemy: 【JS】ガチで学びたい人のためのJavaScriptメカニズム
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初学者向け】JavaScriptのsplitについて

今回はJavaScriptのsplitについて初学者の方向けに解説して行こうと思います。 ①splitについて 前回解説したjoin( )とは逆に文字列を区切り文字のところで分割して、それを配列できるメソッドになります 重要 point①文字列→配列に変換 point②指定した(任意の)"区切り文字"の箇所でバラバラに 区切り文字では「:」(コロン)や「-」(ハイフン) や「 」(空文字)なんかがよく使われます。 ☆区切り文字をsplit( )の引数として渡す際は「''」で囲いましょう ②コードで解説 (1) 区切り文字に「-」(ハイフン)を指定 // 本日の日付を文字列で用意 const today = '2021-10-16'; // ハイフンの箇所で分割して配列に console.log(today.split('-')); // ["2021", "10", "16"] (2) 区切り文字に「:」(コロン)を指定 // 今の時間を用意 const t = '17:08:24'; // コロンの箇所で分割して配列に console.log(t.split(':')); // ["17", "08", "24"] さらに、さらにここで取り出した配列を以前解説した分割代入で変数に代入 // hour minute second に分割代入 const [hour, minute, second] = t.split(':'); // それぞれ表示 console.log(hour); // 17 console.log(minute); // 08 console.log(second); // 24 まとめ 今回解説したsplit( )は正規表現と組み合わせて使われたり、他のメソッドと組み合わせて使うこともよくあります。今回の解説を通して少しでも理解が深まればいいなと思っております
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javascriptの基本からおさらいしてみた

変数 変数とは繰り返し使う値を格納しておく便利な入れ物です。プログラミングにおいて同じ事を繰り返す行為はバッドケースです。そのようなコードの冗長性を回避するためにも変数は避けては通れません。 変数を定義することを変数を宣言すると言います。変数の宣言方法は下記の3つになります。 JS let a = 0; const b = 1; var c = 2; 現在のES6からはvarの使用は非推奨になっております。 使い分けの詳細は別記事にて紹介します。 関数 関数とは、複数の処理をひとまとまりにしたものです。メソッドと呼ばれることもあります(後述のオブジェクトにて説明します)。 下記のコードで説明すると、helloという関数を実行すると{}の中身が実行されブラウザのログに「hello」という文字列が出力されます。 変数と同様に関数を定義することを関数を宣言すると言います。 JS function hello() { console.log("hello"); } 関数には「引数」というものが設定できます。また関数の呼び出し元に返す値を「戻り値」と言います。 JS function fn(引数) { return 戻り値; } 引数を渡して関数を実行する際は関数の末尾に()をつけ中に引数を 書くことで実行できます。 JS function fn(引数) { return 戻り値; } fn(引数1); オブジェクト オブジェクトとは名前(プロパティーまたはキー)と値(バリュー)を ペアで管理する便利な入れ物になります。 JS let obj = { property1: "hello", property2: function(){ }, property3: { d: 'obj in obj' }, } オブジェクトは ・ 名前 : 値 ・ 名前 : 関数 ・ 名前 : オブジェクト のように格納することができます。これらのプロパティへのアクセス方法は下記の2つの方法があります。 ・ ドット記法 JS obj.name ・ブラケット記法 JS obj['name'] また、オブジェクトのプロパティに格納された関数を「メソッド」と表現します。 参考 Udemy: 【JS】ガチで学びたい人のためのJavaScriptメカニズム
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ES2022のArray.prototype.atは今の所遅い (2021/10/16時点)

Array.prototype.atは何ができるの Pythonのように, 負数を指定すると配列の後ろから取得できます. 特に最後尾の数個や, 文字列での末尾からの指定など, 待ち望まれていた機能です. (String.prototype.atもあります) const arr = [1, 2, 3] console.log(arr.at(-1)); // 3 速度 普通の添字で指定と速度を比べてみます. バージョンは > node -v v16.9.0 ソース const arr = [...Array(1000*1000)].map((_, i) => i); // for内の余分な計算は取っ払いたいので先に const len = arr.length; const len2 = len - 1; const nlen = -len; // [] 昇順 { const startTime = (new Date()).getTime(); for (let n = 0; n < 100; ++n) { for (let i = 0; i < len; ++i) { arr[i]; } } const endTime = (new Date()).getTime(); console.log(`インデックス昇順: ${endTime - startTime}ms`); } // [] 降順 { const startTime = (new Date()).getTime(); for (let n = 0; n < 100; ++n) { for (let i = len2; i >= 0; --i) { arr[i]; } } const endTime = (new Date()).getTime(); console.log(`インデックス降順 ${endTime - startTime}ms`); } // at 昇順 { const startTime = (new Date()).getTime(); for (let n = 0; n < 100; ++n) { for (let i = 0; i < len; ++i) { arr.at(i); } } const endTime = (new Date()).getTime(); console.log(`at昇順 ${endTime - startTime}ms`); } // at 降順 { const startTime = (new Date()).getTime(); for (let n = 0; n < 100; ++n) { for (let i = -1; i >= nlen; --i) { arr.at(i); } } const endTime = (new Date()).getTime(); console.log(`at降順 ${endTime - startTime}ms`); } 結果 インデックス昇順: 47ms インデックス降順 82ms at昇順 4189ms at降順 4462ms びっくりするほど遅いです. Chromeでも大体同じ傾向だったため, Nodejsの実装が特別悪いというわけでもなさそうです. polyfillのほうが大幅に速い? からコードを持ってきて, 簡易的に試してみます. Array.prototype.myat = function (n) { // ToInteger() abstract op n = Math.trunc(n) || 0; // Allow negative indexing from the end if (n < 0) n += this.length; // OOB access is guaranteed to return undefined if (n < 0 || n >= this.length) return undefined; // Otherwise, this is just normal property access return this[n]; } // myat 昇順 { const startTime = (new Date()).getTime(); for (let n = 0; n < 100; ++n) { for (let i = 0; i < len; ++i) { arr.myat(i); } } const endTime = (new Date()).getTime(); console.log(`myat昇順 ${endTime - startTime}ms`); } // myat 降順 { const startTime = (new Date()).getTime(); for (let n = 0; n < 100; ++n) { for (let i = -1; i >= mlen; --i) { arr.myat(i); } } const endTime = (new Date()).getTime(); console.log(`myat降順 ${endTime - startTime}ms`); } myat昇順 139ms myat降順 101ms 速いです. 一瞬バグではないかと思いましたが, 添字にNaNや文字列を入れたりしても挙動は合っていたので間違いではないようです. ループ数も100で合っています. 最初は遅い理由を関数のオーバーヘッドガーや単純に処理が多いからーなどと考えていましたが, そう簡単ではないようです. この先は実装を解明してもらえる方に… チラッ 現状組み込みのArray.prototype.atがびっくりするくらい遅いだけで, 本来は添字に近い性能が出るのではないかと思います. Webアプリ開発における普段使いでは大きな影響はないと思いますが, 将来的によく使う機能になると思うので今後の性能改善に期待です. 全体ソース const arr = [...Array(1000*1000)].map((_, i) => i); const len = arr.length; const len2 = len - 1; const nlen = -len; Array.prototype.myat = function (n) { // ToInteger() abstract op n = Math.trunc(n) || 0; // Allow negative indexing from the end if (n < 0) n += this.length; // OOB access is guaranteed to return undefined if (n < 0 || n >= this.length) return undefined; // Otherwise, this is just normal property access return this[n]; } // [] 昇順 { const startTime = (new Date()).getTime(); for (let n = 0; n < 100; ++n) { for (let i = 0; i < len; ++i) { arr[i]; } } const endTime = (new Date()).getTime(); console.log(`インデックス昇順: ${endTime - startTime}ms`); } // [] 降順 { const startTime = (new Date()).getTime(); for (let n = 0; n < 100; ++n) { for (let i = len2; i > 0; --i) { arr[i]; } } const endTime = (new Date()).getTime(); console.log(`インデックス降順 ${endTime - startTime}ms`); } // at 昇順 { const startTime = (new Date()).getTime(); for (let n = 0; n < 100; ++n) { for (let i = 0; i < len; ++i) { arr.at(i); } } const endTime = (new Date()).getTime(); console.log(`at昇順 ${endTime - startTime}ms`); } // at 降順 { const startTime = (new Date()).getTime(); for (let n = 0; n < 100; ++n) { for (let i = -1; i >= nlen; --i) { arr.at(i); } } const endTime = (new Date()).getTime(); console.log(`at降順 ${endTime - startTime}ms`); } // myat 昇順 { const startTime = (new Date()).getTime(); for (let n = 0; n < 100; ++n) { for (let i = 0; i < len; ++i) { arr.myat(i); } } const endTime = (new Date()).getTime(); console.log(`myat昇順 ${endTime - startTime}ms`); } // myat 降順 { const startTime = (new Date()).getTime(); for (let n = 0; n < 100; ++n) { for (let i = -1; i >= nlen; --i) { arr.myat(i); } } const endTime = (new Date()).getTime(); console.log(`myat降順 ${endTime - startTime}ms`); } ```  </div></details>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】Qiita デイリー LGTM 数ランキング【自動更新】

他のタグ AWS Android Docker Git Go iOS Java JavaScript Linux Node.js PHP Python Rails React Ruby Swift TypeScript Vim Vue.js 初心者 集計について 期間: 2021-10-16 ~ 2021-10-17 GitHub スターをもらえるととっても励みになります LGTM 数ランキング 1 位: ES2022のArray.prototype.atは今の所遅い (2021/10/16時点) JavaScript Node.js es2022 3 LGTM 1 ストック @totori-san さん ( 2021-10-16 16:20 に投稿 ) 2 位: Nuxt+fabric.jsで画像同士を重ね合わせるcanvasを作成 JavaScript canvas fabric Vue.js nuxt.js 2 LGTM 1 ストック @greenteabiscuit さん ( 2021-10-17 02:03 に投稿 ) 3 位: 【JavaScript活用】リベンジしてみた!!→QiitaAPIでバイクが好きがいるか調べてみた JavaScript 初心者 QiitaAPI bike VisualStudioCode 2 LGTM 0 ストック @yui-kouy さん ( 2021-10-16 23:09 に投稿 ) 4 位: JavaScriptでHTML要素の高さを取得する方法 JavaScript 1 LGTM 1 ストック @ringtail003 さん ( 2021-10-16 19:51 に投稿 ) 5 位: 算術符号よりめっちゃ高速だとの噂の「非対称符号系(ANS)」のコードを JavaScript で書いて、速度を測ってみた JavaScript 圧縮アルゴリズム 復号化 符号化 非対称符号系 1 LGTM 0 ストック @yamazaki3104 さん ( 2021-10-17 00:20 に投稿 ) 6 位: Azure Web PubSub と Node.js を組み合わせた軽いお試し JavaScript Node.js Azure AzureWebPubSub 1 LGTM 0 ストック @youtoy さん ( 2021-10-16 22:08 に投稿 ) 7 位: GR-ROSE と Node.js のプログラムの間でシリアル通信(一方向) JavaScript Node.js serialport がじぇるね GR-ROSE 1 LGTM 0 ストック @youtoy さん ( 2021-10-16 13:11 に投稿 ) 8 位: Azure Web PubSub を試してみるための下調べ(JavaScript を利用する方向で) JavaScript Node.js Azure AzureWebPubSub 1 LGTM 0 ストック @youtoy さん ( 2021-10-16 12:14 に投稿 ) 9 位: TypeScriptでuseRefを使う JavaScript TypeScript React 1 LGTM 0 ストック @shunnami さん ( 2021-10-16 11:07 に投稿 )
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[React] React+TypeScript 実践

React初心者がReact+TypeScriptについて勉強した時のメモです。 型がないせいでバグっている例 ボタンを押したらJSONPlaceholderのTodoのデータをaxiosを使ってデータ取得するプログラム。 https://jsonplaceholder.typicode.com/todos App.tsx import axios from "axios"; import { useState } from "react"; import { Todo } from "./Todo"; export default function App() { const [todos, setTodos] = useState<any>([]); const onClickFetchData = () => { axios.get("https://jsonplaceholder.typicode.com/todos").then((res) => { setTodos(res.data); console.log(res); }); }; return ( <div className="App"> <button onClick={onClickFetchData}>データ取得</button> {todos.map((todo) => ( <Todo title={todo.title} userid={todo.userid} /> ))} </div> ); } Todo.tsx export const Todo = (props) => { const { title, userid } = props; return <p>{`${title}(ユーザー:${userid})`}</p>; }; データ取得したらユーザー名がundefinedになるというバグが発生している。 取得データに型を定義する App.tsx import axios from "axios"; import { useState } from "react"; import { Todo } from "./Todo"; type TodoType = { userId: number; id: number; title: string; completed: boolean; }; export default function App() { const [todos, setTodos] = useState<Array<TodoType>>([]); const onClickFetchData = () => { axios .get<Array<TodoType>>("https://jsonplaceholder.typicode.com/todos") .then((res) => { setTodos(res.data); }); }; return ( <div className="App"> <button onClick={onClickFetchData}>データ取得</button> {todos.map((todo) => ( <Todo key={todo.id} title={todo.title} userid={todo.userId} /> ))} </div> ); } 取得するデータにtypeとして型を定義する。 axiosのget部分とuseStateの部分に取得データの型を当てることエディターの補完も効くようになりどこが間違っているかが分かる。 今回の場合は、以下の部分のuseridのiが小文字になっていたからというのが分かった。 <Todo title={todo.title} userid={todo.userid} /> propsに型定義 type TodoType = { userId: number; title: string; }; export const Todo = (props: TodoType) => { const { title, userId } = props; return <p>{`${title}(ユーザー:${userId})`}</p>; }; propsも同様に型を定義しておくことでiの小文字に気づけバグを防げる。 PickとOmit 型定義の部分(今回でいうTodoType)を切り出した場合に型を呼び出す際にPickかOmitを使うことで効率的に管理できる。 切り出したTodoType types/todo.ts export type TodoType = { userId: number; id: number; title: string; completed: boolean; }; Pick(抽出) import { TodoType } from "./types/todo" export const Todo = (props: Pick<TodoType, "userId" | "title">) => { const { title, userId } = props; return <p>{`${title}(ユーザー:${userId})`}</p>; }; Pickを使うことでこのコンポーネントで必要な型の情報のみ抜き出せる。 Omit(省略) import { TodoType } from "./types/todo" export const Todo = (props: Omit<TodoType, "id" | "completed">) => { const { title, userId } = props; return <p>{`${title}(ユーザー:${userId})`}</p>; }; OmitはPickの逆でTodoTypeの中から必要ないものを除いた形。 コンポーネント自体の型 export const Todo: FC<Pick<TodoType, "userId" | "title">> = (props) => { const { title, userId } = props; return <p>{`${title}(ユーザー:${userId})`}</p>; }; 関数コンポーネント自体の型定義。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

webpack入門(Node.jsの導入からjsファイルのバンドルまで)

はじめに 業務でwebpackを使用する機会があるので、学習したことをまとめていきます。 今回は、Node.jsのインストール・jsファイルのバンドル・Loaderを使ってcssのバンドルを実際に行います。 ※一度にいろいろやると分からなくなるので、今回はjsファイルのバンドルまで行います。次回Loaderを用いてcssやscssファイルをバンドルする手順についてまとめます。 以前の学習まとめはこちらpackage.jsonとpackage-lock.jsonのバージョン指定や必要性について 今回使用するツールなどのバージョン Node.js:12.16.3 npm:6.14.4 webpack: 5.58.1 webpack-cli: 4.9.0 webpack導入の前に 図が多くわかりやすかったので、こちらの記事を見ておくとイメージが少しつくかと思います。 webpack学習の基本のき 前準備 1.Node.js/npmの導入 webpackを使用するには、Node.jsが必要になるためまずはインストールをしていきます。Node.jsをインストールする際に、npmもインストールされます。 Node.jsダウンロードサイト インストールが完了したら、以下のコマンドで確認しバージョンが表示されればOK。 node -v v12.16.3 npm -v 6.14.4 2.プロジェクトディレクトリの作成とpackage.jsonの作成 Node.jsがインストールできたら、次はプロジェクト用のディレクトリを用意します。 作成したディレクトリに移動したら、以下のコマンドでpackage.jsonを作成します。 色々設定について聞かれますが、特に気にせずEnterでOK。 npm init 上記のようにすべてEnterで問題ない場合は、以下のようにオプションを付けることで何も聞かれることなくpackage.jsonが作成できます。 npm init -y 3.webpack/webpack-cliのインストール 今回はwebpackを使いたいということで、webpackとwebpack-cliの2つをとりあえずインストールします。webpackとwebpack-cliについての細かいところはこちらを →webpack/webpack-cli 以下のコマンドを実行。 npm install --save-dev webpack npm install --save-dev webpack-cli インストールを行うと、ディレクトリ内にnode_modulesが作成されます。 4.webpack.config.jsとバンドルするためのjsファイルを準備 webpack.config.jsとは、webpackの設定ファイルです。 このファイルで、エントリポイントやバンドルしたファイルの出力場所などなどを記載していきます。 まずは、エントリポイント用のjsファイルを作成します。 今回はindex.jsとします。中身に関しては後ほど。 次に、webpack.config.jsを作成します。 内容は以下のように。 webpack.config.js const path = require('path') module.exports = { entry: path.resolve(__dirname, "index.js"), //エントリポイントであるファイルのパスを指定 output: { path: path.resolve(__dirname, 'dist'), //バンドルしたファイルの出力先のパスを指定 filename: 'bundle.js' //出力時のファイル名の指定 } } const path = require('path') pathは、Node.jsにデフォルトであるモジュール。 path.resolve()は、引数のパスをもとに絶対パスを返します。 公式ドキュメントを読んだのですがよくわからなかったので以下のサイトも参考にしました。 絶対パスを返す。 引数の後ろから順に評価、結合していく。 途中で絶対パスが完成したら結合途中でも処理を抜ける。 path.resolve('/a', '/b', '/c'); => /c path.resolve('/a', '/b', 'c'); => /b/c path.resolve('/a', 'b', 'c'); => /a/b/c path.resolve('a', 'b', 'c'); => current_path/a/b/c path.resolve(__dirname, 'a', 'b', 'c'); => current_path/a/b/c 引数の右から順位処理をしていき、”/”に当たった時点で絶対パスが作成されるため、そこで処理が終了する。つまり引数の文字列の先頭に”/”がある場合、そこまでで処理が終わる。 最後まで絶対パスができなかった場合は実行時のcurrent_pathを付けて返す。 引用元:path.resolve()の使い方 そのため、今回であれば entryは、current_path/index.js output(出力する際に作成される)は、current_path/dist/bundle.js というようになります。 参考 Node.js v12.22.7 Documentation 現在のディレクトリ構成はこんな感じ practice ├─ node_modules ├─ index.js ├─ package.json ├─ package-lock.json └─ webpack.config.js 先ほどの、webpack.config.jsの出力設定だとpracticeディレクトリの直下にdistというディレクトリが作成され、そこにbundle.jsというバンドルファイルが作成されます。 5.html、jsファイルを用意する 前準備は大体できたので、あとはバンドルする用のファイルを用意する。 js用ディレクトリを用意し、jsディレクトリには、sum.jsとdevide.jsを作成します。 現在のディレクトリ構成は以下の通りです。 practice ├─ node_modules ├─ src | └─ js  | ├─ devide.js | └─ sum.js ├─ index.html ├─ index.js ├─ package-lock.json ├─ package.json └─ webpack.config.js 各ファイルに処理を記載します。 index.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>webpack学習</title> <!-- バンドルしたファイルを読み込む --> <script src="dist/bundle.js"></script> </head> <body> <p class="message">webpack学習中!!</p> </body> </html> devide.js export default function hello(a, b) { return a / b; } sum.js export default function sum(a, b) { return a + b; } index.js import sum from './src/js/sum.js'; import devide from './src/js/devide.js'; var a = 10; var b = 2; var result = 'sum()の結果は' + sum(a, b) + ' devide()の結果は' + devide(a, b); alert(result); これでファイルの準備はOK。 今回は、足し算結果を出力する処理が記載されたsum.jsと割り算結果を出力する処理が記載されたdevide.jsをバンドルする。 webpack.config.jsで設定したエントリーポイントがindex.jsであるため、このファイル内でバンドルしたいjsファイルを読み込む。 6.scriptsにコマンドを追加 package.jsonのscriptsでコマンドを追加することで、簡単にコマンド実行が出来るようになります。以下のようにコマンドを追加します。 package.json { "name": "practice", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "version": "webpack -v", "dev": "webpack --mode development", "build": "webpack --mode production", "watch": "webpack --mode development --watch", "test": "echo \"Error: no test specified\" && exit 1", }, "author": "", "license": "ISC", "devDependencies": { "webpack": "^5.58.1", "webpack-cli": "^4.9.0" } } scriptsでは「コマンド名:実際に実行したいコマンド」のように記載します。 基本的に実行するにはnpm run コマンド名で実行します。 試しに、npm run versionと実行すると、以下のようにwebpackなどのバージョンが確認できます。 webpack: 5.58.1 webpack-cli: 4.9.0 webpack-dev-server not installed 他の設定したコマンドのざっくりした説明は以下の通り。 webpack --mode developmentは、開発用のビルド webpack --mode productionは、本番環境用のビルド webpack --mode development --watchは開発用のビルドで、ファイルの変更を検知して自動でビルドしてくれる。 7.いざバンドル! 以下のコマンドでバンドルします。 npm run dev バンドルが完了したら、index.htmlをブラウザで確認します。 以下のように、アラートで処理結果が表示されればバンドル成功です。 まとめ 今回はNode.jsの導入からwebpackを用いたjsファイルのバンドルまでをやってみました。以前まではwebpackは難しいものというイメージがありましたがわかってくると便利そうだなと思いました。 いきなり実務で使われている、webpack.config.jsを見てしまうと設定が色々あってくじけてしまうのでぜひ今回のように少ない設定項目から始めるといいのではないかと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

これからReact始めたい人のための今日だけでできるTODO#24 TODOアプリの作成② リストの表示

TODOリストの表示 前回作成したdata.jsonをaxiosを利用して取得して表示します。 コードは以下のとおりです。 App.js import React, { useState, useEffect } from 'react'; import axios from 'axios'; const todoDataUrl = 'http://localhost:3100/todos'; function App() { const [todoList, setTodoList] = useState([]); useEffect(() => { const fetchData = async () => { const response = await axios.get(todoDataUrl); setTodoList(response.data); }; fetchData(); }, []); return ( <> <h1>TODO List</h1> <ul> {todoList.map((todo) => ( <li key={todo.id}> {todo.content}({todo.done ? '完了' : '未完了'}) </li> ))} </ul> </> ); } import useState とuseEffectが利用できるようにimportします。 さらにaxiosが利用できるようにaxiosもimportしておきます。 App.js import React, { useState, useEffect } from 'react'; import axios from 'axios'; axiosで通信する先を定義 json-serverを利用して、ローカルにサーバーを作りました。 コマンドを打ったポート先のURLとdata.jsonに定義したデータ配列の名前を組み合わせたローカルホストURLを定義します。 App.js const todoDataUrl = 'http://localhost:3100/todos'; App()の中に処理を記述 大きく分けて3つ記述を追加します。 - useState()による状態と状態を更新するための関数の定義 - useEffct()でマウント後に実行する処理 - return()の中に取得したデータを表示する処理 表示は以下のようになります。 filter()を使って未完了と完了を分けて表示する filter関数を使って未完了のリストと完了のリストを分けて表示されるようにします。 App.js const inCompletedList = todoList.filter((todo) => { return !todo.done; }); const completedList = todoList.filter((todo) => { return todo.done; }); それぞれ変数を定義してtodoListからfilter()を使って新しい配列を作成します。 あとはreturn()の中に記述map()を使って展開します。 App.js <h2>未完了TODO</h2> <ul> {inCompletedList.map((todo) => ( <li key={todo.id}> {todo.content}({todo.done ? '完了' : '未完了'}) </li> ))} </ul> <h2>完了TODO</h2> <ul> {completedList.map((todo) => ( <li key={todo.id}> {todo.content}({todo.done ? '完了' : '未完了'}) </li> ))} </ul>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[React] TypeScript基礎

React初心者です。 React+TypeScriptについて勉強した時のTypeScriptについてのメモです。 TypeScriptとは? Microsoftが開発したオープンソースの言語。 JavaScriptに型という概念を組み込むことで安全にバグの少ない開発ができるように。 開発者間で認識を合わせやすい。 エディタで補完が効くので開発効率UP。 コンポーネント志向のReactと相性がいい。 TypeScript基本の型 boolean型 let bool: boolean = true; number型 数値 let num: number = 0; string型 文字 let str: string = "AA"; Array型 配列 let arr1: Array<number> = [0, 1, 2]; let arr2: number[] = [0, 1, 2]; any型 どんな方でもエラーにならない let any1: any = false; void型 関数の型でvoidは何も返却していないことを表す 何も返却していない場合は、TypeScriptが察してくれるのであえて書かなくても平気。 const funcA = (): void => { const test = "TEST"; } null型 nullを表す。 let null1: null = null; undefined型 何も設定されていない状態を表す let undefined1: undefined = undefined; object 型 オブジェクト内の型を指定 let obj: { id: number, name: string } = {id: 0, name: "AAA" }; 実装の例1 (引数に型指定) export const Test = () => { const calcTotal = (num: number) => { const total = num * 5; console.log(total); }; const onClickTest = () => { calcTotal(10); }; return ( <> <button onClick={onClickTest}>実行</button> </> ); }; 計算した合計をコンソールで出力する。 この場合引数に型を指定しないとcalcTotal(10);の部分の数値に"1,000"のような形の文字列を入れた時NaNとコンソールに出力されてしまうので型を指定することで安全。 ちなみにcalcTotal("10");のような数値の文字列を入れた場合はJavaScriptが変換してくれるので計算結果が普通に出力される。 実装の例2 (返却値の型指定) export const Test = () => { const calcTotal = (num: number): number => { const total = num * 5; return total }; const onClickTest = () => { console.log(calcTotal(10)); }; return ( <> <button onClick={onClickTest}>実行</button> </> ); }; 今度は計算結果を返却する場合、返却値の型を指定することでtotalの値が数値であることを保証。 実装例3 (変数の型指定) export const Test = () => { const calcTotal = (num: number): number => { const total = num * 5; return total }; const onClickTest = () => { let total: number = 0; total = calcTotal(10) console.log(total); }; return ( <> <button onClick={onClickTest}>実行</button> </> ); }; 変数の型を指定することで変数は数値であることを保証。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

GR-ROSE と Node.js のプログラムの間でシリアル通信(一方向)

表題の件を試した際のメモです。 GR-ROSE と Node.js のプログラムとの間で、一方向のシリアル通信を軽く試してみます。 GR-ROSE側 以下の公式のプログラムを動かしてみます。 ●GR-ROSE チュートリアル:IDE for GRでスケッチ | Renesas  https://www.renesas.com/jp/ja/products/gadget-renesas/boards/gr-rose/project-arduino-sketch-ide-gr 開発には IDE for GR を使う形で、手順は上記のページ内のとおりです。 書き込みをする際、GR-ROSE のリセットボタンを押して USBドライブとして認識させてから行う、というのがあったりします。 プログラムは以下で、サンプルほぼそのままです。 遅延の設定だけ、100ミリ秒から 1秒に変えています。 void setup() { // put your setup code here, to run once: Serial.begin(9600); pinMode(PIN_LED1, OUTPUT); } void loop() { // put your main code here, to run repeatedly: digitalWrite(PIN_LED1, LOW); delay(1000); digitalWrite(PIN_LED1, HIGH); delay(1000); Serial.println("hello"); } こちらの動作確認を、IDE for GR のシリアルモニタで行っておきましょう。 以下のように、一定時間ごとに「hello」という文字列が出てくれば OK です。 Node.js側 Node.js側は、以下を使ったりするとシリアル通信を扱えるようになります。 ●serialport - npm  https://www.npmjs.com/package/serialport かなり前ですが、以下の記事に出てくるお試しの際に、利用したことがありました。 ●#toio を Alexa や micro:bit から操作する 〜概要編〜(JavaScriptライブラリを使ってみた) - Qiita  https://qiita.com/youtoy/items/0ee56f5395e3f4c72c51 今回は、読み取りだけできれば良いので、公式ドキュメントの Readline Parser のサンプルを利用します。 以下で「【自分の環境に合わせて変更】」と書いている部分は、ご自身の環境の設定に合わせて変更してください。 const SerialPort = require('serialport') const Readline = require('@serialport/parser-readline') const port = new SerialPort('/dev/【自分の環境に合わせて変更】') const parser = port.pipe(new Readline({ delimiter: '\r\n' })) parser.on('data', console.log) GR-ROSE は PC につないだまま動作させ、その状態でプログラムを実行すると、以下の出力が得られました。 おわりに とりあえず、公式サンプルを流用した GR-ROSE と Node.js のプログラムの間でシリアル通信を試しました。 さらに、両方のプログラムに処理を追加していって、何か面白そうなものを作っていければと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript入門(関数と宣言)①

概要 JavaScriptを学習、理解を深めるため「JavaScript Primer 迷わないための入門書」を読み、 理解した内容等を記載していく。 JavaScript入門一覧に他の記事をまとめています。 ※下記「動作環境情報」を参考にし、開発者コンソールで結果を確認してください 動作環境情報はコチラ 【環境】 PC:Mac エディタ:Visual Studio Code プラグイン:Live ServerというVisual Studio Codeのプラグインを使用し、サーバ環境を用意。 開発者コンソール:GoogleChrome 【登場ファイル】 func.js:関数を作成、定義するファイル index.html:「func.js」を使用、および開発者コンソールで確認する際に開くファイル 【★ポイント】 index.htmlのheadタグ内に<script src="func.js" type="module"></script>のように記述する index.html <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>JavaScript入門</title> <script src="func.js" type="module"></script> </head> <body> </body> </html> ※最終的に以下のような構成になる。 (任意の場所に「js」フォルダを作成および、フォルダに上記2ファイルを作成・配置する) この記事で理解できること 関数の宣言・定義方法 関数の呼び出し方法 関数の引数 関数と宣言 関数とは、ある一連の手続きを1つの処理としてまとめる機能のこと。 また、一度定義した関数を呼び出すことで同じ処理を何度も実行できる。 関数の宣言方法 JavaScriptで関数を定義するには、functionキーワードを使用する。(これで「今から記載するプログラムは関数なんだな」とJavaScriptが認識してくれる) function 関数名( 仮引数 ※任意 ) { 処理内容 };で関数を定義できる。 また、functionから始まる文を関数宣言と呼ぶ。 // function 関数名( 仮引数 ※任意 ) { 処理内容 };で関数を定義 function testFunc(value) { console.log("testFuncを実行"); }; 関数の要素 関数を構成する要素として、以下4つの要素が存在する。 1. 関数名 ・ 利用できる名前は変数名の命名規則と同じ(変数の命名規則についてはこちらの記事をご参照ください) 2. 仮引数 ・ 関数に渡す値が入る変数 ・ 複数ある場合はカンマ(,)で区切る 3. 処理内容(関数の内容) ・{ }で囲んだブロックの中に実行したい処理の内容を記載する 4. 返り値 ・ 関数が計算や処理した実行結果のこと ・ return文で実行結果を返すことができる ・ 返り値は必須ではないため、関数で実行結果を返す必要がない場合は、retrun文を省略可能 ・ 省略した場合、未定義という意味のundefinedという結果が返される ・ また、return文以降の処理は実行されないため、処理の記載に注意が必要 ★POINT「構成する要素ってなに?」 「にんじん・大根・豚肉」の具が入った豚汁で例えると 豚汁の構成(どんな物が入っているか)は①具の「にんじん・大根・豚肉」と②具以外の「水・出汁・味噌」になる。 なので豚汁を構成する要素=「水・出汁・味噌・にんじん・大根・豚肉」という考え方ができる。 ★POINT「仮引数と(実)引数」 ・仮引数は関数定義時に設定しておく引数のこと。 ・(実)引数は関数を実際に呼び出す際に渡す・渡される値のこと。 ◆例として、仮引数に与えられた値を2倍し、計算結果を返す関数「double」を作成してみる。 func.js // 各要素を持たせた関数を作成 function double(number) { // => 関数名は「double」、仮引数は「number」 const result = number * 2; // => 処理内容は「仮引数に与えられた値に2をかけて変数resultに代入」 return result; // => 返り値は「計算結果が代入された変数result console.log("return以降の処理") // => return以降の処理のため、実行されない } 関数の呼び出し方 作成した関数、もしくはJavaScriptが元々用意している関数(console.log()など)を使用し、処理を実行することを関数を呼び出すと表現することが多い。 それぞれ関数名();で呼び出せる。 ◆先ほどの例で作成した関数double( )に引数「2」を渡し、呼び出してみる func.js // 各要素を持たせた関数を作成 function double(number) { // => 関数名は「double」、仮引数は「number」 const result = number * 2; // => 処理内容は「仮引数に与えられた値に2をかけて変数resultに代入」 return result; // => 返り値は「計算結果が代入された変数result」 console.log("return以降の処理") // => return以降の処理のため、実行されない }; // => ここまでで関数作成が完了 // 関数doubleの引数に2を指定し、呼び出してみる console.log(double(2)); // => 4 関数の引数 関数に定義した仮引数の個数と実際に呼び出した際の引数の個数が違っても、関数を呼び出せる。 引数が省略されたときに、デフォルトの値を指定する デフォルト引数という構文も存在する。 呼び出し時の引数が少ないとき 定義した関数の仮引数よりも呼び出し時の引数が少ない場合、余った仮引数にはundefinedという値が代入される。 func.js // 仮引数「number」を持ち、与えられた引数を返す関数を定義 function argFunc(number) { return number; }; console.log(argFunc()); // => 引数を指定せずに関数を実行することができ、undefinedが返される //仮引数「name」「age」の二つを持ち、与えられた引数を配列で返す関数を定義 function arrayFunc(name, age) { return [name, age]; } console.log(arrayFunc('Taro')); // =>  一部引数を指定せずに関数を実行することができ、['Taro', undefined]と返される 呼び出し時の引数が多いとき 関数の仮引数に対して引数の個数が多い場合、あふれた引数は単純に無視される。 func.js // 仮引数「number」を持ち与えられた引数を返す関数を定義 function argFunc(number) { return number; }; console.log(argFunc(1, 2, 3)); // => 1 // 引数に指定した「2,3」は無視され、最初に指定した「1」のみ返される デフォルト引数 仮引数に対応する引数が渡されていない場合に、デフォルトで代入される値を指定できる。 function 関数名(仮引数 = デフォルト値){ 処理内容 };という構文でデフォルト値を設定できる。 複数指定する場合は、カンマ(,)で区切る。 ES2015から使用できる。 func.js //function 関数名(仮引数 = デフォルト値){ 処理内容 };でデフォルト値を設定 function arrayFunc(name = '名無しさん', age = '未設定') { return [name, age]; } console.log(arrayFunc()); // 引数を指定せず呼び出すと、['名無しさん', '未設定']とデフォルト値が返される 可変長引数 固定した数ではなく任意の個数の引数を受け取れる引数を可変長引数と呼ぶ。 Rest parameters(ES2015〜)か、関数の中でのみ参照できるargumentsという特殊な変数を利用する。 Rest parameters 仮引数名の前に...を付けた仮引数のこと(残余引数とも呼ばれる)。 関数に渡された値が配列として代入される。 通常の仮引数との組み合わせも可能だが、Rest parametersは末尾に指定する必要がある。 ES2015から使用できる。 func.js // 引数に指定された値を返す関数を定義し、仮引数名の前に...をつける function numberFunc(...numbers) { console.log(numbers); } // 通常の仮引数が1つとRest parametersを持つ関数を定義 function nameNumberFunc(name, ...numbers) { // Rest parameters(...)は末尾に指定 console.log(name); console.log(numbers); } // 任意に指定した値が配列として返される numberFunc(1); // => [1] numberFunc(1, 2); // => [1, 2] numberFunc(1, 2, 3); // => [1, 2, 3] nameNumberFunc(1, "Taro", "Hanako", "Goro"); // => 1 // =>  ['Taro', 'Hanako', 'Goro'] arguments 関数の中でのみ参照できる特殊な変数、オブジェクトのこと。 配列のようにインデックスで要素へアクセスできるのが特徴。(Arrayオブジェクトのメソッドは利用できない) ※Rest parameters(ES2015〜)が利用できる環境では、非推奨となっているため詳細は割愛。 関数の引数と分割代入 関数の引数も分割代入が可能。 分割代入はオブジェクトや配列からプロパティを取り出し、変数として定義し直す構文。★オブジェクトやオブジェクトの分割代入に関する記事はこちら ES2015から使用できる。 func.js // オブジェクトのプロパティ「name」をコンソールへ出力する関数を作成 function userName(obj) { console.log(obj.name); } // userオブジェクトを作成 const user = { name: "Taro", age: 21 }; // 関数userNameに引数としてuserオブジェクトを渡す userName(user); // => Taro (nameプロパティを取り出せていることが分かる)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[React] グローバルなstate管理(useContext)

React初心者がグローバルなstate管理について勉強した時のメモです。 現状 以下のような階層構造の時に、 App → ComponentA → ComponentB → ComponentC ComponentCにあさひというUser名の値を渡したい場合、各Componentでpropsのバケツリレーをしないといけない。 App.jsx import { ComponentA } from "./components/ComponentA"; export const App = () => { const name = "あさひ"; return ( <> <ComponentA name={name} /> </> ); }; ComponentA import { ComponentB } from "./ComponentB"; export const ComponentA = (props) => { const { name } = props; return ( <> <p>ComponentA</p> <ComponentB name={name} /> </> ); }; ComponentB.jsx import { ComponentC } from "./ComponentC"; export const ComponentB = (props) => { const { name } = props; return ( <> <p>ComponentB</p> <ComponentC name={name} /> </> ); }; ComponentC.jsx export const ComponentC = (props) => { const { name } = props; return ( <> <p>ComponentC</p> <p>{name}</p> </> ); }; Contextを使ったstate管理 Providerの作成 Providerを作成します。 UserProvider.jsx import React, { createContext } from "react"; export const UserContext = createContext({}) export const UserProvider = (props) => { const { children } = props; const contextName = "あさひ" return ( <UserContext.Provider value={{ contextName }}> {children} </UserContext.Provider> ) } ここで今回受け渡したい値のUser名あさひを設定します。 createContextを使用し以下のように囲うことで{children}に入ってくるコンポーネントではどこでもvalueの値を参照することができます。 <UserContext.Provider value={{ contextName }}> {children} </UserContext.Provider> App.jsx import { ComponentA } from "./components/ComponentA"; import { UserProvider } from "./provider/UserProvider"; export const App = () => { return ( <UserProvider> <ComponentA/> </UserProvider> ); }; 作ったProviderコンポーネントをApp.jsxでComponentAを囲います。 これでComponentA以下の全てのComponentでProviderで設定した値の参照ができます。 useContext useContextを使用してComponentCで値を取得します。 今回はUserContextのcontextNameを取得しています。 ComponentC.jsx import { useContext } from "react"; import { UserContext } from "../provider/UserProvider"; export const ComponentC = () => { const name = useContext(UserContext); return ( <> <p>ComponentC</p> <p>{name.contextName}</p> </> ); }; useStateと使う場合 useStateと一緒に使うことでstateを渡すことができます。 ProviderでuseStateを定義し、valueに値を設定します。 今回はカウントアップのためのstateをComponentCに渡していきます。 UserProvider.jsx import React, { createContext, useState } from "react"; export const UserContext = createContext({}); export const UserProvider = (props) => { const { children } = props; const [count, setCount] = useState(0); return ( <UserContext.Provider value={{ count, setCount }}> {children} </UserContext.Provider> ); }; ComponentC import { useContext } from "react"; import { UserContext } from "../provider/UserProvider"; export const ComponentC = () => { const { count, setCount } = useContext(UserContext); return ( <> <p>ComponentC</p> <p>{count}</p> <button onClick={() => setCount(count + 1)}>ボタン</button> </> ); }; useContextでProviderでセットしたstateを受け取ることで使用できます。 めっちゃ便利!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Azure Web PubSub を試してみるための下調べ(JavaScript を利用する方向で)

この記事は、以下の「Azure Web PubSub」に関する記事です。 どうやら、WebSocket を通信に使って、パブリッシュ - サブスクライブ型のメッセージングができるものらしく、「MQTTっぽい感じなのかな?」と気になりました。 ●Azure Web PubSub – WebSocket Web パブリッシング | Microsoft Azure  https://azure.microsoft.com/ja-jp/services/web-pubsub/ 過去に書いた Qiita の記事でも、リアルタイム通信系で WebSocket・MQTT は何度も登場していて、リアルタイム通信周りの技術は非常に気になるところです。 この Azure Web PubSub は、記事執筆時点では以下の公式ページのサービス名の部分に書かれているように、「プレビュー」という位置付けのようです。 調べてみた内容 とりあえず、ググって出てきた記事をいくつか見てみました。 Azure Web PubSub でリアルタイムメッセージングアプリを作ろう #Azure リレー | cloud.config Tech Blog Azure Web PubSub Public Preview - ほりひログ 公式の情報も見てみたり。 WebSocket と Azure Web PubSub を使用してリアルタイム アプリを簡単に構築する (プレビュー段階) | Azure のブログと更新プログラム | Microsoft Azure クイックスタート - ブラウザーから Azure Web PubSub インスタンスに接続する | Microsoft Docs チュートリアル - WebSocket API と Azure Web PubSub サービス SDK を使用してメッセージの発行とサブスクライブを行う | Microsoft Docs サクッと試す流れ ここまで情報を見ていって、とりあえずサクッとお試しをするには、以下の内容に手をつけるのが良さそうでした。 デモアプリ上で試す 上で書いていた 1つ目の記事の中で、ポータル上で情報を取得し、以下のデモアプリを使って簡単に試せそうな話が登場していました。 ●A Simple Client-Side WebSocket Chat  https://azure.github.io/azure-webpubsub/demos/clientpubsub.html その説明が出ていたので、具体的には以下の画像の部分です。 とりあえず、上記のインターフェースを使った PubSub を試すのが、ブラウザ上だけで完結して簡単そうでした。 JavaScriptライブラリを併用する ポータル上での動作確認が無事に行えたら、自分が書いたプログラムから試してみたいところです。 下調べをした中で、公式の GitHub の以下のサンプルページがひっかかりました。 ●azure-webpubsub/samples at main · Azure/azure-webpubsub  https://github.com/Azure/azure-webpubsub/tree/main/samples 複数の言語用のサンプルがあるようですが、普段、自分がやっている内容的には JavaScript が良いので、それを見てみます。 ●azure-webpubsub/samples/javascript at main · Azure/azure-webpubsub  https://github.com/Azure/azure-webpubsub/tree/main/samples/javascript こんな感じでサンプルが複数あるようですが、pubsub がシンプルそうで、最初に試すのに良さそうでした。 プログラム的には、こんな感じ。 subscribe.js const WebSocket = require('ws'); const { WebPubSubServiceClient } = require('@azure/web-pubsub'); async function main() { if (process.argv.length !== 4) { console.log('Usage: node subscribe <connection-string> <hub-name>'); return 1; } let serviceClient = new WebPubSubServiceClient(process.argv[2], process.argv[3]); let token = await serviceClient.getAuthenticationToken(); let ws = new WebSocket(token.url); ws.on('open', () => console.log('connected')); ws.on('message', data => console.log(data));; } main(); publish.js const { WebPubSubServiceClient } = require('@azure/web-pubsub'); if (process.argv.length !== 5) { console.log('Usage: node publish <connection-string> <hub-name> <message>'); return 1; } let serviceClient = new WebPubSubServiceClient(process.argv[2], process.argv[3]); // by default it uses `application/json`, specify contentType as `text/plain` if you want plain-text serviceClient.sendToAll(process.argv[4], { contentType: "text/plain" }); 気になったサンプル1: videoshare ぱっと見た中で、「videoshare」というやつが、ものすごく気になる... バイナリデータの PubSub。 気になったサンプル2: functions 以下も JavaScript で扱えそうな感じのもので、気になりました。 名前的に、Azure Functions で扱うようなものっぽいです。 ●azure-webpubsub/samples/functions/js at main · Azure/azure-webpubsub  https://github.com/Azure/azure-webpubsub/tree/main/samples/functions/js ●azure-webpubsub/samples/functions/client/nodeclient at main · Azure/azure-webpubsub  https://github.com/Azure/azure-webpubsub/tree/main/samples/functions/client/nodeclient おわりに Azure に関する調べものをしていて見かけた「Azure Web PubSub」について、お試しをする前段階の話を記事にしてみました。 この後は、JavaScriptライブラリを使って、過去に WebSocket・MQTT を使ってやっていたようなサービス間連携・デバイス連携などができそうか、調査・お試しを続けていければと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TypeScriptでuseRefを使う

import { useRef } from "react"; const sendInput = (inputValue: string | undefined) => { console.log(inputValue) } function App() { const inputRef = useRef<HTMLInputElement>(null); return ( <div className="App"> <input ref={inputRef} type="text" /> <button onClick={()=>sendInput(inputRef.current?.value)}>送信</button> </div> ); } export default App;
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】関数とオブジェクト①

はじめに Udemyの【JS】ガチで学びたい人のためのJavaScriptメカニズムの講座の振り返りです。 前回の記事 目的 関数とオブジェクトについての理解を深める 本題 1.関数 関数の特徴 JavaScriptでは、引数の順番が重要 // 関数を定義 function fn(a,b) { console.log(a,b); } // bの引数だけ渡したいと思っても,順番に出力されるからできない // もし、0を消した場合はundefinedとb側に出力される fn(0,1); bの値だけを渡したい場合どうすればいいのか // 関数を定義 function fn(a,b) { console.log(a,b); } // もしbの値だけ出力したい場合は、下記のようにaの値の場所にNULLと記述 fn(null,1); JavaScriptの関数名が重複していた場合、後から定義した関数名が有効になる // 関数を定義 function fn(a,b) { console.log(a,b); } // 同じ名前の関数を用意 function fn(a,b) { console.log(2); } // この場合、実行結果は2と出力される fn(1) // javascriptでは引数が違くても関数で判断されるので注意 // エラーも発生しない 関数の重複を避けたい場合はどのようにするべきか // 下記のように関数式で定義すると // constによってsyntax errorが発生する // 基本的には無名関数で書くのでfunctionの後ろのfnは削除(あっても動く) const fn = function (a,b) { console.log(a,b); } function fn(a,b) { console.log(2); } fn(1) default値について // bに予め値を渡しておく => default値 function fn(a,b = 1) { console.log(a,b); } // 実行結果はaで引数の1が、bではdefault値の1が渡ってくる fn(1); // もし実行結果の値がundefinedやnullだったら // nullだと「null」で返ってくる fn(1, null); // undefinedだとそのままdefault値が返ってくる fn(1,undefined); // 意図的に値を空にしたい場合はnullを使用する // 下記の書き方は基本的にしない let c = undefined; fn(1,c); // こっちが一般的 let d = null; fn(1,d); Argumentsについて 関数コンテキストによって自動生成される function fn() { console.log(arguments); console.log(a,b); } fn(1, undefined) 上記のようにargumentsを出力すると実行結果は下記の通り console. Arguments(2) [1, undefined, callee: (...), Symbol(Symbol.iterator): ƒ] 0: 1 1: undefined callee: (...) length: 2 Symbol(Symbol.iterator): ƒ values() get callee: ƒ () set callee: ƒ () [[Prototype]]: Object 0番目に1,1番目にundefinedと出力されている (配列、、、、?) function fn() { console.log(arguments); console.log(a,b); } // 実引数がargumentsに格納されて出力された fn(1, undefined) // 仮引数がなくてもargumentsの値は出力される 例 function fn() { // 下記のように変数a,bにargumentsの番号を代入 const a = arguments[0]; const b = arguments[1]; // このようにすることで、argumentsが引数の役割をargumentsが果たしていることになる console.log(arguments); console.log(a,b); } fn(1, undefined) // 引数の数が決まっていない場合(ループなど)で使用する しかし最近ではES6から導入されたレストパラメーターを使用する // 3つのドットとargsという値で表す function fn(...args) { // argsを出力すると配列として出力される console.log(args) } // 3つ目の引数を増やしたとしても対応できる fn(1, undefined,3) returnについて function fn() { const a = arguments[0]; const b = arguments[1]; // このようにaという値を呼び出し元関数fnに戻す return a; } // 下記でcに関数を渡す let c = fn(1, undefined); // returnによりfnにはaが入っているので、下記の実行結果は1になる console.log(c); 今日はここまで! 参考にさせて頂いた記事 【JS】ガチで学びたい人のためのJavaScriptメカニズム
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Firebase, Firestore] custom Map Object で混乱した話

プログラミング初心者の覚書です。 Unsupported field value: a custom Map object Firestoreでドキュメントを更新しようとしたときに発生したエラー文です。 FirebaseError: Function setDoc() called with invalid data. Unsupported field value: a custom Map object (found in field tags in document ではsetDoc()でどんなオブジェクトを更新しようとしていたかというと、 async saveProfile(user){ try { const obj = {...user} // --- abbr --- let tags = new Map(); tags.set(user.basic.gender, true); tags.set(user.basic.isTeacher, true); obj.tags = tags; await setDoc(docRef, obj, { merge: false }); } catch(e) { console.log(e); } } Firestoreからドキュメントを取得(user)し、フィールドの値をtagsというMapオブジェクトとして保持することで、タグ検索を行うというものでした。 (FirestoreではタグのAND検索クエリを実行できないため、この形を取っています。) Firestoreのフィールドには既にMapを設定できているのに、なぜこのエラーが発生したのかが分からず困っていました。 というか、そもそもnew Map()で宣言したものが、Map Objectではなく custom Map Object となる理由が分かりませんでした。 JavaScriptのリファレンスを見ても、Mapの宣言に問題があるようには思えませんでした。 コンソールで既存のMapフィールドと比較すると違いが分かった Firestore上でMapフィールドと表示されているものをコンソールで見てみると、理由が分かりました。 これで一発でした。 Cloud Firestoreのダッシュボードから見るとマップと書かれたものが、実際にはオブジェクトなんですね。 いや、よく考えてみたら自分でオブジェクトとして入れてたんですが。 という事で、公式リファレンスを見てみると、 ドキュメント内の複雑なネストされたオブジェクトはマップと呼ばれます。 Firestoreではオブジェクトを「マップ」と呼んでる 僕の理解が及んでいないのか何なのか分かりませんが、とりあえず現状の理解としては ・Firestoreが言う「マップ」はObjectのこと。 ・「マップ」はMapオブジェクトの事ではない。 と解釈しました。 自分でコードを書いたりドキュメントを読んでいくうちに、マップなのかオブジェクトなのか分からなくなってしまっていたようです。 マッピングとかそういう意味での「マップ」なんでしょう。 理解が足らなかったようなので、精進したいと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】関数は実行可能なオブジェクトである

はじめに 改めてJavaScriptの基本を学び直している。 その中で学んだ内容のメモ兼学習アウトプットです。 基礎的な部分を学び直すと、色々なところで「なるほど〜」とわかった気になって楽しいです。笑 関数もオブジェクトである JavaScriptのデータ型は大きくプリミティブ型とオブジェクト型に分けられる。 参考:https://qiita.com/makotoo2/items/9566cebf205ef8b42505 関数も例外ではなく、オブジェクト型に属する。 そのため、基本的にオブジェクトと同様の機能をもつ。 そして関数は実行可能なオブジェクトである。 サンプルコード まず関数を定義する // 関数funcを定義 function func() { console.log("funcを実行"); } 値を格納できる 通常のオブジェクトと同様に、関数にも値や関数を格納することができる。 // 関数funcに値(prop)と関数(method)を格納する func.prop = 10; func.method = () => { console.log("methodを実行"); }; // funcを実行 func(); // func.propの中身を確認 console.log("func.prop:", func.prop); // func.methodを実行 func.method(); コンソール 関数funcの中身を確認してみる。 // 関数funcに値(prop)と関数(method)を格納する func.prop = 10; func.method = () => { console.log("methodを実行"); }; // func の中身を確認してみる console.log({ func }); コンソール ちゃんと格納した値propと関数methodが入っていることが確認できる。 実行可能なオブジェクトである 関数は()をつけることで処理が実行されます。 func(); func.method(); 最後に いつもありがとうございます! 参考にさせていただきました! https://www.udemy.com/course/javascript-essence/ https://qiita.com/makotoo2/items/9566cebf205ef8b42505 https://qiita.com/yoshi389111/items/245df2d642e49d2acf3a
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptのthisやあれこれ

はじめに 初学者です。 基本は備忘録 関数とthis function fn(a, b) { console.log(a, b); } fn(1) //結果: 1 undefined fn(null, 1)//結果 null 1 //2つめだけ使いたい場nullを使う jsでは同じ関数が2つ以上ある場合後に書いた方が実行される 関数式で宣言するとエラーで教えてくれる function fn(a, b = 2) { //デフォルト値を設定できる console.log(a, b); } fn(1, null)// 1 null fn(1, undefined)// 1 2 //意図的に空にしたい場合はnullにする。undefinedは基本使わない argumentsとは function fn(a, b) { console.log(a, b); } fn(1, 2) argumentsとは実引数のことである 上記のコードだと1と2が該当する 関数とオブジェクト 関数は実行可能なオブジェクトである 実行可能な点以外は全て一緒 コールバック関数 他の関数の引数として渡された関数のこと function hello(name) { console.log('hello' + name) } function fn(cd) { cb('Ren') } fn(hello)//結果: hello Ren //実引数(上記だとhello)がコールバック関数 コールバック関数は無名関数を使って直接記述しても使用可能 function greet() { console.log('Hello') } setTimeout(greet, 2000)//2秒後にHelloと表示 //greetがコールバック関数 なぜコールバック関数が存在するのか? 上記のコードは「○秒後に実行」+「helloと表示」の2つの関数で成り立っている 関数helloを別なものに変更するだけで変更した関数も○秒後に発動できる 処理を切り分けることでコードの再利用性が高まっている thisについて thisは呼び出し元のオブジェクトへの参照を保持する 実行コンテキストによってthisの参照先は変わる const person = { name: 'Ren', hello: function() { console.log('Hello' + this.name) } } person.hello() //結果 Hello Ren //thisは呼び出し元のpersonを参照している 参照のコピーとthis window.name = 'Miku' const person = { name: 'Ren', hello: function() { console.log('Hello' + this.name) } } const ref = person.hello ref(); //Hello Miku //refにはhello()の参照先の関数が渡されるのでオブジェクトを経由しない。  //thisはレキシカルスコープのwindowを参照する 以下のように考えることができる オブジェクトのメソットとして実行される場合 'this' => 呼び出し元のオブジェクト 関数として実行される場合 'this' => グローバルオブジェクト コールバック関数とthis window.name = 'Miku' const preson = { name: 'Ren', hello: function() { console.log('Hello', this.name) } } function fn(ref) { ref(); } fn(person.hello)//Hello Miku //refにはメソットの参照先の関数が直接入る(関数への直接の参照) //オブジェクトは経由しないため、thisにはwindowオブジェクトが格納される //レキシカルスコープはfnだが関数として実行されているためwindowオブジェクトを参照 メソットを他の変数に代入すると、オブジェクト経由で呼び出すのではなく関数を格納している変数から直接呼ぶため、呼び出し元のオブジェクトは基本的にwindowオブジェクトになる bindとthis bindを使うことでthisや引数を固定することができる bindによるthisの束縛と呼ぶ bindで固定する場合元の関数とは別の関数が新しく作成される const preson = { name: 'Ren', hello: function() { console.log('Hello', this.name) } } const helloRen = person.hello.bind(person); //person.helloのthisの参照を固定できる function fn(ref) { ref(); } fn(helloRen)//結果 Hello Ren; --------------------------------------------------------- function a() { console.log('hello' + this.name); } const b = a.bind({name: 'Ren'}); b()//結果 hello Ren 引数を固定 function a(name) { console.log('Hello' + name); } const b = a.bind(null, 'Ren') //第一引数はthis,第二引数は引数を固定する //thisを必要としない場合nullにする b()//結果: Hello Ren applyとcall bindとの違いは定義と同時に実行までされる点 function a(name, name1) { console.log('Hello ' + name + ' ' + name1); } const tim = {name: 'Tim'}; a.apply(tim, ['Ren', 'Miku']);//第一引数はthis,第二引数は(配列)を固定する a.call(tim, 'Ren', 'Miku');//第一引数はthis,第二引数は引数を固定する //applyとcallは定義するのと同時に実行まで行われる アロー関数とは 無名関数を記述しやすくした省略記法 const b = (name) => { return 'hello' + name; } //引数が1つなら()は省略できる //実行行が1行なら{}とreturnは省略できる const b = name => 'hello' + name; console.log(b('Ren'))//結果: hello Ren 無名関数とアロー関数の挙動には違いがある - 無名関数 アロー関数 this ○ x arguments ○ x new ○ x prototype ○ x アロー関数とthis アロー関数はthisを保持しないためレキシカルスコープに探しに行く アロー関数はthiwを保持しないのでbind等で束縛もできない const person = { name: 'Ren'; hello: function() { console.log('Hello' + this.name)//Hello Ren //無名関数はthisを保持するためオブジェクトへの参照を保持 } } window.name = 'Miku'; const person = { name: 'Ren'; hello: () => { console.log('Hello' + this.name)// Hello Miku //アロー関数はthisを保持しないためレキシカルスコープ参照 //このコードだとwindowオブジェクトがレキシカルスコープ } } window.name = 'Miku'; const person = { name: 'Ren'; hello() { console.log('Hello' + this.name)// Hello Ren const a = () => console.log('Bye' + this.name) a();// Hello Ren } } //アロー関数はthisを保持しないためレキシカルスコープを参照 //このコードだと関数helloの関数スコープがレキシカルスコープ //関数helloはメソットとして実行しているのでthisを保持している
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsでディレクトリを再帰的に作成/削除するのにmkdirpやrimrafはもう必要ない

Node.jsでディレクトリを再帰的に作成/削除するためのnpmパッケージとしてmkdirpやrimrafがありますが、現代のNode.js(v14.14.0以降)において、それらはもはや必要ありません。 ディレクトリを再帰的に作成する方法 mkdir -pのようにディレクトリを再帰的に作成するには、fs.mkdirのrecursiveオプションを使います。 コールバックAPIを使う場合: const fs = require('fs') const dir = process.argv[2] console.log(`try creating ${dir} directory`) fs.mkdir(dir, { recursive: true }, (e) => { if (e) { console.error(e) } else { console.log(`created ${dir} directory`) } }) Promise APIを使う場合: const fs = require('fs').promises const dir = process.argv[2] console.log(`try creating ${dir} directory`) fs.mkdir(dir, { recursive: true }) .then(() => console.log(`created ${dir} directory`)) .catch((e) => console.error(e)) なお、fs.mkdirにrecursiveオプションが追加されたのはv10.12.0です。 ディレクトリを再帰的に削除する方法 rm -rfのようにディレクトリを再帰的に削除するには、fs.rmのrecursiveオプションとforceオプションを使います。 コールバックAPIを使う場合: const fs = require('fs') const dir = process.argv[2] console.log(`try deleting ${dir} directory`) fs.rm(dir, { recursive: true, force: true }, (e) => { if (e) { console.error(e) } else { console.log(`deleted ${dir} directory`) } }) Promise APIを使う場合: const fs = require('fs').promises const dir = process.argv[2] console.log(`try deleting ${dir} directory`) fs.rm(dir, { recursive: true, force: true }) .then(() => console.log(`deleted ${dir} directory`)) .catch((e) => console.error(e)) なお、fs.rmが追加されたのはv14.14.0です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Nuxt3(beta)をDocker上で構築した時に詰まったポイント

概要 2021/10/12にNuxt3がリリースされました。 早速触ってみようと思い、今回は最近よくDocker上に開発環境を構築することが多いので、せっかくならNuxtもDocker上に構築しようと思いました。 ただ、初期設定で少し詰まった部分があったので、備忘録もかねてまとめようと思います。 構成 まずは、下記のような構成でファイルを作成します。 nuxt3-docker-app ├ docker-compose.yml ├ node │ └ Dockerfile └ src 各ファイルの準備 Dockerの起動に必要な各ファイルを準備します。 docker-compose.yml 今回はNode.jsのみ使用するので、必要な記述を記載します。 docker-compose.yml version: '3' services: node: build: node volumes: - ./src:/root/src tty: true environment: - CHOKIDAR_USEPOLLING=true ports: - '3000:3000' Dockerfile Node.jsのバージョンとポートを記述します。 Dockerfile FROM node:14.16.1 EXPOSE 3000 Nuxt3をインストールする まずDockerをCompose Upで立ち上げます 次にNode.jsのContainerが起動しているので、Attach Shellします Attach Shellしたら下記コマンドを実行し、作業ディレクトリに移動します cd root/src 下記コマンドを実行します(詳細は公式HPをご参照ください) npx nuxi init 【アプリケーションの名前(今回はappとします)】 下記コマンドでディレクトリを移動します cd app 下記コマンドを実行し、Nuxt3をインストールします npm install インストールが完了したら、下記コマンドを実行し、開発サーバを立ち上げます npm run dev 上手くいけば開発サーバが立ち上がりますが、私は以下のような問題が発生しました。 問題 開発サーバを立ち上げると無限リロードが起こる 開発サーバが立ち上がり、「 http://localhost:3000/ 」にアクセスすると画面は表示されますが、リロードが無限に起こってしまう問題が発生しました。 これでは、まともに開発出来ないので原因を調べたのですが、以下のようなコンソールエラーが発生していました。 WebSocket connection to 'ws://localhost:24678/_nuxt/' failed: 解決方を探してみたのですが、Nuxt3が原因なのか、バンドルに使用しているViteが原因なのかなど原因を解明することは出来ませんでした。 ただ、エラーメッセージに表示されているポートをdocker-compose.ymlに追加することでこの問題が発生しなくなりました。 docker-compose.yml version: '3' services: node: build: node volumes: - ./src:/root/src tty: true environment: - CHOKIDAR_USEPOLLING=true ports: - '3000:3000' - '24678:24678' # 追加 上記のように「- '24678:24678'」を追加後、Docker Restartをして、先ほどと同様に開発サーバを立ち上げると正常に起動していることが確認できました。 成果物 今回作成したものは下記リポジトリにアップしました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

特定ユーザーのQiita記事一覧を得る(5)

前回の記事のアップデートです。 uriのクエリに&tag=[key]を追加することでタグを選択した状態で記事一覧を表示できます。 自分自身の備忘録として記事を書いてきたのに、記事が増えすぎて調べるのに手間取るようになってしまったのですが、これのおかげでとても助かっています。 API使用制限(60回/時間)があるのでその点のみご注意ください。 また、記事が多いと表示まで数秒かかることがあります。 動作デモ 前回同様ローカル環境でも動作します。 <!DOCTYPE html> <html><head><title>Qiita index</title> <style type="text/css"> .cOnButton { color: #111; background-color: #fc8; } .cOffButton { color: #888; } button{ margin: 1px; height: 30px; font-size: 1rem;} </style> <script> var USER_ID='ELIXIR'; var ALL_DISP_TAG = 'All'; const PAR_PAGE=100; // Qiita API の上限 var resArr = []; var qVar=''; var existTagArr = []; var TmGetUrlVarByKeys = function(){ var vars = {}; var param = location.search.substring(1).split('&'); for(var i = 0; i < param.length; i++) { var keySearch = param[i].search(/=/); var key = ''; if(keySearch != -1) key = param[i].slice(0, keySearch); var val = param[i].slice(param[i].indexOf('=', 0) + 1); if(key != '') vars[key] = decodeURI(val); } return vars; } function onSearch(_tag=''){ USER_ID = document.getElementById('iUuid').value; if(USER_ID==''){ return; } resArr = []; document.title=USER_ID; var vars = TmGetUrlVarByKeys(); if(qVar != USER_ID) { window.location.search = '?uuid='+USER_ID+(_tag==''?'':('&tag='+_tag)); }else{ getUser(_tag); } } function onLoad(){ var vars = TmGetUrlVarByKeys(); if(!(typeof vars["uuid"] === "undefined")) { qVar = vars["uuid"]; document.getElementById('iUuid').value = qVar; var qTag = (!(typeof vars["tag"] === "undefined")) ? vars["tag"] : ''; onSearch(qTag); } } function apiCall(_api, _callback, _userData=0){ var req = new XMLHttpRequest(); req.responseType="json"; req._userData=_userData; req.addEventListener("load", _callback); req.open('GET', 'https://qiita.com/api/v2/'+_api, true); req.send(); } function getUser(_tag){ apiCall('users/'+USER_ID,function(){ if(this.response["items_count"]==undefined) document.getElementById('iResult').innerHTML = 'エラーまたはapi使用上限(60回/時間)に達しました。'; else getData(Math.floor((this.response["items_count"]+(PAR_PAGE-1))/PAR_PAGE),_tag); }); } function getData(_page=1,_tag=''){ apiCall('users/'+USER_ID+'/items?per_page='+PAR_PAGE+'&page='+_page,function(){ resArr=resArr.concat(this.response); if(this._userData>1) getData(this._userData-1,_tag); else dispData(resArr,_tag); },_page); } function sortButtons(_sortByCount=false){ var buttonsEle = document.getElementById('iButtons'); var btnArr = Array.from(document.getElementById('iButtons').children); btnArr.sort( function(v0,v1){ if(_sortByCount){ return (v0.existCnt > v1.existCnt) ? -1:1; }else{ return (v0.innerText < v1.innerText) ? -1:1; } } ); buttonsEle.innerHTML=""; for(var i=0;i<btnArr.length;++i){ buttonsEle.appendChild(btnArr[i]); } } function createButton(_name,_tag){ existTagArr.push(_name); var btn = document.createElement('button'); btn.innerHTML=_name; btn.className=(_name==_tag)?'cOnButton':'cOffButton'; btn.onclick=function(){dispData(resArr,_name);} btn.id = 'i_btn'+_name; btn.existCnt=1; return btn; } function createButtons(_resArr,_tag){ var buttonsEle = document.getElementById('iButtons'); existTagArr=[]; buttonsEle.innerHTML = ''; if(_resArr.length>1){ buttonsEle.appendChild(createButton(ALL_DISP_TAG,'')); document.getElementById('i_btn'+ALL_DISP_TAG).existCnt=_resArr.length; } for(var i=0;i<_resArr.length;++i){ var tagArr=_resArr[i]['tags']; for(var j=0;j<tagArr.length;++j){ if(!existTagArr.includes(tagArr[j]['name'])){ buttonsEle.appendChild(createButton(tagArr[j]['name'],_tag)); }else{ document.getElementById('i_btn'+tagArr[j]['name']).existCnt+=1; } } } sortButtons(true); //false if sort by name } function articleContainsTag(_article,_tag){ for(var i=0;i<_article['tags'].length;++i) if(_article['tags'][i]['name']==_tag) return true; return false; } function dispData(_resArr,_tag=''){ createButtons(_resArr,_tag); var articles = []; for(var i=0;i<_resArr.length;++i){ if((_tag=="")||(_tag==ALL_DISP_TAG)||(articleContainsTag(_resArr[i],_tag))){ var tmpStr = '<a href="'+_resArr[i]['url']+'" target="_blank">'+ _resArr[i]['title']+'</a>'; articles.push({'title':tmpStr, 'created_at':_resArr[i]['created_at']}); } } articles.sort(function(a,b){ return ( a.created_at < b.created_at ) ? 1 : -1; }); var str=""; for(var i=0;i<articles.length;++i) str += articles[i]['title']+"<br/>"; str += (articles.length>1) ? '('+articles.length+' articles)<br/>' : ''; document.getElementById('iResult').innerHTML = str; } </script> </head> <body onload="onLoad()"> user_id@<input id="iUuid" type="text" onchange="onSearch()"> <input type="button" value="search" onclick="onSearch()"><div id="iButtons"></div><div id="iResult"></div></body> <br/> </html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む