- 投稿日:2021-07-29T22:35:35+09:00
【今日の学びメモ】JavaScript で Falsy な値を使った判定をする
JavaScript で A && B と書いた場合、 A の値が Falsy だと A の値そのものが返ってくる。 例えば以下のような感じ。 console.log(undefined && true); >> undefined 返り値をbooleanに固定したければ、否定演算子!を二つ重ねて console.log(!!undefined && true); >> false とすれば良い。 undefinedではなく!!undefinedとする意味がわからなかったのでメモ。
- 投稿日:2021-07-29T21:28:30+09:00
自前のローカルサーバ(外部プロセス)で動く React 開発環境 を作りながら、webpack の設定に入門した
Webフロントの開発にデータAPIとしてのモックサーバは必要不可欠ですが、Express やそもそも Node が得意じゃない人は自分の慣れた言語でモックサーバを書きたいこともあると思います。 特に protobuf とか、 dll なんかで提供される資源を利用したいとき、Node からアクセスするのは初心者にはハードルが高い。 ということで今回は自前のローカルサーバを使う React の開発環境を、OSに依存しないよう設定しました。 手作業でしたが想像以上にお手軽に動いたので共有します。 webpack 完全に理解した 達成したこと webpack による JS, CSS ファイルのホットバンドル(ブラウザのリロードではない) yarn start で全部起動するOS非依存設定 (ホットバンドルと自作サーバの立上げ) React 用のloaderの設定。 外部プロセスの(並列)起動設定だけ知りたい方 yarn add yarn-run-all -D npm-run-all --parallel のアライアスの run-p で npm script を並列実行することができます。 package.json ... "scripts": { "start": "run-p start:webpack start:server", "start:webpack": "webpack --watch", "start:server" : "yarn run-server", "run-server" : "./server/run <Your Executable>" } ... ミソは "start:server" に直接 bin を指定するのではなく、"run-server" と名前を付けてから、"yarn run-server" とすることで npm script にすることです。 run-all は npm script でないと処理できないため、普通に実行可能ファイルを登録すると失敗します。 やってみる 1.リポジトリの中でyarn init, git ignore する yarn init -y .gitignore + /node_modules/ + /server/run 自作サーバのディレクトリが有る場合はお好みで ignore します。 2.webpack でバンドルしてみる yarn add webpack webpack-cli --latest -D Node のサーバは使わないのでwebpack-dev-serverは入れません。 -Dフラグで ビルド時に使わないモジュールとして指定できます。 以下で取り合えず動きます。 webpack.config.js module.exports = { mode:"development" }; 確認。 yarn webpack webpackはエントリポイントや出力先のデフォ値を持っているで動きます。 が、記述されていないのはストレスになるのでちゃんと明示します。 webpack.config.js module.exports = { mode: "development", entry: "./src/index.js", output: { filename: "bundle.js", } }; "./src/index.js" をエントリーポイントにバンドルしたファイルを、 "./dist/bundle.js" に出力します。 動くか確認しましょう。 yarn webpack 3.yarn start でホットバンドルの設定 webpack には標準で watch 機能が付いています。起動時に --watch フラグを立てることで、バンドル後そのままソースを監視して変更を検知、自動的にバンドルしてくれます。 これを yarn start コマンドに登録します。 package.json ... "scripts": { "start": "webpack --watch" } ... 確認します。 yarn start 4#.同じように外部プロセスも起動しようとすると、失敗する まずは単純に以下を試してみます。 package.json ... "scripts": { "start": "webpack --watch & ./server/server.exe<Your Executable>" } ... これは webpack がプロセスをブロックしてしまいサーバが起動されません。 & はシェルに並列に見えるようバックグラウンド処理をお願いするのですが、 webpack --watch は明示的に終了させない限り走り続けるので、サーバが起動されません。 サーバ自体をノンブロッキングに書き換えて起動順を入れ替えたり、Unix なら GNU parallel, Windows なら start <program> で何とかなると思うのですが(未確認)、出来ることなら Node の設定のみで動き OS に依存しない方法を取りたいです。 4.yarn-run-all を使う yarn add yarn-run-all -D npm-run-all --parallel のアライアスの run-p で npm script を並列実行することができます。 package.json ... "scripts": { "start": "run-p start:webpack start:server", "start:webpack": "webpack --watch", "start:server" : "yarn run-server", "run-server" : "./server/run <Your Executable>" } ... ミソは "start:server" に直接 bin を指定するのではなく、"run-server" と名前を付けてから、"yarn run-server" とすることで npm script にすることです。 run-all は npm script でないと処理できないため、普通に実行可能ファイルを登録すると失敗します。 凄い単純ですが2時間ぐらいハマってました。 5.React がコンパイルされるようにする 長いです...調べれば出る情報ばかりなので慣れている方は飛ばして下さい。 入門ということで、動作確認しながら設定したいので取り合えずソースを書き換えます。 ./src/index.js import React from 'react'; import ReactDOM from 'react-dom'; const App = ()=>{ return(<h1>Hello 開発環境.</h1>); } ReactDOM.render( <App />, document.getElementById("root") ); モジュールを足していきましょう。 yarn add react react-dom この時点でyarn startしても、当然通りません。webpack が jsx を認識できないからです。 webpack が読み込む前に下処理をするフィルターのような機能、ローダーを使います。 yarn add babel-loader @babel/core -D package.json ... "devDependencies": { ... + "@babel/core" : "^7.14.6", + "babel-loader": "^8.2.2", 最新バージョンで設定していきます。 実は yarn add を行った場合、pakcage に記述されていない依存関係も勝手に解決してくれるので "babel-loader" だけaddしても(裏で "@babel/core" を使って)動くのですが、明示的なのは良い事なので "@babel/core" もちゃんと入れます。 で、webpack がちゃんとこのローダーを使うよう設定します。 ./webpack.config.js ... module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: "babel-loader" } ... Babel に限らずですが Nodeのパッケージは汎用的になるよう設計されていて、どのファイルであれ読み込んで変換させるためには設定が必要です。この設定がまとまった @babel/preset-env を入れ、設定します。 yarn add @babel/preset-env -D ./babelrc { "presets": ["@babel/preset-env"] } 更に react のJSXを変換する設定も必要です。 yarn add @babel/preset-react -D ./babelrc "presets": ["@babel/preset-env", "@babel/preset-react"] ここでバンドルすると... yarn webpack なんと通ります(!!) 「"babel-plugin-transform-react-jsx"は要らないの?」と思った方もいるかもしれませんが、どうやら "@babel/preset-react" の依存先として yarn が裏で解決してくれているみたいですね。良い時代に生まれたものです。 ......「明示的なのは良いことだって言ってたじゃないか」という声が聞こえそうですが、まあこのぐらいなら良いかな。何がこのぐらいなのかは自分でも分かりません。 お好みで追加してください。 6.CSS を別ファイルへ書き出すよう設定する 現状ではwebpack に CSSを処理させようとすると失敗します。 Module parse failed: Unexpected token (1:4) You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders JSX でも見たように、 webpackはjavascript (用のAST? 内部の動作は分かりません) しか認識できないので、CSSファイルを(javascript として)読み取れるようにローダーと、その設定が必要です。 yarn add css-loader -D ./webpack.config.js ... rules: [ ... { test: /\.css$/, use: { loader: "css-loader" } } ここで yarn start して見ると分かるのですが、このままでは CSS が実際の画面に反映されません。理由は、css-loader はあくまで js ファイル上でインポートされたスタイルを認識できるようにするもので、dist.js からブラウザがスタイルとして認識できる形になっていないからです。 css-loader -> style-loader' で JS 上のデータとして認識したものをHTMLへ埋め込んだりも出来ますが、今回はMiniCssExtractPlugin`を使ってスクリプトとは別のファイルへバンドルします。 yarn add mini-css-extract-plugin -D webpack.config.js const MCEP = require('mini-css-extract-plugin'); module.exports = { ... plugins: [new MCEP({filename:"style.css"})], rules = [ ... { test: /\.css$/i, use: [MCEP.loader, 'css-loader'], }, filename:"style.css" にセットして初期化したMCEPのローダー使用して、css-loader -> MCEP.loader の順で webpack に処理させています。 これで、webpack によりソースコードが bundle.js, style.css へとまとめられるはずです。 yarn start では同じ流れで Sass ファイルにも対応しましょう。ローダを末尾に追加することで、 sass-loadr が CSS ファイルへと変換し、その後は同様に css-loader -> MCEP.loader の順で webpack に流れていきます。 yarn add sass sass-loader -D webpack.config.js ... { test: /\.css$/i, use: [MCEP.loader, 'css-loader'] ソースにscssファイルを追加して試してみましょう。 yarn start 7.Jest を React 用に設定する テスト自体を書くわけではないのでおまけのようなものですが、Jest の設定もしておきます。 yarn add jest babel-jest react-test-renderer -D ./webpack.config.js ... "scripts": ... "test": "jest" ... 完成時設定ファイル これで環境は完成です! あとは React と Jest の問題なので勉強しながら作っていきましょう packege.json { "name": "blog-front", "version": "1.0.0", "main": "index.js", "repository": "https://github.com/yourpage/yourrepository", "license": "MIT", "dependencies": { "react": "^17.0.2", "react-dom": "^17.0.2" }, "devDependencies": { "@babel/core": "^7.14.6", "@babel/preset-env": "^7.14.5", "@babel/preset-react": "^7.14.5", "babel-jest": "^27.0.2", "babel-loader": "^8.2.2", "css-loader": "^5.2.6", "jest": "^27.0.4", "mini-css-extract-plugin": "^1.6.0", "react-test-renderer": "^17.0.2", "sass": "^1.35.1", "sass-loader": "^12.1.0", "webpack": "^5.39.0", "webpack-cli": "^4.7.2", "yarn-run-all": "^3.1.1" }, "scripts": { "start": "run-p start:webpack start:server", "start:webpack": "webpack --watch", "start:server": "yarn run-server", "run-server": "./server/run", "test": "jest" } } ./webpack.config.js const MCEP = require('mini-css-extract-plugin'); module.exports = { mode:"development", plugins: [new MCEP({filename:"style.css"})], module: { rules: [ { test: /\.(js|jsx)$/, exclude: /node_modules/, use: { loader: "babel-loader" } }, { test: /\.(sa|sc|c)ss$/, exclude: /node_modules/, use: [MCEP.loader, "css-loader", "sass-loader"] } ] } } ./.babelrc { "presets":["@babel/preset-env", "@babel/preset-react"] }
- 投稿日:2021-07-29T20:02:27+09:00
TypeScriptのコンパイル
概要 ここでは、typeScriptをコンパイルする際のいくつか便利な技術を紹介します。 1. watchモードで保存次に自動的にTSからJSにコンパイルする tsからjsにコンパイルを毎回するのは手間だと思います。 そこでコンパイル次に-wオプションを追加することによって、 watchモードが起動し、自動的に変更内容を反映してくれます $ tsc index.ts -w 2. tsconfig.jsonを作成して、全てのファイルを一気にコンパイルする ファイルが一つの場合、tsc ファイル名でTSからJSにコンパイルができる。 しかし、数多くのファイルがある場合はこの方法では時間がかかる。 そのため、一気に複数ファイルをコンパイルしたい。 初めに $ tsc --init によってtscconfig.jsonファイルを作成できる。 その後は、 $ tsc を実行するだけで、全てのファイルをコンパイルできる。 3. tsconfig.jsonを書き換えて指定したファイルのみコンパイルする tsconfig.jsonを作成すると、初期状態から全てのファイルをコンパイルされる。 そのため、一部除外したいファイルを指定する。 { "include": [ "conpiler.ts" ], "exclude": [ "index.ts", "node_modules", ], "files": [ "index.ts" ] } "include"に指定したファイルはコンパイルされる "exclude"に指定したファイルはコンパイルされない "files"と"include"の違いは、filesは絶対パスで指定しなければならない点 4. tsconfig.jsonのcompilerOptions "target": "es5",: 変換するESのバージョンを指定できる "outDir": "./dist": distディレクトリに生成されるjsファイルをまとめてくれる "noEmitOnError": エラーがあった場合にファイルを生成しなくなる strict strictをtrueにすると noImplicitAny noImplicitThis alwaysStrict strictBindCallApply strictNullChecks strictFunctionTypes strictPropertyInitialization これら全てがtrueになる noImplicitAny: 暗黙的なanyをエラーとする => しっかりとした型定義が必要 strictNullChecks: 全ての型でnull undefinedを禁止(一部例外はあり)
- 投稿日:2021-07-29T17:07:38+09:00
ウェブサイト作成用備忘録・15号:three.jsの学習に挑戦【コピペでプレビュー】
今回はwebGLの学習に挑戦してみました! …が、まだまだ継続中の為、今回は学習の過程をまとめてみようと思います。 1・webGLの直書きはとても難しい… → ライブラリを探してみる! 最初はブラウザ上での3Dグラフィックの表現に挑戦してみたくてwebGLについて検索する所から始めてみました。 しかし、実際に色々調べてみるとwebGLの直書きはとても難しく、基本的には色々なライブラリを併用して扱う技術なのだと分かりました。 2・three.jsを発見 → 学習の為のWEBサイトを漁る オススメのライブラリを探す過程で、three.jsというライブラリが人気だという事が分かりました。 人気のライブラリは日本語で解説された参考サイトが多く、英語の公式ドキュメントで調べても分からない事を日本語で検索して解決できる所が強みだと思います。 参考サイト集 ↓ 0から勉強し始めて、ある程度の基本を覚えるまでは下記のサイトの内容が分かりやすく、とても参考になりました。 ↓ こちらのサイトは英語版の記事を日本語に翻訳したものなので多少読みにくさを感じる時もありますが、 ↑ のサイトである程度three.jsに慣れてから読み始めると、より細かい設定をやライブラリの仕組みを日本語でより深く学習する事が出来るサイトです。 ↓ こちらの方のブログでは、より具体的なテクニックやトラブルの解決方法などが掲載されており、先述したサイトである程度学習を進め、自主課題作成の時に分からないことや躓いている事を調べたい時に参考になりました。 ↓ こちらが公式ドキュメントです。日本語で書かれたページもありますが、基本英語のドキュメントなので、英語に疎い自分の場合、日本語の参考サイトで自分なりに色々調べながら学習を進めていき、three.jsに慣れてきた頃にやっと翻訳しながら分からない事を調べたり、やりたい事を実現する為の方法をサンプル一覧を見ながら調べていく事が出来るようになりました。 そして… three.jsでアニメーションを作成出来るようになると、「じゃあ、webGLで具体的に何を作ってみよう?」という段階になり、色々試していたのですが、ここで問題が発生しました。 three.jsで作成した3Dオブジェクトをアニメーションさせる時に、基本的にはrequestAnimationFrameメソッドを使用して1フレーム毎に処理を加え続ける事でアニメーションされるのですが… 現時点の自分では永続的に回転し続ける様なループアニメーションなら何とか実装できるのですが、あるボタンを押したら処理1→2→3の様に順番に別々のアニメーションを行ったりするような、複雑な動作をthree.jsだけでは実現できませんでした。 ※その気になれば時間をかけて実現する事も出来るとは思うのですが、とにかくもっと楽して解決したかったのです! three.js単品だと難しい場面が出てくる → 更に別のライブラリ、TweeenMaxを導入 今の所、TweeenMax自体の機能についてはまだまだ分からない事が多いのですが… 分からないなりに必要な部分だけ調べて使っている状態でも苦戦していたアニメーション部分を楽に解決する事が出来たので、使えるライブラリやプラグインはバンバン使って試していくのが良いと思います! 最後にコピペでプレビュー枠ですが、実用性というよりも、何とか動かすので精一杯な作品が出来上がりました… 良くも悪くも誰かの参考になれば幸いなので、記念に載せておきます。 <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1" /> <!-- jquery --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script> <!-- TweenMax --> <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/1.20.3/TweenMax.min.js"></script> <!-- three.js --> <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r127/three.min.js"></script> <script src="https://cdn.jsdelivr.net/gh/mrdoob/three.js@r109/examples/js/controls/OrbitControls.js"></script> </head> <body style="margin: 0;"> <div id="myCanvas"></div> </body> <script> jQuery(document).ready(function () { // ページの読み込みを待つ $(window).on("load", function () { // レンダラーを作成 const canvas = document.getElementById("myCanvas"); const renderer = new THREE.WebGLRenderer({ alpha: true, }); $("#myCanvas").append(renderer.domElement); // カメラを作成 const camera = new THREE.PerspectiveCamera(); // カメラコントローラーを作成 const controls = new THREE.OrbitControls(camera, canvas); function onResize() { // レンダラーのサイズを設定する(高画質対応) renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio); // カメラを設定する camera.fov = 50; camera.aspect = window.innerWidth / window.innerHeight; camera.position.set(0, 3, 1.5); // 原点方向を見つめる camera.lookAt(new THREE.Vector3(0, 0, 0)); // 滑らかにカメラコントローラーを制御する controls.enableDamping = true; controls.dampingFactor = 0.2; camera.updateProjectionMatrix(); } // レンダラー・カメラの初期設定 onResize(); // リサイズ時にレンダラー・カメラの再設定 $(window).resize(function () { onResize(); }); // シーンを作成 const scene = new THREE.Scene(); // シーン→グループ const group = new THREE.Group(); scene.add(group); // グループ→正四面体 const Tetrahedron_size = 1; const Tetrahedron = new THREE.TetrahedronGeometry(Tetrahedron_size); const Tetrahedron_m = new THREE.MeshNormalMaterial({ side: THREE.DoubleSide, transparent: true, blending: THREE.NormalBlending, }); Tetrahedron_m.opacity = .5; const mesh_Tetrahedron = new THREE.Mesh(Tetrahedron, Tetrahedron_m); mesh_Tetrahedron.position.set(0, Tetrahedron_size / 3, 0); const Tetrahedron_y = (Math.PI * 2) / 8; const Tetrahedron_x = 2.186275; const Tetrahedron_A = 1; mesh_Tetrahedron.rotation.set(Tetrahedron_x * Tetrahedron_A, Tetrahedron_y, 0); group.add(mesh_Tetrahedron); // グループ→正四面体外縁線 //ジオメトリーの作成 const points = []; points.push(new THREE.Vector3(0, Tetrahedron_size * 1.5, 0)); //1 points.push(new THREE.Vector3(Tetrahedron_size * .95, -.1, .55)); //2 points.push(new THREE.Vector3(Tetrahedron_size * -.95, -.1, .55)); //3 points.push(new THREE.Vector3(0, -.1, -1.1)); //3.5 points.push(new THREE.Vector3(Tetrahedron_size * .95, -.1, .55)); //4 points.push(new THREE.Vector3(Tetrahedron_size * -.95, -.1, .55)); //5 points.push(new THREE.Vector3(0, Tetrahedron_size * 1.5, 0)); //6 points.push(new THREE.Vector3(Tetrahedron_size * .95, -.1, .55)); //7 points.push(new THREE.Vector3(Tetrahedron_size * -.95, -.1, .55)); //8 points.push(new THREE.Vector3(0, -.1, -1.1)); //9 points.push(new THREE.Vector3(0, Tetrahedron_size * 1.5, 0)); //10 const line = new THREE.BufferGeometry().setFromPoints(points); const line_m = new THREE.LineBasicMaterial({ color: 0xff0000, linewidth: 1, side: THREE.DoubleSide, }) const mesh_line = new THREE.Line(line, line_m); group.add(mesh_line); // グループ→正八面体 const Octahedron = new THREE.OctahedronGeometry(.1); const Octahedron_m = new THREE.MeshNormalMaterial({ side: THREE.DoubleSide, }); const mesh_sprite = new THREE.Mesh(Octahedron, Octahedron_m); mesh_sprite.position.set(0, Tetrahedron_size * 1.5, 0); group.add(mesh_sprite); // 正八面体を外縁線沿いにアニメーション function path1_2() { TweenMax.to(mesh_sprite.position, 1.5, { x: Tetrahedron_size * .95, y: -.1, z: .55, onComplete: path2_3, }); } path1_2(); function path2_3() { TweenMax.to(mesh_sprite.position, 1.5, { x: Tetrahedron_size * -.95, y: -.1, z: .55, onComplete: path3_11, }); } function path3_11() { TweenMax.to(mesh_sprite.position, 1.5, { x: 0, y: -.1, z: -1.1, onComplete: path11_4, }); } function path11_4() { TweenMax.to(mesh_sprite.position, 1.5, { x: Tetrahedron_size * .95, y: -.1, z: .55, onComplete: path4_5, }); } function path4_5() { TweenMax.to(mesh_sprite.position, 1.5, { x: Tetrahedron_size * -.95, y: -.1, z: .55, onComplete: path5_6, }); } function path5_6() { TweenMax.to(mesh_sprite.position, 1.5, { x: 0, y: Tetrahedron_size * 1.5, z: 0, onComplete: path6_7, }); } function path6_7() { TweenMax.to(mesh_sprite.position, 1.5, { x: Tetrahedron_size * .95, y: -.1, z: .55, onComplete: path7_8, }); } function path7_8() { TweenMax.to(mesh_sprite.position, 1.5, { x: Tetrahedron_size * -.95, y: -.1, z: .55, onComplete: path8_9, }); } function path8_9() { TweenMax.to(mesh_sprite.position, 1.5, { x: 0, y: -.1, z: -1.1, onComplete: path9_1, }); } function path9_1() { TweenMax.to(mesh_sprite.position, 1.5, { x: 0, y: Tetrahedron_size * 1.5, z: 0, onComplete: path1_2, }); } // シーン→トップライト const directionalLight = new THREE.AmbientLight(0xFFFFFF, 1); directionalLight.position.set(0, 1, -1); scene.add(directionalLight); // ループアニメーション設定 function tick() { requestAnimationFrame(tick); mesh_sprite.rotation.y += 0.25; // カメラコントローラーを更新 controls.update(); // レンダリングを更新 renderer.render(scene, camera); } tick(); }); }); </script> </html>
- 投稿日:2021-07-29T16:51:22+09:00
JavaScriptでコメントアウトする方法
○概要 JavaScriptでコメントアウトする方法を解説します。 ○背景 普段練習していたのがJavaなのですが、初めての業務で突然JavaScriptメインで使うことに。 コメントアウトの方法など細かいことは忘れてしまうタイプなのでメモ。 ○やり方 こんな感じ。 /** * */
- 投稿日:2021-07-29T16:35:07+09:00
[Rails→Vue]一対多の関係にある親子オブジェクトをネストさせた状態で取得する
[Vue→Rails]ネストされた状態の親子孫オブジェクト(一対多の関係あり)を全て同時にデータベースに保存するでは、Vue側で作ったネストされたデータを複数のテーブルに保存した。 この記事では、別々のテーブルに保存されたそれらのデータを再びネストされた形でVueコンポーネント側で取得する。 前提条件 モデル 親:School 子:Student school.rb # 親 class School has_many :students student.rb # 子 class Student belongs_to :school 保存されているレコード School { id: 1, school_name: 'first' } { id: 2, school_name: 'second'} Student { id: 1, student_name: 'Suzuki', age: 13, school_id: 1 } { id: 2, student_name: 'Hirano', age: 14, school_id: 1 } { id: 3, student_name: 'Nagai', age: 15, school_id: 1 } { id: 4, student_name: 'Sato', age: 13, school_id: 2 } { id: 5, student_name: 'Iguchi', age: 14, school_id: 2 } { id: 6, student_name: 'Arai', age: 15, school_id: 2 } axios.getによるデータの取得 $axiosはインスタンスプロパティに追加済み schools#indexにgetリクエストを送りデータを取得する <script> ... this.$axios.get('schools') .then(res => { console.log(res) }) .catch(err => { console.log(err) }) ... </script> コントローラ to_jsonメソッドの使用 includeオプションでstudentsを指定する。school.studentsで取得されるstudentの配列をschoolにネストさせることができる。 schools_controller.rb def index schools = School.all string = schools.map.to_json(include: :students) render json: string end 取得できるデータ to_jsonのincludeオプションで指定した'students'がそのままキー名になる。 data: [ { schoolName: 'first', students: [ { studentName: 'Suzuki', age: 13 }, { studentName: 'Hirano', age: 14 }, { studentName: 'Nagai', age: 15 } ] }, { schoolName: 'second', students: [ { studentName: 'Sato', age: 13 }, { studentName: 'Iguchi', age: 14 }, { studentName: 'Arai', age: 15 } ] } ] 【追記】 キー名を変えたいとき students以外のキー名にしたいとき(例:studentDataなど) モデル アソシエーションを取得するメソッドを親側に新しく定義し、目的のキー名をメソッド名にする。 school.rb class School < ApplicationRecord has_many :students def studentData # 目的のキー名 self.students end end コントローラ to_jsonメソッドのmethodsオプションでそのメソッド名を指定する。 メソッド名がそのままキー名になる。 schools_controller.rb def index schools = School.all string = schools.map.to_json(methods: :studentData) render json: string end 取得できるデータ data: [ { schoolName: 'first', studentData: [ { studentName: 'Suzuki', age: 13 }, { studentName: 'Hirano', age: 14 }, { studentName: 'Nagai', age: 15 } ] }, { schoolName: 'second', studentData: [ { studentName: 'Sato', age: 13 }, { studentName: 'Iguchi', age: 14 }, { studentName: 'Arai', age: 15 } ] } ] 参考文献
- 投稿日:2021-07-29T15:00:11+09:00
全角チルダ?波ダッシュ?に殺された記録
事件はAndroidの文字入力で起きました. PCとスマホで入力文字の挙動が違う 依頼を受けてブラウザ上で縦書き画像を簡単に作れるサービスを開発していました. textareaから文字の入力を受けて,それをcanvas上にjavascriptで縦書き文字として描画するっていうシンプルなロジックのものでした. Reactをベースにして,特にこれと言ったライブラリは一切使っていませんでした. 事件はそのとき起きました 依頼者の方から, 「~が横のままになってるのでそこだけ修正お願いします~!」 と連絡がきました. これはまずいと思い,急いでPCから修正を行おうと思いましたが・・・ まったく縦のままでした・・・. いやいや依頼者の方,なんの文字を入力したんだと思いその環境を聞くと 「スマホのChromeです~!」 とのこと.ふむふむなるほどと思い私もスマホのchromeで確認すると あああああああああああああああああああああああああああああああああああああああああ(断末魔) はい,PCとスマホで挙動が違うじゃありませんか. 検証 原因はなんとなく推測がつきます. 文字コード これしかないやろと. ということでtextareaで受け取った文字を比較しました. encodeURIComponent(input) 結果 PC スマホ やった!原因合ってた!!!!!!!! あああああああああああああああああああああああああああああああああああああああ(断末魔) 修正 文字をそのまま比較していたところを文字情報で比較することにしました // 修正前 if(ch === "~"){ // } //修正後 if(["%EF%BD%9E","%E3%80%9C"].includes(encodeURIComponent(ch)){ // } 結果 スマホ GG
- 投稿日:2021-07-29T14:08:39+09:00
[Vue→Rails]一対多の関係にある親子孫のデータがネストされたオブジェクトを全て同時にデータベースに保存する
前提条件 モデル 親:City 子:School 孫:Student 一つの市に複数の学校があり、それぞれの学校に複数の生徒がいるという想定 city.rb # 親 class City has_many :school school.rb # 子 class School has_many :students belongs_to :city student.rb # 孫 class Student belongs_to :school Vue(フロントエンド側)で生成されるオブジェクト // the city has 2 schools // each school has 3 students { city: { cityName: 'tokyo', schools: [ { schoolName: 'first', students: [ { studentName: 'Suzuki', age: 13 }, { studentName: 'Hirano', age: 14 }, { studentName: 'Nagai', age: 15 } ] }, { schoolName: 'second', students: [ { studentName: 'Sato', age: 13 }, { studentName: 'Iguchi', age: 14 }, { studentName: 'Arai', age: 15 } ] }, ] } } axiosのpostメソッドでバックエンドに送る。 コントローラ cities,schools,studentsコントローラで個別にレコードを保存するのではなく、citiesコントローラで一括で保存を行う。 ストロングパラメータの記述 cityの一つの属性としてschoolsを指定する。配列データであるschoolsは[]で表し、中にschoolsのパラメータを記述する。 さらに、schoolsの一つのパラメータとしてstudentsを指定する。同様に配列データであるstudentsを[]で表し、中にstudentsのパラメータを記述する。 createアクションの記述 CityObjectインスタンス(次章で解説)をビルドし、パラメータを渡す(2行目)。 CityObjectのインスタンスメソッド'save'(次章で解説:ActiveRecordのsaveメソッドではない)内で親子孫それぞれのモデルにおいてレコードの保存を行う(4行目)。 cities_controller.rb def create city = CityObject.new(city_params) if city.save render json: city else render json: city.errors.full_messages, status: :unprocessable_entity end end private def city_params params.require(:city).permit(:cityName, schools: [:schoolName, students: [:studentName, :age]]) end 子孫オブジェクトを包含したインスタンスの利用 City,School,Studentのパラメータを持つ非ActiveRecordクラス'CityObject'を定義する。 コントローラでCityObjectのインスタンスにパラメータが渡されているので、インスタンスメソッド'save'内でそのパラメータにアクセスし、City,School,Studentそれぞれのインスタンスに渡して保存を行う。 ActiveModelモジュールの利用 CityObjectクラスを定義し、ActiveModel::ModelをインクルードすることでActiveRecordモデルと同様の処理を可能にする(1,2行目)。 それとともにActiveModel::Attributesをインクルードし、attributeメソッドによりCityObjectに渡されたパラメータを参照できるようにする(3-5行目)。親オブジェクトの各属性を指定する。 CityObjectのインスタンスメソッド'save'の定義 CityObjectの属性値'cityName'にアクセスし、新しいCityインスタンスの属性値'city_name'としてセットし、それを保存する(10-11行目)。 schools(selfは省略可)でschoolオブジェクトの配列を取得し、一つ一つのオブジェクトについて以下のループ処理を行う(14行目)。 schoolオブジェクトの'schoolName'の値をSchoolインスタンスの属性値'school_name'としてセットし、先程保存したCityインスタンスの子オブジェクトとして保存する(15行目)。 sc[:students]でschoolオブジェクトが持つstudentオブジェクトの配列を取得し、一つ一つのオブジェクトについて以下のループ処理を行う(16行目)。 studentオブジェクトの'studentName','age'の値をStudentインスタンスの属性値'student_name','age'として渡し、先程保存したSchoolインスタンスの子として保存する(18行目)。 app/models/city_object.rb class CityObject include ActiveModel::Model include ActiveModel::Attributes attribute :cityName attribute :schools def save # Cityインスタンスの保存 city = City.new(city_name: self.cityName) city.save # Schoolインスタンスの保存 self.schools.each do |sc| school = city.schools.create(school_name: sc[:schoolName]) sc[:students].each do |st| # Studentインスタンスの保存 school.students.create(student_name: st[:studentName], age: st[:age]) end end end end 保存されるレコード City { id: 1, city_name: 'tokyo' } School { id: 1, school_name: 'first', city_id: 1 } { id: 2, school_name: 'second', city_id: 1 } Student { id: 1, student_name: 'Suzuki', age: 13, school_id: 1 } { id: 2, student_name: 'Hirano', age: 14, school_id: 1 } { id: 3, student_name: 'Nagai', age: 15, school_id: 1 } { id: 4, student_name: 'Sato', age: 13, school_id: 2 } { id: 5, student_name: 'Iguchi', age: 14, school_id: 2 } { id: 6, student_name: 'Arai', age: 15, school_id: 2 }
- 投稿日:2021-07-29T14:08:39+09:00
[Vue→Rails]ネストされた状態の親子孫オブジェクト(一対多の関係あり)を全て同時にデータベースに保存する
前提条件 モデル 親:City 子:School 孫:Student 一つの市に複数の学校があり、それぞれの学校に複数の生徒がいるという想定 city.rb # 親 class City has_many :school school.rb # 子 class School has_many :students belongs_to :city student.rb # 孫 class Student belongs_to :school Vue(フロントエンド側)で生成されるオブジェクト // the city has 2 schools // each school has 3 students { city: { cityName: 'tokyo', schools: [ { schoolName: 'first', students: [ { studentName: 'Suzuki', age: 13 }, { studentName: 'Hirano', age: 14 }, { studentName: 'Nagai', age: 15 } ] }, { schoolName: 'second', students: [ { studentName: 'Sato', age: 13 }, { studentName: 'Iguchi', age: 14 }, { studentName: 'Arai', age: 15 } ] }, ] } } axiosのpostメソッドでバックエンドに送る。 コントローラ cities,schools,studentsコントローラで個別にレコードを保存するのではなく、citiesコントローラで一括で保存を行う。 ストロングパラメータの記述 cityの一つの属性としてschoolsを指定する。配列データであるschoolsは[]で表し、中にschoolsのパラメータを記述する。 さらに、schoolsの一つのパラメータとしてstudentsを指定する。同様に配列データであるstudentsを[]で表し、中にstudentsのパラメータを記述する。 createアクションの記述 CityObjectインスタンス(次章で解説)をビルドし、パラメータを渡す(2行目)。 CityObjectのインスタンスメソッド'save'(次章で解説:ActiveRecordのsaveメソッドではない)内で親子孫それぞれのモデルにおいてレコードの保存を行う(4行目)。 cities_controller.rb def create city = CityObject.new(city_params) if city.save render json: city else render json: city.errors.full_messages, status: :unprocessable_entity end end private def city_params params.require(:city).permit(:cityName, schools: [:schoolName, students: [:studentName, :age]]) end 子孫オブジェクトを包含したインスタンスの利用 City,School,Studentのパラメータを持つ非ActiveRecordクラス'CityObject'を定義する。 コントローラでCityObjectのインスタンスにパラメータが渡されているので、インスタンスメソッド'save'内でそのパラメータにアクセスし、City,School,Studentそれぞれのインスタンスに渡して保存を行う。 ActiveModelモジュールの利用 CityObjectクラスを定義し、ActiveModel::ModelをインクルードすることでActiveRecordモデルと同様の処理を可能にする(1,2行目)。 それとともにActiveModel::Attributesをインクルードし、attributeメソッドによりCityObjectに渡されたパラメータを参照できるようにする(3-5行目)。親オブジェクトの各属性を指定する。 CityObjectのインスタンスメソッド'save'の定義 CityObjectの属性値'cityName'にアクセスし、新しいCityインスタンスの属性値'city_name'としてセットし、それを保存する(10-11行目)。 schools(selfは省略可)でschoolオブジェクトの配列を取得し、一つ一つのオブジェクトについて以下のループ処理を行う(14行目)。 schoolオブジェクトの'schoolName'の値をSchoolインスタンスの属性値'school_name'としてセットし、先程保存したCityインスタンスの子オブジェクトとして保存する(15行目)。 sc[:students]でschoolオブジェクトが持つstudentオブジェクトの配列を取得し、一つ一つのオブジェクトについて以下のループ処理を行う(16行目)。 studentオブジェクトの'studentName','age'の値をStudentインスタンスの属性値'student_name','age'として渡し、先程保存したSchoolインスタンスの子として保存する(18行目)。 app/models/city_object.rb class CityObject include ActiveModel::Model include ActiveModel::Attributes attribute :cityName attribute :schools def save # Cityインスタンスの保存 city = City.new(city_name: self.cityName) city.save # Schoolインスタンスの保存 self.schools.each do |sc| school = city.schools.create(school_name: sc[:schoolName]) sc[:students].each do |st| # Studentインスタンスの保存 school.students.create(student_name: st[:studentName], age: st[:age]) end end end end 保存されるレコード City { id: 1, city_name: 'tokyo' } School { id: 1, school_name: 'first', city_id: 1 } { id: 2, school_name: 'second', city_id: 1 } Student { id: 1, student_name: 'Suzuki', age: 13, school_id: 1 } { id: 2, student_name: 'Hirano', age: 14, school_id: 1 } { id: 3, student_name: 'Nagai', age: 15, school_id: 1 } { id: 4, student_name: 'Sato', age: 13, school_id: 2 } { id: 5, student_name: 'Iguchi', age: 14, school_id: 2 } { id: 6, student_name: 'Arai', age: 15, school_id: 2 }
- 投稿日:2021-07-29T14:08:39+09:00
[Vue→Rails]一対多の関係にある親、子、孫オブジェクトを全て同時にデータベースに保存する
前提条件 モデル 親:City 子:School 孫:Student 一つの市に複数の学校があり、それぞれの学校に複数の生徒がいるという想定 city.rb # 親 class City has_many :school school.rb # 子 class School has_many :students belongs_to :city student.rb # 孫 class Student belongs_to :school Vue(フロントエンド側)で生成されるオブジェクト // the city has 2 schools // each school has 3 students { city: { cityName: 'tokyo', schools: [ { schoolName: 'first', students: [ { studentName: 'Suzuki', age: 13 }, { studentName: 'Hirano', age: 14 }, { studentName: 'Nagai', age: 15 } ] }, { schoolName: 'second', students: [ { studentName: 'Sato', age: 13 }, { studentName: 'Iguchi', age: 14 }, { studentName: 'Arai', age: 15 } ] }, ] } } axiosのpostメソッドでバックエンドに送る。 コントローラ cities,schools,studentsコントローラで個別にレコードを保存するのではなく、citiesコントローラで一括で保存を行う。 ストロングパラメータの記述 cityの一つの属性としてschoolsを指定する。配列データであるschoolsは[]で表し、中にschoolsのパラメータを記述する。 さらに、schoolsの一つのパラメータとしてstudentsを指定する。同様に配列データであるstudentsを[]で表し、中にstudentsのパラメータを記述する。 createアクションの記述 CityObjectインスタンス(次章で解説)をビルドし、パラメータを渡す(2行目)。 CityObjectのインスタンスメソッド'save'(次章で解説:ActiveRecordのsaveメソッドではない)内で親子孫それぞれのモデルにおいてレコードの保存を行う(4行目)。 cities_controller.rb def create city = CityObject.new(city_params) if city.save render json: city else render json: city.errors.full_messages, status: :unprocessable_entity end end private def city_params params.require(:city).permit(:cityName, schools: [:schoolName, students: [:studentName, :age]]) end 子孫オブジェクトを包含したインスタンスの利用 City,School,Studentのパラメータを持つ非ActiveRecordクラス'CityObject'を定義する。 コントローラでCityObjectのインスタンスにパラメータが渡されているので、インスタンスメソッド'save'内でそのパラメータにアクセスし、City,School,Studentそれぞれのインスタンスに渡して保存を行う。 ActiveModelモジュールの利用 CityObjectクラスを定義し、ActiveModel::ModelをインクルードすることでActiveRecordモデルと同様の処理を可能にする(1,2行目)。 それとともにActiveModel::Attributesをインクルードし、attributeメソッドによりCityObjectに渡されたパラメータを参照できるようにする(3-5行目)。親オブジェクトの各属性を指定する。 CityObjectのインスタンスメソッド'save'の定義 CityObjectの属性値'cityName'にアクセスし、新しいCityインスタンスの属性値'city_name'としてセットし、それを保存する(10-11行目)。 schools(selfは省略可)でschoolオブジェクトの配列を取得し、一つ一つのオブジェクトについて以下のループ処理を行う(14行目)。 schoolオブジェクトの'schoolName'の値をSchoolインスタンスの属性値'school_name'としてセットし、先程保存したCityインスタンスの子オブジェクトとして保存する(15行目)。 sc[:students]でschoolオブジェクトが持つstudentオブジェクトの配列を取得し、一つ一つのオブジェクトについて以下のループ処理を行う(16行目)。 studentオブジェクトの'studentName','age'の値をStudentインスタンスの属性値'student_name','age'として渡し、先程保存したSchoolインスタンスの子として保存する(18行目)。 app/models/city_object.rb class CityObject include ActiveModel::Model include ActiveModel::Attributes attribute :cityName attribute :schools def save # Cityインスタンスの保存 city = City.new(city_name: self.cityName) city.save # Schoolインスタンスの保存 self.schools.each do |sc| school = city.schools.create(school_name: sc[:schoolName]) sc[:students].each do |st| # Studentインスタンスの保存 school.students.create(student_name: st[:studentName], age: st[:age]) end end end end 保存されるレコード City { id: 1, city_name: 'tokyo' } School { id: 1, school_name: 'first', city_id: 1 } { id: 2, school_name: 'second', city_id: 1 } Student { id: 1, student_name: 'Suzuki', age: 13, school_id: 1 } { id: 2, student_name: 'Hirano', age: 14, school_id: 1 } { id: 3, student_name: 'Nagai', age: 15, school_id: 1 } { id: 4, student_name: 'Sato', age: 13, school_id: 2 } { id: 5, student_name: 'Iguchi', age: 14, school_id: 2 } { id: 6, student_name: 'Arai', age: 15, school_id: 2 }
- 投稿日:2021-07-29T10:46:51+09:00
JavaScriptの学習方法を基本からまとめてみた
はじめに 学習するに至った経緯 2020年より、未経験からエンジニアへの転職を目指し、某プログラミングスクールへ通う。 入学後、『Ruby』を未経験から学ぶ人が多いのと『Ruby』の求人が思っていた以上に少ないので、 卒業後、フロントエンドのエンジニアを目指す事に。 当初は、プロゲート,ドットインストール,Youtube,Udemyを使い学習をしていたが、 Todolistなどのアプリケーションをテキスト通りに作成できても、カスタマイズなど実装できない事に悩む。その時に、youtubeを見て、アウトプット中心の学習で少しでも経験を積んで、今の状況を改善できればと思い、Javascriptの学習した事を言語化し、認識の深化による備忘録として記載。 『言葉で考える』とは 文章や箇条書きでプログラミングの処理を書き出してから、実際にコードを書いていくこと。 ⇨コードを書く前に、簡単に設計をしてからコードを書くということ。 『言葉で考える学習』のメリットとは 何が理解出来ていないのか明確にできる。⇨ 分析能力が鍛えられる。 プログラミング学習でやってはダメなこと 『Googleで答えを探すこと』⇨ プログラミングが上達しない。 仕事などで作ることが目的の場合は良いが、学習中はコードをコピーするだけでは、スキルが身につかない。 プログラミング思考の流れを学ぶ(分析) プログラミングはパズルみたいなもの。何かWEBサービスやアプリを作ろうと思った時、WEBサービスやアプリを作るためにいろんな機能が必要で、一つの機能をつくるためにいろんな処理が必要。一つの機能や処理を作るために、小さなコードの集まりを組み合わせて作成していく。 1. 何かアプリを作成しようと思ったらまず言葉に書き換える ⇨ コードを書く前に、何を知らないといけないのかを明確にする。 2. 書き換えた言葉を検索する 3. コードのサンプルを拾ってきて、組み合わせる 言葉で考える項目は3つ HTML要素(見た目) トリガー(何をしたら?) イベント(何が起きる?) ① アプリを作成する前に書き出す (例) <HTML要素(見た目)> ページ右上にメニューボタンがある 画面右にメニューリストが隠れている <トリガー(何をしたら?)> メニューボタンを押したら <イベント(何が起きる?)> メニューが右から出てくる CSSアニメーションをJSで動かすので、JSではclassをつける必要がある ② 具体的に書き出し、思考の流れを整理する (例) HTML要素のheaderにclassをjsでつける もう一回クリックしたら、classが外れてメニューから引っ込む ③ 要するに何をしたいかを書き出す (例) 1つのボタンのクリックで、headerに対してclassをつけ外しをする ④ 検索する (例) 『1つのボタンのクリック』『headerに対してclassをつけ外しをする』 ⑤ 検索して、サンプルコードを拾ってきて組み合わせる 初心者で言葉で考えられない場合は チュートリアルやコードを読み解く数が不足。 数をこなして自分がプログラムを組む時に選択肢となるピースを1つでも多く知る必要がある。 参考サイト <プログラミング難しい人向け>言葉で考えるJavaScriptの学習方法【プログラミング脳を鍛える】
- 投稿日:2021-07-29T10:46:51+09:00
JavaScriptでアプリを作成しました【4】【超基礎】【随時更新】
はじめに 学習するに至った経緯 2020年より、未経験からエンジニアへの転職を目指し、某プログラミングスクールへ通う。 入学後、『Ruby』を未経験から学ぶ人が多いのと『Ruby』の求人が思っていた以上に少ないので、 卒業後、フロントエンドのエンジニアを目指す事に。 Javascriptの学習した事を言語化し、認識の深化による備忘録として記載。 参考サイト <プログラミング難しい人向け>言葉で考えるJavaScriptの学習方法【プログラミング脳を鍛える】
- 投稿日:2021-07-29T10:46:51+09:00
JavaScriptの学習方法を基本からまとめてみた【随時更新】
はじめに 学習するに至った経緯 2020年より、未経験からエンジニアへの転職を目指し、某プログラミングスクールへ通う。 入学後、『Ruby』を未経験から学ぶ人が多いのと『Ruby』の求人が思っていた以上に少ないので、 卒業後、フロントエンドのエンジニアを目指す事に。 当初は、プロゲート,ドットインストール,Youtube,Udemyを使い学習をしていたが、 Todolistなどのアプリケーションをテキスト通りに作成できても、カスタマイズなど実装できない事に悩む。その時に、youtubeを見て、アウトプット中心の学習で少しでも経験を積んで、今の状況を改善できればと思い、Javascriptの学習した事を言語化し、認識の深化による備忘録として記載。 『言葉で考える』とは 文章や箇条書きでプログラミングの処理を書き出してから、実際にコードを書いていくこと。 ⇨コードを書く前に、簡単に設計をしてからコードを書くということ。 『言葉で考える学習』のメリットとは 何が理解出来ていないのか明確にできる。⇨ 分析能力が鍛えられる。 プログラミング学習でやってはダメなこと 『Googleで答えを探すこと』⇨ プログラミングが上達しない。 仕事などで作ることが目的の場合は良いが、学習中はコードをコピーするだけでは、スキルが身につかない。 プログラミング思考の流れを学ぶ(分析) プログラミングはパズルみたいなもの。何かWEBサービスやアプリを作ろうと思った時、WEBサービスやアプリを作るためにいろんな機能が必要で、一つの機能をつくるためにいろんな処理が必要。一つの機能や処理を作るために、小さなコードの集まりを組み合わせて作成していく。 1. 何かアプリを作成しようと思ったらまず言葉に書き換える 2. 書き換えた言葉を検索する 3. コードのサンプルを拾ってきて、組み合わせる 言葉で考える項目は3つ HTML要素(見た目) トリガー(何をしたら?) イベント(何が起きる?) ① アプリを作成する前に書き出す (例) <HTML要素(見た目)> ページ右上にメニューボタンがある 画面右にメニューリストが隠れている <トリガー(何をしたら?)> メニューボタンを押したら <イベント(何が起きる?)> メニューが右から出てくる CSSアニメーションをJSで動かすので、JSではclassをつける必要がある ② 具体的に書き出し、思考の流れを整理する (例) HTML要素のheaderにclassをjsでつける もう一回クリックしたら、classが外れてメニューから引っ込む ③ 要するに何をしたいかを書き出す (例) 1つのボタンのクリックで、headerに対してclassをつけ外しをする ④ 検索する (例) 『1つのボタンのクリック』『headerに対してclassをつけ外しをする』 ⑤ 検索して、サンプルコードを拾ってきて組み合わせる 参考サイト <プログラミング難しい人向け>言葉で考えるJavaScriptの学習方法【プログラミング脳を鍛える】
- 投稿日:2021-07-29T10:43:45+09:00
TypeScriptの型
TypeScriptの型 1.基本的な型 2.Intersection Type, Union Type 3.typeof, keyof 4.ジェネリックス 1. 基本的な型 string, number, boolean型 const name: string = "hello"; const num: number = 1; const flag: boolean = false; interface(抽象型) interface NAME { first: string; last?: string | null; } const nameObj: NAME = { first: "Yamada", last: "Taro" }; interfase型:中身の実装を持たず、メンバーや型の定義だけ持つ。 指定したメンバが必ず存在する last?:はlastが存在しない可能性もあることを指す string | null はstringまたはnullであることを指す interfaseとtypeの違い interface NAME { first: string; last: string; } type PROFILE = { age: number; city: string; }; 表記の仕方が違う(=や;の有無) interfaceではオブジェクトとクラスの型だけ定義できる typeでは他の型も参照できる interfaceは拡張ができる 最近ではtypeを使う人が多くなった 参考資料 関数の場合 const func = (x: number, y: number): number => { return x + y; }; 引数と返り値の方を指定する 2. Intersection Type, Union Type Intersection Type type PROFILE = { age: number; city: string; }; type LOGIN = { username: string; password: string; }; type USER = PROFILE & LOGIN; const userA: USER = { age: 30, city: "tokyo", username: "xxx", password: "yyy", }; type USER = PROFILE & LOGIN;によってPROFILEとLOGINの両方を満たす方を生成できる Union Type // booleanかnumberのいずれか let value: boolean | number; value = true; value = 10; // numberかstringいずれかの配列 let arrayUni: (number | string)[]; arrayUni = [0, 1, 2]; // "Facebook"か"Google"か"Amazon"のいずれか let company: "Facebook" | "Google" | "Amazon"; company = "Amazon"; 3. typeof, keyof typeof let message: string = "Hi"; // typeがstringになる let message2: typeof message; let animal = { cat: "small cat", }; let newAnimal: typeof animal = { cat: "big cat", }; typeofを使うと、指定した変数の方をコピーできる JAONの複雑な型を取得するときにtypeofが便利になる keyof type KEYS = { primary: string; secondary: string; }; let key: keyof KEYS; // "primary" || "secondary" key = "primary"; keyofを使うと、指定したオブジェクトのキーからなる型を生成してくれる 4. ジェネリックス 変数の場合 type GEN<T> = { item: T; }; // T=string const gen0: GEN<string> = { item: "hello", }; // string || numberになる type GEN2<T extends string | number> = { item: T; }; ジェネリックスを使うと、抽象的に型を宣言できる。 関数の場合 function funcGEN<T>(props: T) { return { item: props }; } const gen = funcGEN<string>("test"); 型引数Tを受け取って、propsの方にはめる
- 投稿日:2021-07-29T08:57:50+09:00
[JavaScript] import, exportについて
私は普段phpを書いており、最近はVue.js, Nuxt.jsも触っているのですが、 動かすことのできるコードは書けています。 Vue, Nuxt を触る前まではES5しか書いたことがなく、 ほぼ毎日レベルで見ている import, export を正直理解できていなかったので、 記事にしてまとめました。 import, export is 何 変数・関数・クラスを文字通りexportすることができ、そのexportされたものをimportすることで、外部モジュールに記載された処理等々を使うことができる機能。 phpでいえば importは require で、exportはpublic修飾子にあたるようなもの。 export はクラスで言う、public修飾子? public修飾子というわけではないが、exportをしなければ、そのファイル内に機能が閉じる。 そのためexportをすれば外部から参照する手段となるので、そういう意味では "public" という認識で間違いない。 export の名前付き、デフォルトについて 名前付き 変数・関数・クラスに付けられた名前でexportする。 デフォルト 変数・関数・クラスに名前を付けても付けなくてもいい。1つのファイルに1回しか使えない。 import の名前付き、デフォルトについて exportの方法が2種類あるのでimportの方法も2種類ある。 名前付き import {needCurlyBrace} from './Foo.js'; デフォルト 名前付きのように波括弧は不要だが、import時に命名する必要がある import Bar from './Foo.js'; import, export の構文について 色々あるので、下記MDNを参照してください。 import export export で被った名前をimportしたらどうなるの? 名前が被っている場合は、importできない。import時に as を加えて別名にする必要あり。 importしたファイル内に同じ名前があった場合は? こちらも同様、importできない。import時に as を加えて別名にする必要あり。 名前付きのほうが良く無い?import側で毎回命名って辛く無い? そう思う。し、世間もそのようである。 export defaultを使わないようにしている記事もいくつかあるから、そうなんだと思う。 TypeScript Deep Dive でさえもそのような記事がある。 また、Tree-Shaking的な観点でもやっぱりアンチパターンのようです。 Qiita開発チームがReactの開発で default export を使わなくなった理由 なぜ default export を使うべきではないのか? Avoid Export Default ちなみにJetbrains WebStormではdefaultで使われているものもコードジャンプできた。けどね。 もしできなかったらどこになにあるか分からなくて地獄だね。 export default 使うタイミングってどこよ? 調べたけど特に見つからなかった。 ご存知の方いらっしゃいましたら教えてください! まとめ 基本 named export を使おう。 命名はいつも通り気をつけよう。 使っていない(無駄な)import は避けよう。 編集後記 以前ざっと import, export を調べた時は、 基本的に export default 推奨、って結構書かれてた気がするんだけど、全くの真逆だった。 調べると件数は少ないながらも、そのような記述している昔の記事がでてくるので、 トレンド的にそのような時もあったのかな。。
- 投稿日:2021-07-29T06:40:57+09:00
Promoiseでwait風メソッド
前置き 以前こんな記事を書いてLGTMをたくさんもらいましたが今回はその別解でよりモダンな方法を紹介します。 async, await, Promiseメソッドが実装されたことにより、より簡単に、しかも非jQueryでwaitを実現できるようになりました。 実装 function wait(msec) { return new Promise((resolve) => setTimeout(resolve, msec)); } これだけです。え?これどう動かすの、と言われたら使い方は二種類あります。 パターン1 console.log(new Date); wait(5000).then(() => { console.log(new Date); }); このタイプは単純にPromiseのメソッドチェーンを使っているのでどこでも使えます。ただしメソッド自体は非同期で走るのでwaitは素通りされてします。 パターン2 async function test() { console.log(new Date); await wait(5000); console.log(new Date); } 今度のタイプはasyncメソッドの中であればawaitでpromiseを待つことができることを利用しているのでsleep関数みたいな使い方ができます。 javascriptは日々進化していて便利になっていってますのでついていくのが大変な反面面白い言語です。
- 投稿日:2021-07-29T01:28:10+09:00
GASで東京都オープンデータカタログから新型コロナウィルス陽性患者数を取得
はじまり 東京都の新型コロナ陽性者数を日別でグラフ化したり、前日比、先週比、速報値などの情報をGASを使って毎日、自動的に自分のスマホへ通知したいなという動機が事の始まりです。 情報元 まず、日毎のデータですが、東京都では東京都オープンデータカタログサイトにて情報開示しており、コレを活用することで調べる事ができます。 APIについては、オープンデータAPIについて にある /Covid19Patient新型コロナウイルス陽性患者発表詳細 を利用すると日々の陽性者数を取得できます。 データを取得する際、ポイントとなるのが最大取得レコード数(上限:1000件)を超える場合、パラメータcursorの値には、前レスポンスで取得したendCursor値をURLエンコードしてからリクエストする必要があります。 ※取得したendCursor値をそのまま当て込んでもその後のデータは得られません。 コード.gs // メインの処理 function main() { // 前日は引数を1 var date = getTargetDate(1); var object = getAPItoXML(date, date); // 性別毎の患者数を集計 var group = object.reduce(function (result, current) { var row = result.find(function (x) { return x.gender === current['患者_性別'] }); if (row) { row.count ++; } else { result.push({ gender: current['患者_性別'], count: 1, }); } return result; }, []); // 日付と患者数 Logger.log(Utilities.formatString("%s --> 患者数:%s", Utilities.formatDate(date, 'JST', 'yyyy-MM-dd'), object.length)); // 性別毎の患者数 Logger.log(group); } // リクエスト処理 function getAPItoXML(inFrom, inTill) { var wkPayload = { // 日付(公表_年月日)による絞り込み (自) 'from' : Utilities.formatDate(inFrom, 'JST', 'yyyy-MM-dd'), // 日付(公表_年月日)による絞り込み (至) 'till' : Utilities.formatDate(inTill, 'JST', 'yyyy-MM-dd'), // 最大取得レコード数 (上限:1000) 'limit' : 1000 }; var wkResponse = UrlFetchApp.fetch(getParamUrl(wkPayload)); var wkJson = JSON.parse(wkResponse.getContentText()); var wkRes = wkJson[0]; var wkEndCur = wkJson[1]; while(wkEndCur['moreResults']==='MORE_RESULTS_AFTER_LIMIT'){ // 相手サーバーへの負荷を抑制するため5秒待つ Utilities.sleep(5000); // 取得レコードがlimitを超える場合、前のレスポンスにおける"endCursor"の値を指定 wkPayload['cursor'] = wkEndCur['endCursor']; wkResponse = UrlFetchApp.fetch(getParamUrl(wkPayload)); wkJson = JSON.parse(wkResponse.getContentText()); wkEndCur = wkJson[1]; // 取得データをマージする wkRes.push(...wkJson[0]); } return wkRes; } // 日数から日付型データを取得 function getTargetDate(inInt) { var date = new Date(); //現在日時のDateオブジェクトを作る var day = date.getDate(); date.setDate(day - inInt); return date; } // パラメータはURLエンコードしてURLを生成 function getParamUrl(inArray) { var arr = []; for(var key in inArray) { // 値はURLエンコードする arr.push(key + "=" + encodeURIComponent(inArray[key])); } var url = 'https://api.data.metro.tokyo.lg.jp/v1/Covid19Patient' + "?" + arr.join("&"); return url; } 結果 注意点 東京都オープンデータカタログを速報値として利用するには、更新のタイミングが遅いため正直、向いていません。 概ね20時過ぎに更新されるている様なので、速報値は東京都福祉保健局のTwitterを見た方が早いです。 Twitterアカウントでは、毎日16:45〜46のタイミングで速報値を発表されています。 また、希に当日データの値が速報値より少ない事があったり、22:00過ぎても更新されなかった事がありますが、翌日には修正されています。