20210417のJavaScriptに関する記事は27件です。

【初心者でもわかる】CSSで「✕」の作り方

どうも7noteです。CSSオンリーで「✕」を作ります CSSを使って✕を作ります。 コピペで使えますよ。 線の端を丸くすることも可能 cssで✕の作り方 .batten { width: 20px; /* 線の長さ */ position: relative; /* 基準位置に指定 */ } .batten::before, .batten::after { content: ""; /* 疑似要素に必須 */ width: 100%; /* 幅いっぱいを指定 */ height: 2px; /* 適度な太さを指定 */ display: inline-block; /* 高さを持たせるためにinline-blockを指定 */ background: #aaa; /* 線の色を指定 */ border-radius: 2px; /* 線の端を丸くしたいなら指定する */ position: absolute; /* 相対位置に指定 */ top: 0; /* 表示位置を上から0pxに指定 */ left: 0; /* 表示位置を左から0pxに指定 */ } .batten::before { transform: rotate(45deg); /* 時計回りに45度回転させる */ } .batten::after { transform: rotate(-45deg); /* 反時計回りに45度回転させる */ } ハンバーガーメニュー(3本線メニュー)から✕に変わるやつ こちらは過去の記事で紹介しているので以下からご覧ください。 まとめ テキストで✕にしてもいいのですが、線1本ずつに装飾したり、アニメーションをさせるならcssで✕を作ったほうが勝手がいいですね。 あとはborder-radiusで線の端を丸くできるので、デザイン的にも◎。 おそまつ! ~ Qiitaで毎日投稿中!! ~ 【初心者向け】WEB制作のちょいテク詰め合わせ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

BookMarkletを作る

BookMarklet作る 素人は手を出しづらいからかこんなに便利なのにあまり使っている人がいない。 セキュリティ懸念なのか。単に知らないのか 強制ダークモード いまさらながらダークモード対応に入門したより javascript:(function(){ document.body.style.filter="invert(100%) hue-rotate(180deg)"; })() ただ、上記ページでも言われている通り、画像が反転する。実用的ではないですね。 真・強制ダークモード javascript:(function(){document.body.style.color="#c5cddb";document.body.style.backgroundColor="#171923";document.body.style.transition="all 500ms";let elm=document.querySelectorAll("a,p,div");Object.keys(elm).forEach(function(i){elm[i].style.color="#c5cddb";elm[i].style.backgroundColor="#171923";});})() ぼちぼち使えます。目に優しい。 Before After
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

配列操作メソッド

forEach 列データに対してforEachを実行すると、、配列データの値1つずつに対してコールバック関数に記述した処理を実行できる。 配列の中身を取得 const items = ['りんご','バナナ','もも','ブドウ']; items.forEach(value => { console.log(value); });//りんごバナナももブドウ) map 関数内に実行したい処理を書いておくことで、配列の各要素に対して好きな操作ができる 「forEach」との違いは、「map」は実行後の結果を配列データとして返すこと 元となる配列から新しい配列を作成できる const array = [1,2,3,4,5,6,7]; const newArray = array.map(value => value * 3) console.log(newArray);//[3,6,9,12,15,18,21] find 提供されたテスト関数を満たす配列内の 最初の要素の値を返す trueを返す要素が見つかるまで、要素に対して一度ずつ関数を実行する。 const before = [25,40,230,280]; const after = before.find(param => { return ((param % 20) === 0); }); console.log(after);//40 filter 全ての要素に対して関数を一度ずつ実行し、戻り値(return)で true を返した要素からなる新しい配列を生成する。 mapとは異なり、trueを返した要素のみの配列を返し、生成された配列の値には実行時の要素の値が格納される。 const before = [25,40,230,280]; const after = before.filter(param => { return ((param % 20) === 0); }); console.log(after);//[40,280] some trueを返す要素が見つかるまで、要素に対して一度ずつ関数を実行する。 trueを返す要素が見つかると、trueを返す。 const list = [10, 20, 30, 40]; const retVal = list.some(param => { console.log(param); return (param >= 20); }); >10 >20 console.log(retVal)//true evey false を返す要素が見つかるまで、要素に対して一度ずつ関数を実行する。 falseを返す要素が見つかると、falseを返す。 const list = [10, 20, 30, 40]; const retVal = list.every(param => { console.log(param); return (param >= 20); }); >10 console.log(retVal);//false
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【ブログ作りたい方必見】Reactでmdファイルを使わずに、Qiitaと同じようにMarkdownで記事を書く方法

皆さんこんにちは! つい1ヶ月ほど前ぐらいからReactの勉強をし始め、今ではReactのフレームワークGatsby.jsで遊んでいます。 そんなReactで面白いパッケージを発見したのでご紹介したいと思います。 その名は、、、 react-markdownです! これはQiitaと同じようにMarkdownで記事を書くことが可能です! 世の中便利になりましたね。 実装も全く難しくありません。 パッケージのインストール時間を合わせても10分いかないくらいで実装できます。 説明はこの辺にして早速始めていきましょう! はじめに QiitaのMarkdownすべてが実装できるわけではありません。 console.log('markdown') 例えば、このようなコードを記載するMarkdownは使えません。 もしかしたら実装できるかもしれないのですが、調べた限り方法は見つかりませんでした。 知っている方がいたらぜひコメント欄に教えて下さると助かります。 必要パッケージのインストール まずは必要なパッケージをインストールします。 npm install react-markdown remark-gfm github-markdown-css react-markdownの設定 import 'github-markdown-css/github-markdown.css'; import React from 'react'; import ReactMarkdown from 'react-markdown'; import gfm from 'remark-gfm'; const Markdown = () => { const markdown = ` **aiueo** # aiueo ` return( <React.Fragment> <ReactMarkdown className="markdown-body p-3" remarkPlugins={[gfm]} children={markdown} /> </React.Fragment> ) } export default Markdown まず初めに気を付けることはmarkdownの記述を""(ダブルクォーテーション)ではなく、``(バッククォーテーション)で行っているということです。 ダブルクォーテーションだと正しくmarkdownが読み込まれないので気を付けください。 remarkPluginsはよくわかんないっす(笑)。 一応公式ドキュメントを載せておきます。 react-markdown - npm children にはMarkdownで記述したものを指定します。 最後にimport 'github-markdown-css/github-markdown.css'でCSSをインポートしてください。 これでMarkdownでブログを書くことが可能です。 実際に書くとこんな感じです。 意外と簡単に実装できました。 ただ、import 'github-markdown-css/github-markdown.css'これがないとQiitaのようなMarkdownを記述できないところで少し躓きました。 皆さんはコピペで十分です! 以上、「【ブログ作りたい方必見】Reactでmdファイルを使わずに、Qiitaと同じようにMarkdownで記事を書く方法」でした! Thank you for reading
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

1から100までの間で、3の倍数の数だけを足した

for (var i = 0; i < 100; i++) { document.write(i); var j= (i%3 === 0); だめだ、全然違う。 var はこう言う名前の変数 使います。宣言の言葉 sum は指定範囲の合計 num は数字、番号 こんなんできました var sum = 0;//こんなふうに変数の宣言と値の代入を一行で行うことを初期化っていう for (var i = 1; i < 100; i++) { if (i%3 === 0) { sum = sum +i ; } } document.write(sum); for文の1回目のループは初期値で回るから1ではなく0スタートvar sum = 0; ブラウザには 1683 と出たよ やり方あってますか私?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

復習 Javascript きほんの 「き」 初心者  for文

for文 繰り返し ループ <script> for (var i = 0; i < 11; i++) { document.write(i); } </script> ブラウザ表示 012345678910 こんな感じで出た。ぁー10まで順番に出るのね。 どんな感じで処理されていくか。 1・iに0を入れて 2・0<11を比較 3・ document.write(i) iの値の0を出力して 4・i++ i の値が1になる 1ずつ増やして繰り返していく。 最後は 11<11を比較して falseになるので、そこでストップ だから10までしか ブラウザにでない。 結果がfalseになったら終了なんだね
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【javascript】「,」でsplitする際、「"~,~"」の「,」での分割を回避

前置き javascriptでsplitを使ってコンマで分割すると、""で囲まれた内部のコンマまで分割されてしまう件 - Qiita https://qiita.com/hatorijobs/items/dd0c730e6faba0c84203 こちらの記事きっかけで書いてます。 はい、また他人の記事に乗っかってます。ごめんなさい。 CSVで読み込んだ行を「,」で分割する際の問題のようです。 このような行を block1,block2,"¥12,345",block4,block5 こうしたいのに => ["block1", "block2", ""¥12,345"", "block4", "block5"] こうなってしまう => ["block1", "block2", ""¥12", "345"", "block4", "block5"] コード 最近ハマってるreduceを使って書きました。 const csvSplit = (str, s=',') => { str = str.replace(/"+/g,'"').split(s); //replaceは不要なら外してもOK let flag = str[0].startsWith('"') - str[0].endsWith('"'); //"~"内の時「1」 str = str.slice(1).reduce((p, x) => { //「"」が[1]先頭・[2]末尾または[3]両方に存在するか、[0]無いか分類し、処理 switch (x.startsWith('"') + x.endsWith('"')*2) { case 0: return p.concat( flag===1 ? p.pop()+s+x : x ); case 2: return p.concat( flag--===1 ? p.pop()+s+x : x ); case 1: flag===1 ? p.push( ...p.pop().split(s) ) : flag = 1; return p.concat(x); //修正 case 3: flag--;return p.concat(x); //修正 } }, [str[0]]); //最後の要素が閉じられてない場合は分割 return flag===1 ? str.concat( ...str.pop().split(s) ) : str; }; console.log( csvSplit('block1",block2",block3","¥12,345,678,900",block4,block5"') ); // ["block1"", "block2"", "block3"", ""¥12,345,678,900"", "block4", "block5""] console.log( csvSplit( '"block1,block2","block3,block4,"¥12,345,678,900",block4,"block5,block6' ) ); // [""block1,block2"", "block3", ""¥12,345,678,900"", "block4", ""block5", "block6"] 感想 なんかごちゃごちゃしてて微妙。 気を遣う所が増えてしまったので、別のアプローチで考えたほうが良かったかもしれない。 コード(追記) 要素一つ一つを追加処理していく先のコードと違い、今回は範囲を切り出して追加していく。 切り出しの開始位置を記憶する変数startを追加。 const csvSplit = (str, s=',') => { let [flag, start] = [0, 0]; str = str.replace(/"+/g,'"').split(s); return str.reduce((p, x, i, a) => { switch (x.startsWith('"') + x.endsWith('"')*2) { case 1: p.push( ...a.slice(start, i) ); [flag, start] = [1, i]; break; case 2: if (!flag) break; p.push( a.slice(start, i+1).join(s) ); [flag, start] = [0, i+1]; break; case 3: p.push(x); [flag, start] = [0,i+1]; break; } return p; }, []).concat(str.slice(start)); }; ↓更に追加。 switch内の類似処理が気になったので関数化してまとめました。 処理自体は上記と同じ。人によっては少し見難いかもしれない。 const csvSplit = (str, s=',') => { let [flag, start] = [0, 0]; const proc = (p, e, f, t) => ([flag, start] = [f, t], p.concat(e)); const reducer = (p, x, i, a) => { switch (x.startsWith('"') + x.endsWith('"')*2) { case 1: return proc(p, a.slice(start,i), 1, i); case 2: return flag ? proc(p, a.slice(start,i+1).join(s), 0, i+1) : p; case 3: return proc(p, x, 0, i+1); default: return p; } }; str = str.replace(/"+/g,'"').split(s); return str.reduce(reducer, []).concat(str.slice(start)); };
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【javascript】「,」でsplitする際、「"~,~"」の「,」での分割を回避(追記コード2個)

前置き javascriptでsplitを使ってコンマで分割すると、""で囲まれた内部のコンマまで分割されてしまう件 - Qiita https://qiita.com/hatorijobs/items/dd0c730e6faba0c84203 こちらの記事きっかけで書いてます。 はい、また他人の記事に乗っかってます。ごめんなさい。 CSVで読み込んだ行を「,」で分割する際の問題のようです。 このような行を block1,block2,"¥12,345",block4,block5 こうしたいのに => ["block1", "block2", ""¥12,345"", "block4", "block5"] こうなってしまう => ["block1", "block2", ""¥12", "345"", "block4", "block5"] コード 最近ハマってるreduceを使って書きました。 const csvSplit = (str, s=',') => { str = str.replace(/"+/g,'"').split(s); //replaceは不要なら外してもOK let flag = str[0].startsWith('"') - str[0].endsWith('"'); //"~"内の時「1」 str = str.slice(1).reduce((p, x) => { //「"」が[1]先頭・[2]末尾または[3]両方に存在するか、[0]無いか分類し、処理 switch (x.startsWith('"') + x.endsWith('"')*2) { case 0: return p.concat( flag===1 ? p.pop()+s+x : x ); case 2: return p.concat( flag--===1 ? p.pop()+s+x : x ); case 1: flag===1 ? p.push( ...p.pop().split(s) ) : flag = 1; return p.concat(x); //修正 case 3: flag--;return p.concat(x); //修正 } }, [str[0]]); //最後の要素が閉じられてない場合は分割 return flag===1 ? str.concat( ...str.pop().split(s) ) : str; }; console.log( csvSplit('block1",block2",block3","¥12,345,678,900",block4,block5"') ); // ["block1"", "block2"", "block3"", ""¥12,345,678,900"", "block4", "block5""] console.log( csvSplit( '"block1,block2","block3,block4,"¥12,345,678,900",block4,"block5,block6' ) ); // [""block1,block2"", "block3", ""¥12,345,678,900"", "block4", ""block5", "block6"] 感想 なんかごちゃごちゃしてて微妙。 気を遣う所が増えてしまったので、別のアプローチで考えたほうが良かったかもしれない。 コード(追記) 要素一つ一つを追加処理していく先のコードと違い、今回は範囲を切り出して追加していく。 切り出しの開始位置を記憶する変数startを追加。 const csvSplit = (str, s=',') => { let [flag, start] = [0, 0]; str = str.replace(/"+/g,'"').split(s); return str.reduce((p, x, i, a) => { switch (x.startsWith('"') + x.endsWith('"')*2) { case 1: p.push( ...a.slice(start, i) ); [flag, start] = [1, i]; break; case 2: if (!flag) break; p.push( a.slice(start, i+1).join(s) ); [flag, start] = [0, i+1]; break; case 3: p.push(x); [flag, start] = [0,i+1]; break; } return p; }, []).concat(str.slice(start)); }; ↓更に追加。 switch内の類似処理が気になったのでまとめました。 処理自体は上記と同じ。人によっては少し見難いかもしれない。 const csvSplit = (str, s=',') => { let [flag, start] = [0, 0]; const proc = (p, e, f, t) => ([flag, start] = [f, t], p.concat(e)); const reducer = (p, x, i, a) => { switch (x.startsWith('"') + x.endsWith('"')*2) { case 1: return proc(p, a.slice(start,i), 1, i); case 2: return flag ? proc(p, a.slice(start,i+1).join(s), 0, i+1) : p; case 3: return proc(p, x, 0, i+1); default: return p; } }; str = str.replace(/"+/g,'"').split(s); return str.reduce(reducer, []).concat(str.slice(start)); }; 個人的に気になっているところは潰せたと思う。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

QR忘れ物アプリ (2) ログインページ [Vue,Vuetify,Firebase]

はじめに 前回:環境構築 https://qiita.com/rayan/items/5d04ee2ca7860c220dec GitHubリポジトリ 準備中 用いる省略名称 RTDB: Realtime Database 操作するファイル public index.js authenticationOps.js databaseOps.js src router index.js views Authentication.vue (新規ファイル) App.vue public/index.js authenticationOps.js、databaseOps.jsを呼び出すscriptの一覧に追加。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <!-- update the version number as needed --> <script src="/__/firebase/8.3.1/firebase-app.js"></script> <!-- include only the Firebase features as you need --> <script src="/__/firebase/8.3.1/firebase-auth.js"></script> <script src="/__/firebase/8.3.1/firebase-database.js"></script> <script src="/__/firebase/8.3.1/firebase-functions.js"></script> <!-- initialize the SDK after all desired features are loaded, set useEmulator to false to avoid connecting the SDK to running emulators. --> <script src="/__/firebase/init.js?useEmulator=true"></script> <script src="https://www.gstatic.com/firebasejs/ui/4.8.0/firebase-ui-auth.js"></script> <link type="text/css" rel="stylesheet" href="https://www.gstatic.com/firebasejs/ui/4.8.0/firebase-ui-auth.css" /> <script type="application/javascript" src="databaseOps.js"></script> <script type="application/javascript" src="authenticationOps.js"></script> </head> <body> <div id="app"></div> <!-- built files will be auto injected --> </body> </html> public/databaseOps.js Firebase RTDB関連のfunctionを格納 addUser ユーザーを追加し、userPreferencesを初期設定します。 Firebase AuthのユーザーID(userId) をkeyとして用います。 var vars = {}; // stores global variable function addUser(userId, userName, email){ var data = { 'email': email, 'name': userName }; vars.userId = userId; userCredentials.add(data); userPreferences.init(); } userCredentials = { 'add': function (data) { firebase.database().ref('userCredentials/'+vars.userId).set(data); }, }; userPreferences = { 'init': function () { var data = { 'notifyAtLinkOpen': false, 'notifyNotLostItem': true, 'whereToNotify': 'email' }; firebase.database().ref('userPreferences/'+vars.userId).set(data); }, }; public/authenticationOps.js Firebase Auth関連のfunctionを格納 mounted mountedは要素がDOMに追加されたときに呼ばれます。 uiconfig EmailAuthProvider: メールアドレスとパスワードによるログインを行います。 callbacks signInSuccessWithAuthResult サインインに成功したときに呼ばれます。 サインイン結果からuid、displayName、emailを得ることができます。 新規登録の時にはaddUserを呼び、RTDBにユーザーを登録します。 uiShown UIがロードされたときに呼ばれます。今回はロードが終了したため、loaderの表示を消します。 function loginScreen (){ // FirebaseUI config. var uiConfig = { signInOptions: [ // Leave the lines as is for the providers you want to offer your users. //firebase.auth.GoogleAuthProvider.PROVIDER_ID, firebase.auth.EmailAuthProvider.PROVIDER_ID, ], callbacks: { signInSuccessWithAuthResult: (authResult, redirectUrl) => { var userId = authResult.user.uid; if (authResult.additionalUserInfo.isNewUser){ // if new user, register to database addUser(userId, authResult.user.displayName, authResult.user.email) } document.getElementById('userAuthentication').style.display = 'none'; //document.getElementById('mainMenu').style.display = 'block'; return false; }, uiShown: () => { document.getElementById('loader').style.display = 'none'; }, }, }; // Initialize the FirebaseUI Widget using Firebase. var ui = new firebaseui.auth.AuthUI(firebase.auth()); // The start method will wait until the DOM is loaded. ui.start('#firebaseui-auth-container', uiConfig); } src/views/Authentication.vue src/views内に新しくAuthentication.vueを作ります。ログイン・新規登録画面のHTMLです。 ログイン・新規登録画面画面 loaderはログイン・新規登録画面のdiv要素がロード中に"Loading..."と表示するdiv要素です。 firebaseui-auth-containerはログイン・新規登録画面のdiv要素です。 <template> <!-- authentication --> <div id="userAuthentication" style="display:block"> <div id="firebaseui-auth-container"></div> <div id="loader">Loading...</div> </div> </template> <script> export default { name: "Authentication", mounted () { loginScreen(); }, data: () => ({ // }), }; </script> src/router/index.js Authentication.vueの設定を追加します。 import Vue from "vue"; import VueRouter from "vue-router"; import Home from "../views/Home.vue"; Vue.use(VueRouter); const routes = [ { path: "/", name: "Home", component: Home, }, { path: "/authentication", name: "Authentication", // route level code-splitting // this generates a separate chunk (about.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import(/* webpackChunkName: "about" */ "../views/Authentication.vue"), }, ]; const router = new VueRouter({ mode: "history", base: process.env.BASE_URL, routes, }); export default router; src/App.vue Authentication.vueの設定を追加します。 <template> <v-app> <v-main> <Authentication /> </v-main> </v-app> </template> <script> //import HelloWorld from "./components/HelloWorld"; import Authentication from "./views/Authentication"; export default { name: "App", components: { Authentication, }, data: () => ({ // }), }; </script> Build ビルドし、動作をテストします。動作のテストにはFirebase Emulatorが必要であるため、起動します。 npm run build firebase emulators:start dist/index.htmlをブラウザで閲覧し下の画像のような画面が出ることを確認します。 アカウント作成実行後、AuthとTRDBに正常にアカウント登録の処理がされていることを確認します。 Firebase Authentication Emulator http://localhost:4000/auth にアクセス 登録したメールアドレス、名前のアカウントが追加されていることを確認する。 Firebase RTDB Emulator http://localhost:4000/database/{データベース名}/data にアクセス 以下のjsonファイルが生成されていることを確認する。 http://127.0.0.1:9000/?ns=[データベース名]: { userCredentials: { [Firebase AuthのUID]: { email: "[Email]" name: "[名前]" } }, userPreferences: { [Firebase AuthのUID]: { notifyAtLinkOpen: false, notifyNotLostItem: true, whereToNotify: "email" } } } 次回 アイテム登録画面
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

セレクトボックスで選んだ数だけ要素を追加する

セレクトボックスで数字を選び、選んだ数字の数だけ要素を追加する方法。 liタグの数を変える。 html <select name='addNumber'> <option value="1">1</option> <option value="2">2</option> <option value="3">3</option> <option value="4">4</option> <option value="5">5</option> <option value="6">6</option> </select> <ul id="addTarget"> <li>最初の要素</li> </ul> jquery $('[name=addNumber]').on('change', function(){ //追加要素をいったん全部消す $(".addContent").remove() //セレクトボックスから数字を取ってくる let num = $('[name=addNumber]').val() //セレクトボックスの文字を数字にする num = Number(num) //追加するコンテンツ let add_content = '<li class="addContent">追加</li>' //要素数が2個以上は追加 if(num > 1){ for (let i=1; i<num; i++) { $('#addTarget').append(add_content) } } });
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】破壊的メソッドの非破壊的な書き方

破壊的メソッドの何がまずいのか? 破壊的メソッドを使うとコードの読み手に混乱を与えてしまう可能性があります。 破壊的メソッドを使った例 const numbers = [1, 2, 3, 4, 5]; // とても長い処理 numbers.push(6); <- 読み飛ばされてしまう可能性が高い // とても長い処理 console.log(numbers); <- 読み手は[1, 2, 3, 4, 5]を期待している // 実際は[1, 2, 3, 4, 5, 6]; 非破壊的メソッドを使った例 const numbers = [1, 2, 3, 4, 5]; // とても長い処理 const newNumbers = numbers.concat(6); // とても長い処理 console.log(numbers); // [1, 2, 3, 4, 5] 読み手の期待通りのまま console.log(newNumbers); // [1, 2, 3, 4, 5, 6] 結論 スプレッド構文を使えばOK! 以下、具体的な書き方と別の書き方をまとめます。 目次 push() unshift() pop() shift() sort() reverse() copyWithin() push() Array.prototype.push() push() メソッドは、配列の末尾に 1 つ以上の要素を追加することができます。また戻り値として新しい配列の要素数を返します。 破壊 const numbers = [1, 2, 3, 4, 5]; numbers.push(6); console.log(numbers); // [1, 2, 3, 4, 5, 6] 非破壊 // スプレッド構文 const numbers = [1, 2, 3, 4, 5]; const newNumbers = [...numbers, 6]; console.log(numbers); // [1, 2, 3, 4, 5] console.log(newNumbers); // [1, 2, 3, 4, 5, 6] // concat() const numbers = [1, 2, 3, 4, 5]; const newNumbers = numbers.concat(6); console.log(numbers); // [1, 2, 3, 4, 5] console.log(newNumbers); // [1, 2, 3, 4, 5, 6] unshift() Array.prototype.unshift() unshift() メソッドは、配列の最初に 1 つ以上の要素を追加し、新しい配列の長さを返します。 破壊 const numbers = [1, 2, 3, 4, 5]; numbers.unshift(0); console.log(numbers); // [0, 1, 2, 3, 4, 5] 非破壊 // スプレッド構文 const numbers = [1, 2, 3, 4, 5]; const newNumbers = [0, ...numbers]; console.log(numbers); // [1, 2, 3, 4, 5] console.log(newNumbers); // [0, 1, 2, 3, 4, 5] // concat() const numbers = [1, 2, 3, 4, 5]; const newNumbers = [0].concat(numbers); console.log(numbers); // [1, 2, 3, 4, 5] console.log(newNumbers); // [0, 1, 2, 3, 4, 5] pop() Array.prototype.pop() pop() メソッドは、配列から最後の要素を取り除き、その要素を返します。このメソッドは配列の長さを変化させます。 破壊 const numbers = [1, 2, 3, 4, 5]; numbers.pop(); console.log(numbers); // [1, 2, 3, 4] 非破壊 // スプレッド構文 const numbers = [1, 2, 3, 4, 5]; const newNumbers = [...numbers].pop(); console.log(numbers); // [1, 2, 3, 4, 5] console.log(newNumbers); // [1, 2, 3, 4] // slice() const numbers = [1, 2, 3, 4, 5]; const newNumbers = numbers.slice(0, 4); console.log(numbers); // [1, 2, 3, 4, 5] console.log(newNumbers); // [1, 2, 3, 4] shift() Array.prototype.shift() shift() メソッドは、配列から最初の要素を取り除き、その要素を返します。このメソッドは配列の長さを変えます。 破壊 const numbers = [1, 2, 3, 4, 5]; numbers.shift(); console.log(numbers); // [2, 3, 4, 5] 非破壊 // スプレッド構文 const numbers = [1, 2, 3, 4, 5]; const newNumbers = [...numbers].unshift(); console.log(numbers); // [1, 2, 3, 4, 5] console.log(newNumbers); // [2, 3, 4, 5] // slice() const numbers = [1, 2, 3, 4, 5]; const newNumbers = numbers.slice(1, 5); console.log(numbers); // [1, 2, 3, 4, 5] console.log(newNumbers); // [2, 3, 4, 5] sort() Array.prototype.sort() sort() メソッドは、配列の要素を in place でソートします。既定のソート順は昇順で、要素を文字列に変換してから、UTF-16 コード単位の値の並びとして比較します。 破壊 const numbers = [5, 1, 4, 2, 3]; numbers.sort(); console.log(numbers); // [1, 2, 3, 4, 5] 非破壊 // スプレッド構文 const numbers = [5, 1, 4, 2, 3]; newNumbers = [...numbers].sort() console.log(numbers); // [5, 1, 4, 2, 3] console.log(newNumbers); // [1, 2, 3, 4, 5] reverse() Array.prototype.reverse() reverse() メソッドは、配列の要素を In-place アルゴリズムで反転させます。最初の要素が最後の要素に、最後の要素が最初の要素になります。 破壊 const numbers = [1, 2, 3, 4, 5]; numbers.reverse(); console.log(numbers); // [5, 4, 3, 2, 1] 非破壊 // スプレッド構文 const numbers = [1, 2, 3, 4, 5]; newNumbers = [...numbers].reverse() console.log(numbers); // [1, 2, 3, 4, 5] console.log(newNumbers); // [5, 4, 3, 2, 1] fill() Array.prototype.fill() fill() メソッドは、開始インデックス(デフォルトは 0)から終了インデックス(デフォルトは array.length)までのすべての要素を、静的な値に変更した配列を返します。 破壊 const numbers = [1, 2, 3, 4, 5]; numbers.fill(7); console.log(numbers); // [7, 7, 7, 7, 7] 非破壊 // スプレッド構文 const numbers = [1, 2, 3, 4, 5]; newNumbers = [...numbers].fill(7) console.log(numbers); // [1, 2, 3, 4, 5] console.log(newNumbers); // [7, 7, 7, 7, 7] copyWithin() Array.prototype.copyWithin() copyWithin() メソッドは、サイズを変更せずに、配列の一部を同じ配列内の別の場所にシャローコピーして返します。 破壊 const numbers = [1, 2, 3, 4, 5]; numbers.copyWithin(2, 3, 5); console.log(numbers); // [ 1, 2, 4, 5, 5 ] 非破壊 // スプレッド構文 const numbers = [1, 2, 3, 4, 5]; newNumbers = [...numbers].copyWithin(2, 3, 5); console.log(numbers); // [1, 2, 3, 4, 5] console.log(newNumbers); // [ 1, 2, 4, 5, 5 ] 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsアプリに非同期通信のいいね機能を実装

はじめに Rialsアプリに非同期通信のいいね機能を実装しました。 方針としては、部分テンプレートを作成してレスポンスをJS形式で返すことで画面の一部のみを更新するというものです。備忘録としてまとめたいと思います。 実装イメージ 非同期でいいねといいねの解除ができる いいねされたらカウントに反映される 開発環境 macOS Catalina Ruby 2.6.5 Ruby on Rails 5.2 前提 userとpost(投稿)モデルを作成済み fontawesomeを導入済み jQueryを導入済み 目次 1.モデルとアソシエーション 2.ルーティング 3.コントローラー 4.view 1. モデルとアソシエーション モデルとアソシエーションの構成は以下の通りです。 ではfavoriteモデルを作成します。 ターミナル $ rails g model favorite user:references post:references $ rails db:migrate 次にアソシエーションを組みます。 user.rb has_many :posts, dependent: :destroy has_many :favorites, dependent: :destroy # すでにいいねしているかを判定するメソッド def already_favorited?(post) self.favorites.exists?(post_id: post.id) end already_favorited?メソッドを定義します。 これはもし投稿にいいねしていたら解除のリンクを表示させ、解除していたらいいねリンクを表示させる条件分岐のために記述しています。 post.rb belongs_to :user has_many :favorites, dependent: :destroy favorite.rb belongs_to :user belongs_to :post 2. ルーティング いいねは投稿ひとつひとつに紐付いているのでルーティングはネストさせて記述します。 ネストさせることでアソシエーション先のレコードのidをparamsに追加してコントローラーに渡せるようになります。 (今回の場合はfavoriteのidを取得できる) routes.rb resources :posts, only: [:index, :new, :create, :show, :destroy] do resource :favorites, only: [:create, :destroy] end 3. コントローラー favorites_controller.rbではcreateとdestroyアクションを定義します。 favorites_controller.rb class FavoritesController < ApplicationController before_action :set_post def create @favorite = Favorite.create(user_id: current_user.id, post_id: @post.id) @favorite.save end def destroy @favorite = Favorite.find_by(user_id: current_user.id, post_id: @post.id) @favorite.destroy end private def set_post @post = Post.find(params[:post_id]) end end set_post ・before_actionを設定することで、アクション実行前にどの投稿に対するものなのかを判断するためにidを取得します。 posts_controller.rb def show @post= Post.find(params[:id]) end 4. view 非同期通信をするために_favorite.html.erbという部分テンプレートを作成します。 更にcreateとdestroyアクション実行時にページの一部を更新するためにcreate.js.erbとdestroy.js.erbというファイルを作成します。 JS形式でレスポンスするためにjs.erbという拡張子になっています。 ディレクトリ構成は以下の通りです。 views |-posts | |-show.html.erb |-favorites |-_favorite.html.erb |-create.js.erb |-destroy.js.erb まずは、投稿詳細ページのviewから記述します。 posts/show.html.erb <div id="favorite_area_<%= post.id %>", class="favoriteArea"> <%= render partial: "favorites/favorite", locals: { post: @post } %> </div> divタグにfavorite_area_<%= post.id %>というidを付与しています。 これはどの投稿に対するものなのかを判別するために記述しています。 またrenderメソッドで_favorite.html.erbファイルを呼び出しています。 localsオプションではpostコントローラーで定義した@postの変数を部分テンプレートのなかでpostとして使用できるように定義しています。 次に部分テンプレートです。 favorites/_favorite.html.erb <% if current_user.already_favorited?(post) %> <%= link_to post_favorites_path(post), method: :delete, class: "goodLink", remote: true do %> <i class="fas fa-heart"></i> <% end %> <% else %> <%= link_to post_favorites_path(post), method: :post, class: "goodLink", remote: true do %> <i class="far fa-heart"></i> <% end %> <% end %> <p class="favoriteCount"><%= post.favorites.count %></p> user.rbで定義したalready_favorited?メソッドを使用しています。 link_toにはremote: tureを記述することで非同期通信をしますという意味になります。これでHTML形式ではな先程作成したjs.erbファイルを返す挙動になります。 最後の行の<%= post.favorites.count %>はcountメソッドを使用していいねされている数を表示しています。 最後にjs.erbファイルの編集です。 favorites/create.js.erb <!-- #favorite_area_<%= @post.id %>この部分のHTMLだけ、renderで部分的に更新するという処理 --> $("#favorite_area_<%= @post.id %>").html("<%= j(render partial: 'favorites/favorite', locals: { post: @post }) %>"); favorites/destroy.js.erb <!-- #favorite_area_<%= @post.id %>この部分のHTMLだけ、renderで部分的に更新するという処理 --> $("#favorite_area_<%= @post.id %>").html("<%= j(render partial: 'favorites/favorite', locals: { post: @post }) %>"); 意味としてはfavorite_area_<%= @post.id %>というidを付与したdivタグの中身を_favorite.html.erbの内容で更新するという内容になります。 以上で非同期のいいね機能を実装できていると思います!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

復習 Javascript きほんの 「き」 Math.max と出会った

勉強を進めていく中で出会った Math.max Math.max関数 引数に複数の数値を指定すると、その中の最大値を返す 「使い方」 Math.max(数値1、数値2、数値3、・・・)
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

復習 Javascript きほんの 「き」 初心者  問題  ランダム if文

0〜3のランダムな数値を2つ取得し、「それぞれの数値」と「どちらの数値の方が大きいか」 2つの値が同じ場合は「同じ値」と表示させる。 rand1 : 2 rand2 : 3 rand2の方が大きいです ==================== rand1 : 0 rand2 : 2 rand2の方が大きいです rand1 : 2 rand2 : 2 2つは同じ値です こんな感じで作りました <script> var rand1 = Math.floor(Math.random() *4); var rand2 = Math.floor(Math.random() *4); document.write('<p>rand1 : '+ rand1 + '</p>'); document.write('<p>rand2 : '+ rand2 + '</p>'); if (rand1 > rand2){ document.write('<p>rand1の方が大きいです</p>'); } else if (rand2 > rand1){ document.write('<p>rand2の方が大きいです</p>'); } else { document.write('<p>2つは同じ値です</p>'); } </script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsのインストール方法の紹介

はじめまして。seki_miと申します。 本日は初投稿ということで簡単なものからアウトプットをしていきたいと考え昨日学習したNode.jsのインストール方法について説明させて頂きたいと思います。 Node.jsとは? まずNode.jsとは何か。 簡単にJavaScriptをサーバーサイドで動かせるもの。他にもPHP、Rubyとありますが、今どきのモダンなフロントエンド環境を構築する上でNode.jsを使うため、バックエンドエンジニアのみならずフロントエンドエンジニアの人も押さえておくべきスキルになります。 Node.jsインストール方法 インストール方法は2つあり、 1.Node.jsの公式サイトからzipファイルを落としてダウンロード 2.nodebrewを使う です。 どちらが良いかというと2のnodebrewを使う方法で、理由としてはNode.jsをインストールかつバージョン管理ができるツールだからです。1の方法だと数あるバージョンの中で用途により使い分ける必要があり都度サイトにアクセスしてzipを落としてインストールするという手間が発生し非常に効率が悪くなります。 nodebrewをインストールする まずはnodebrewの公式サイトにアクセスしてください。 スクロールしていくと下記のようなコードがあるのでコピーしてターミナルに貼り付けて実行して下さい。 $ curl -L git.io/nodebrew | perl – setup これは「HTTPリクエストを通してサーバーにアクセスして何かしらのコンテンツをダウンロードしたり送る」という意味のコマンドです。 次に実行されると下記のコマンドが表示されるのでコピーして実行してください。 $ export PATH=$HOME/.nodebrew/current/bin:$PATH これはパスを通すためのコマンドであり、簡単に言うと「このファイルを毎回参照してください」という意味です。 ここまで完了したらインストールが完了です。 念の為、ちゃんとインストールが出来ているか下記のコマンドで確認してみましょう。 $ nodebrew -v 実行すると「nodebrew use [バージョン名]」と表示されるはずです。 ここまではnodebrewのインストールでありNode.jsのインストールではないので注意してください。 Node.jsのインストール Node.jsをインストール際のコマンドはいくつかあります。 $ nodebrew install-binary stable $ nodebrew install-binary latest $ nodebrew install-binary [バージョン]  ※括弧は不要です 各コマンドについてですが stable... 「もっとも新しいバージョンかつ安定版」の意味です。 ※これが一番オススメです。 latest... 「もっとも新しいバージョン」の意味です。ですが、動作を100%保証されるものではありません。 [バージョン]...説明不要だと思いますが「バージョンを指定してインストールをする」という意味です。 ちなみに上記全てインストールをしても問題はございません。 次にインストールが完了したら下記のコマンドでバージョンの確認をしましょう。 $ nodebrew ls バージョンが確認出来たら「current:」という部分に注目してください。 これは現在使用しているバージョンが表示される部分であり初めてインストールする方は「current: none」と表示されているでしょう。「none」の状態だとインストールをされているだけで使える状態にはなっていません。 下記のコマンドで使えるようにしましょう。 $ nodebrew use [任意のバージョン名] ※括弧は不要です 任意のバージョン名については「stable」、「latest」でも問題ありません。 コマンド実行後、念の為に下記のコマンドで確認してみましょう。 $ nodebrew ls 「current:」の後にバージョンが表示されていれば完了です。 次は実際にNode.jsを操作してみましょう。 下記コマンドを実行してください。 $ node すると下記のような表示が出てきます。 Welcome to Node.js [バージョン名] Type ".help" for more information. > 「>」の後に下記のコマンドを入力してみましょう。 $ console.log("Hello") すると下記のように「Hello」と表示されるはずです。 > console.log("Hello") Hello これでNode.jsが動いていることが確認出来ました。終了するには"ctrl + c"を入力すると終了できます。 以上になります。 最後まで読んで頂きありがとうございました。 seki_mi
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Node.jsのインストール方法

はじめまして。seki_miと申します。 本日は初投稿ということで簡単なものからアウトプットをしていきたいと考え昨日学習したNode.jsのインストール方法について説明させて頂きたいと思います。 Node.jsとは? まずNode.jsとは何か。 簡単にJavaScriptをサーバーサイドで動かせるもの。他にもPHP、Rubyとありますが、今どきのモダンなフロントエンド環境を構築する上でNode.jsを使うため、バックエンドエンジニアのみならずフロントエンドエンジニアの人も押さえておくべきスキルになります。 Node.jsインストール方法 インストール方法は2つあり、 1.Node.jsの公式サイトからzipファイルを落としてダウンロード 2.nodebrewを使う です。 どちらが良いかというと2のnodebrewを使う方法で、理由としてはNode.jsをインストールかつバージョン管理ができるツールだからです。1の方法だと数あるバージョンの中で用途により使い分ける必要があり都度サイトにアクセスしてzipを落としてインストールするという手間が発生し非常に効率が悪くなります。 nodebrewをインストールする まずはnodebrewの公式サイトにアクセスしてください。 スクロールしていくと下記のようなコードがあるのでコピーしてターミナルに貼り付けて実行して下さい。 $ curl -L git.io/nodebrew | perl – setup これは「HTTPリクエストを通してサーバーにアクセスして何かしらのコンテンツをダウンロードしたり送る」という意味のコマンドです。 次に実行されると下記のコマンドが表示されるのでコピーして実行してください。 $ export PATH=$HOME/.nodebrew/current/bin:$PATH これはパスを通すためのコマンドであり、簡単に言うと「このファイルを毎回参照してください」という意味です。 ここまで完了したらインストールが完了です。 念の為、ちゃんとインストールが出来ているか下記のコマンドで確認してみましょう。 $ nodebrew -v 実行すると「nodebrew use [バージョン名]」と表示されるはずです。 ここまではnodebrewのインストールでありNode.jsのインストールではないので注意してください。 Node.jsのインストール Node.jsをインストール際のコマンドはいくつかあります。 $ nodebrew install-binary stable $ nodebrew install-binary latest $ nodebrew install-binary [バージョン]  ※括弧は不要です 各コマンドについてですが stable... 「もっとも新しいバージョンかつ安定版」の意味です。 ※これが一番オススメです。 latest... 「もっとも新しいバージョン」の意味です。ですが、動作を100%保証されるものではありません。 [バージョン]...説明不要だと思いますが「バージョンを指定してインストールをする」という意味です。 ちなみに上記全てインストールをしても問題はございません。 次にインストールが完了したら下記のコマンドでバージョンの確認をしましょう。 $ nodebrew ls バージョンが確認出来たら「current:」という部分に注目してください。 これは現在使用しているバージョンが表示される部分であり初めてインストールする方は「current: none」と表示されているでしょう。「none」の状態だとインストールをされているだけで使える状態にはなっていません。 下記のコマンドで使えるようにしましょう。 $ nodebrew use [任意のバージョン名] ※括弧は不要です 任意のバージョン名については「stable」、「latest」でも問題ありません。 コマンド実行後、念の為に下記のコマンドで確認してみましょう。 $ nodebrew ls 「current:」の後にバージョンが表示されていれば完了です。 次は実際にNode.jsを操作してみましょう。 下記コマンドを実行してください。 $ node すると下記のような表示が出てきます。 Welcome to Node.js [バージョン名] Type ".help" for more information. > 「>」の後に下記のコマンドを入力してみましょう。 $ console.log("Hello") すると下記のように「Hello」と表示されるはずです。 > console.log("Hello") Hello これでNode.jsが動いていることが確認出来ました。終了するには"ctrl + c"を入力すると終了できます。 以上になります。 最後まで読んで頂きありがとうございました。 seki_mi
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

three.js の examplesをnode.jsで動かす

node.jsでthree,jsのサンプルを動かそうとすると SyntaxError: Unexpected token < in JSON at position 0 だったりデータが取ってこれないことがありました。 原因はContent-Typeやencodingが適切に設定されていないことのようでしたので、 $ npm install mime-types でモジュールを追加し(必要があれば)、 server.js const mime = require('mime-types'); const port = 8080; var http = require('http'); var fs = require('fs'); var http_server = new http.createServer(function(req, res) { var url = '.'+req.url; var type = mime.lookup(url); var encoding = type.includes('text') ? 'UTF-8' : null; //console.log('>>'+'.'+url+':'+type); res.writeHead(200, {'Content-Type': type}); res.end(fs.readFileSync(url,encoding)); }).listen(port); console.log('Check http://localhost:'+port+'/ on brouser.'); を作成し node server.js とすることで解決できました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

復習 Javascript きほんの 「き」 初心者  if文の条件

ifは条件により処理を変えることができる 条件は「データ型」 論理型のtrue または false で判定してる。 通常、比較演算子を用い、比較した結果 一致した場合・・・true しない場合 ・・・false    となるように利用していくよ 比較する文を作ろう。 //0~100のランダムな数字を取得 var rand = Math.floor(Math.random() * 101);//ランダムな数字を取得できる書き方 document.write('<p>rand: ' + rand + '</p>');//rand=ランダムのこと //↑rand: ランダムな数字 の形でブラウザに表示 document.write('<p>rand >=80: ' + (rand >= 80) + '</p>');//←比較する文 //書き出し rand >=80:ランダムな数字が表示・・・80以上ならtrue! document.write('<p>rand <=30: ' + (rand <= 30) + '</p>');//←比較する文 //書き出し rand <=30:ランダムな数字が表示・・・30以下ならtrue! if (rand >= 80) { document.write('<p>randは80以上</p>'); } else if (rand <= 30) { document.write('<p>randは30以下</p>'); } else { document.write('<p>randは30より大きく80より小さい</p>'); } ブラウザの表示はこんな感じ rand: 14      ・・・ランダムに選ばれた数字について判定! rand >=80: false ・・・80より大きくないもんね rand <=30: true  ・・・30より小さいよね randは30以下 rand: 56 rand >=80: false rand <=30: false randは30より大きく80より小さい もういっちょ rand: 15 rand >=80: false rand <=30: true randは30以下
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript基礎】ユーザー操作時に発生するイベントについて

JavaScriptにおける「ユーザー操作に合わせて発生するイベント」について、自己の忘備録としてまとめています。 参考URL 参考著書 ウェブ上には、多くのイベントがあります。 クリック タップ スクロール 画像読み込み完了 など JavaScriptではそれらのイベントに合わせて処理を実行させることができます。 イベント発生時に処理を設定する イベントに合わせて処理を実行させる際は、addEventListner()メソッドを使うとイベントを制御することができます。 イベントを発生させるオブジェクトのことを「イベントターゲット」と呼び、 「イベントターゲット」でイベントが発生したときの処理を「イベントリスナー」と呼びます。 addEventLister()のオプション イベントリスナーを設定する際、addEventLister()メソッドの第3引数にオプションを指定することができます。 オプション 意味 型 capture キャプチャリングフェーズで取得するか 真偽値 once イベントリスナーの呼び出しを1回のみにするか 真偽値 passive パッシブイベントかどうか 真偽値 例えば、イベントリスナーを1度だけ呼び出したい時は、onceにtrueを設定します。 // オプション指定 const options = { once: true, }; const btn = document.querySelector('.button'); const onClickButton = () => { console.log('ボタンがクリックされました!'); }; btn.addEventListener('click', onClickButton, option); キャプチャリングフェーズやパッシブイベントについては、自分自身知らない概念だったので、下記に参考リンクだけ記載しておきます。 キャプチャリングフェーズについて イベントの伝搬(キャプチャリングとバブリング パッシブイベントについて passive: trueでなぜパフォーマンスがよくなるの?簡単な説明と使い方! 設定したイベントを削除する 設定したイベントを削除する場合は、removeEventListner()メソッドを使用します。 const btn = document.querySelector('.button'); const onClickButton = () => { console.log('ボタンがクリックされました!'); }; btn.addEventListener('click', onClickButton); //3秒後にイベントを削除する setTimeout(() => { btn.removeEventListener('click', onClickButton); }, 3000); ページが表示された時に処理をする JavaScriptでDOMを操作できるのは、HTMLドキュメントの読み込みと解析が完了したタイミングです。このタイミングで発生するのが、DOMContentedLoadedイベントです。 loadイベントは、ページ内の全リソース(画像、音声、動画など)の読み込み完了時に発生します。そのため、DOMContentedLoadedイベントによりもタイミングが遅くなります。 イベント名 発生タイミング DOMContentLoaded HTMLドキュメントの解析完了時 load 全リソースを読み込み完了時 「ページが表示されたいタイミングで操作をしたい」といった場合、大抵DOMContentedLoadedを使う方が適しています。 scriptタグのdefer属性とDOMContentedLoadedについて scriptタグにdefer属性を設定してJavascriptを読み込むと、スクリプトはHTMLの解析終了後に実行されます。これはDOMContenedLoadedの発生前であるため、defer属性を設定している場合はDOMContentedLoadedによるイベント設定は不要だと考えられます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Js・vue.js】chart.jsでのデータ加工のやり方

こんにちは! 個人的に開発しているvuejs × rails apiのアプリにてグラフを使ったデータ表示を実装した時に躓いたポイントがあったので記事にしたいと思います。 環境 rails 5.2.3 rails 5.2.3 vue.js 2.6.12 参考 【Javascript】配列(オブジェクト)の操作【map/filter/some/reduce】 ActiveRecordで日付ごとにgroup byしたい Railsガイド 躓いたポイント chart.jsの使い方は参考記事がたくさんあったのですが、「じゃあapiで取得したデータをどうやってグラフに渡すの?」ってなり、なかなか参考記事が見つからずに苦労したので... 完成 これがchart.jsを使って表示している棒グラフです やりたい事 1週間の学習時間をDBから取得 1日に複数の学習時間を登録している場合は合計して1つの連想配列にまとめる 学習時間を登録していない日は学習時間を0とする 最終的に取得したデータをchart.jsに渡して棒グラフを表示する はじめにデータを取得 欲しいデータは以下のようなデータです [ {"id":9,"time":1.5,"user_id":1,"created_at":"2021-01-25T12:00:00.000+09:00","day_of_week":1}, {"id":11,"time":2.0,"user_id":1,"created_at":"2021-01-27T09:39:30.000+09:00","day_of_week":3}, {"id":14,"time":0.5,"user_id":1,"created_at":"2021-01-28T07:52:24.000+09:00","day_of_week":4} ] railsのactiveRecordを使って取得します まず今週の日〜土で期間指定します # models/study.rb def self.get_week_chart_data @this_day = Time.now @range = @this_day.all_week(:sunday) self.where(created_at: @range) end これでcreated_atが今週2021/01/24~2021/01/30で期間が指定できます 次にフロントで使うデータを指定して取得します def self.get_week_chart_data @this_day = Time.now @range = @this_day.all_week(:sunday) self.where(created_at: @range) .select(:id, "sum(time) as time", :user_id, :created_at, "dayofweek(created_at) - 1 as day_of_week") end 不要なデータも取ってきてますが気にしない笑 ポイントは、 - sum(time) as timeとして1日に複数回の学習時間(time)を登録している場合もあるので、その合計値を取得する - dayofweek(created_at) -1 as day_of_weekを使って曜日(この場合は曜日の添字である0~6)を取得する mysqlだとdayofweekで取得できる添字が1~7になるので注意 JsではgetDay()を使うと0~6が取得できる なんか揃ってないのが気になるので-1して揃えているだけ 最後に日付ごとにまとめる def self.get_week_chart_data @this_day = Time.now @range = @this_day.all_week(:sunday) self.where(created_at: @range) .select(:id, "sum(time) as time", :user_id, :created_at, "dayofweek(created_at) - 1 as day_of_week") .group("date(created_at)") end これで曜日ごとにグループ化できたので当初取得したかったデータが取得できる このモデルメソッドをコントローラに渡す. ついでにルーティングも設定 # controllers/studies_controller.rb def history histories = current_user.studies.get_week_chart_data // current_userの説明ははしょります. すいません! render json: histories end # config/routes.rb namespace :api, {format: 'json'} do namespace :v1 do get 'histories', controller: :studies, action: :history // RESTfulじゃないのは許して end end 本題 これで下準備が完了. chart.jsにデータを渡す // components/Chart.vue // これは公式の書き方のまま <script> import { Bar, mixins } from 'vue-chartjs'; const { reactiveProp } = mixins export default { extends: Bar, mixins: [reactiveProp], // reactiveProp使わないとデータ更新できないので注意 props: ['options'], mounted () { this.renderChart(this.chartData, this.options) } } </script> // History.vue <template> <div> <Chart class="chart" v-if="loaded" :chartData="chartData" :options="options"/> </div> </template> <script> import Chart from '../components/Chart'; export default { components: { Chart }, data() { histories: [], loaded: false, chartData: { labels: [], datasets: [] }, // これがchart.jsのデータの定義 options: { // ここの書き方はググるとたくさん出てくるので省略 } }, mounted () { this.$http.secured.get('/api/v1/histories') .then(response => { this.histories = response.data this.fillData() // エラー処理は省略 }, methods: { // ここでchartにデータを渡す処理書きます fillData () { this.loaded = false var data = this.arrayData() // ここでデータを加工するメソッドを呼び出しています this.chartData = { labels: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], datasets: [{ data: data }] // 配列の形でデータを渡す必要があります } this.loaded = true } } } </script> これで先程apiで取得したデータをchartに渡せるようになります ただ、まだjsonデータを加工してないのでarrayData()メソッドを書いていきます datasets:[{ data: }]に渡す時は配列にしてデータを渡します なので、jsonでは連想配列を配列にまとめた形でデータを受け取っているのでmapを使って配列に加工します ~省略~ methods: { arrayData () { var getData = this.histories; const times = getData.map(item => item.time) return times } ~省略~ } mapは連想配列のキーを指定する事でvalueだけ取り出してループ処理し配列を作ってくれます const times = getData.map(item => item.time) console.log(times) // => [1.5, 2.0, 0.5] あれ? 本来のデータ通りだと 1/25(月) => 1.5 1/27(水)=> 2.0 1/28(木)=> 0.5 にならなければいけないのに、mapで作ったデータでは 1/24(日) => 1.5 1/25(月) => 2.0 1/26(火) => 0.5 になっちゃってます 確かに配列データを作れたのでdatasets:[{ data: }]に渡せるデータにできましたが、1週間は7日なので、これだと日・月・火のデータになってしまっています... 現状の状態をまとめると、 [0, 1.5, 2.0, 0.5, 0, 0, 0]という配列を作らなくてはいけないのに、[1.5, 2.0, 0.5]という配列ができている状態です mapで作った配列はindex[0]から値を埋めていくので、3つの連想配列から取り出したプロパティはindex[0]~[2]に詰めてセットされてしまっています この問題を解消する為にやりたい事の3つ目学習時間を登録していない日は学習時間を0とする処理をarrayData()メソッドに書きます 考え方 (A) jsonデータのday_of_weekを使って比較用の配列を作成する (B) 1週間は7日なので比較対象の配列を用意する (A)と(B)を比較して(A)が持っていない数字(曜日)を求める 持っていない数字(曜日)番目に{ time: 0 }を突っ込んでやる(無理やり感...) 果たしてこれが良いやり方なのかは甚だ疑問だが、やってみよう データの成形 ~省略~ methods: { arrayData () { var getData = this.histories; // getDataの連想配列数が7つ未満の場合 if(getData.length < 7) { // (A)比較用の配列を作成する // 各連想配列created_atの曜日(添字)を抽出して配列を作成 const arrayDayOfWeek = getData.map(item => item.day_of_week) // => [1, 3, 4] // (B)比較対象の配列を用意する // 1週間7日分の曜日(添字)の数値を格納した配列を作成 const arraySeven = [0, 1, 2, 3, 4, 5, 6] // (A)と(B)を比較して差分を求める // filterメソッドを使って、arrayDay0fWeekが持っていない値を抽出 var resultArray = arraySeven.filter(i => arrayDayOfWeek.indexOf(i) == -1) // => [0, 2, 5, 6]がresultArrayに格納 // このケースだとresultArrayには4つデータが格納されているので4回ループされる // spliceメソッドを使ってgetDataの配列に格納されている[0, 2, 5, 6]番目に{time: 0}を追加 for(var i = 0; i < resultArray.length; i++) { getData.splice(resultArray[i], 0, {time: 0}) }; ↓ 結果 ↓ // 配列index番号の0, 2, 5, 6番目に{time: 0}が追加された(プロパティの数が違うのは気にしない) // getData = [ {time: 0} {id: 9, time: 1.5, user_id: 1, created_at: "2021-01-25T12:00:00.000+09:00", day_of_week: 1} {time: 0} {id: 11, time: 2, user_id: 1, created_at: "2021-01-27T09:39:30.000+09:00", day_of_week: 3} {id: 14, time: 0.5, user_id: 1, created_at: "2021-01-28T07:52:24.000+09:00", day_of_week: 4} {time: 0} {time: 0} ] // } const times = getData.map(item => item.time) return times // chartに渡したい配列を作成できた // => [0, 1.5, 0, 0.5, 0, 0, 0] } ~省略~ } for(var i = 0; i < resultArray.length; i++) { getData.splice(resultArray[i], 0, {time: 0}) }; この処理では、 resultArray[0] // => 配列0番目に格納されている値は[0] getData0番目にspliceメソッドによって{time: 0}が追加 resultArray[1] // => 配列1番目に格納されている値は[2] getData2番目にspliceメソッドによって{time: 0}が追加 をループ処理でresultArray.length分、繰り返します これで目的のデータの成形が完了したのでchartに渡せます chart.jsに成形したデータを渡す // 必要箇所だけ <template> <div> <Chart class="chart" v-if="loaded" :chartData="chartData" :options="options"/> // => chartDataにfillDataメソッドで生成されたデータが渡される // => optionsにdata() {}内で定義したoptionsデータを渡せる(この記事では省略してます) </div> </template> <script> export default { data() { histories: [], loaded: false, chartData: { labels: [], datasets: [] }, }, mounted() { this.$http.secured.get('/api/v1/histories') .then(response => { this.histories = response.data //jsonデータを受け取る this.fillData() // fillDataメソッドを呼び出す }) }, methods: { arrayData () { ~省略~ return times // 返り値 => [0, 1.5, 0, 0.5, 0, 0, 0] }, fillData () { this.loaded = false var data = this.arrayData() // [0, 1.5, 0, 0.5, 0, 0, 0]をdataに格納 this.chartData = { labels: ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], datasets: [ { data: data } ] } // data() {}内で定義したchartData { labels: [], datasets: [] }に生成したデータが格納され, templateタグ内の<Chart ~~/>へマウントされる this.loaded = true } }, } </script> これで記事の最初に貼ったキャプチャの棒グラフが完成しました 最後に もっと効率的な書き方あれば教えて下さい! ちなみに、dayofweek()はmysqlで用意されているメソッドなのでpostgresqlだとエラーします... herokuでデプロイしたらエラーで萎えました... おわり
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【JavaScript】関数のthisキーワードについて

この記事について 初学者の私にとって、JavaScritpのthisキーワードにかなり混乱してしまうので、 とりあえず関数を中心にthisについてまとめておく。 Thisを関数やメソッドに使うと メソッド thisが直感的にわかるメソッドの例から。 メソッドでのthisキーワードは、呼び出し元のオブジェクト(apple)を参照しますね。 なのでapple.priceである150を使用することができます。 method const apple = { price: 150, calcPrice: function() { this.price * 0.9; }, } apple.calcPrice(); arrow関数 独自のthisキーワードは持たないため、呼び出し元の親スコープがthisとなります。 下記の場合、appleはグローバルオブジェクトで、親がwindowになります。 thisは、windowオブジェクトのpriceプロパティを探そうとするのですが、もちろんwindowオブジェクトにpriceプロパティはないので意図する値は返ってきません。 arrow-function const apple = { price: 150, calcPrice: () => console.log(this.price * 0.9), } apple.calcPrice(); 通常の関数 通常の関数内(function declarationやfunction expression)でのthisキーワードは"undefined"となります。(※strict modeの場合) 注意ポイント ①メソッドでは常に呼び出し元オブジェクトを参照する 下の例にあるように、appleのcalcPriceメソッドをbananaオブジェクトにコピーします。 bananaオブジェクトでは、calcPriceメソッドにあるthisキーワードはappleオブジェクトのpriceか、bananaオブジェクトのpriceのどちらを参照すると思いますか?? 答えはbananaオブジェクトです。常に呼び出し元オブジェクトを参照するからです。 copy-method //appleオブジェクト const apple = { price: 150, calcPrice: function() { return this.price * 0.9; }, } console.log(apple.calcPrice()); //result -> 135 //bananaオブジェクト let banana = { price: 200, } banana.calcPrice = apple.calcPrice; // apple.calcPriceメソッドをbananaオブジェクトにコピー console.log(banana.calcPrice()); //result -> 180 ②メソッドの中で書かれていても関数ならundefinedとなる 下の例は、メソッドの中に書かれていようが、関数なのでthisはundefinedとなり、下記は正しく動きません。 function-inside-of-method const apple = { price: 150, calcPrice: function() { //calcPriceメソッド console.log(1000 - this.price); const log = function() { //log関数 console.log(this.price); } log(); //log関数呼び出し }, } apple.calcPrice(); ただこちらは回避する方法が二つあります。 解決法①:thisを別の変数に代入しておく。 this-to-self const apple = { price: 150, calcPrice: function() { console.log(1000 - this.price); const self = this; //thisをselfに代入する(appleオブジェクトがselfとなる) const log = function() { console.log(self.price); //apple.priceと同じ意味になる } log(); }, } apple.calcPrice(); メソッドの中で、thisを別の変数に代入しておけば解決できます。 変数には慣習的にselfや_thisやthatが使われるようですね。 解決法②:arrow関数を使用する arrow-function const apple = { price: 150, calcPrice: function() { console.log(1000 - this.price); const log = () => { console.log(this.price); } log(); }, } apple.calcPrice(); 先程も見たようにarrow関数を使用すると、呼び出し元の親スコープがthisとなります。 log()の呼び出し元=calcPriceメソッドで、メソッドはappleオブジェクトを参照するため、thisはapple.priceとなります。 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

読まれないQiitaを求めてQiitaAPIで遊ぶ

ご存知だろうか などというタグがあることを。私は知らなかった。ということでQiitaAPIを利用して読まれないQiitaタグを発掘した、したい。というおもちゃづくりメモです。技術的には初心者向け。 話の前提 4月に入りQiitaのトップが変わった。 コミュニティが大きくなるにつれ、使ってくれているユーザーも多様になってきており、それに従って投稿される記事もプログラミング言語、技術、思想、コミュニティ、レベル感などが多様になってきていると思います。これ自体は決してこれは悪いことではなく、Qiitaというエンジニアコミュニティがもつ可能性を示してくれていると思っています。 今のQiitaが抱える課題は、多種多様な記事の中から自分がほしい情報を得たり、整理ができなくなってきていることです。 Qiitaが提供しているコンテンツはマス的なものが多く、全てのユーザーの活動を元に1つのコンテンツを作っています。今回のフィード変更は、Qiitaを「ユーザー一人一人が発見、学びを楽しめるプラットフォーム」にすることを目指して行うものです。 なるほど、なるほど。QiitaのこのアップデートからはQiitaの埋もれた良記事発掘という、新たな楽しみ方が提案されたと考えることにします。であればもっとその先を見たいというのが人情です。Qiitaで最も読まれていないタグの記事たちにはどんな原石が転がっているのか。 QiitaAPIを使ってみる とても簡単。Docsは以下。 https://qiita.com/api/v2/docs#get-apiv2tags タグ一覧を作成日時の降順で返します。 sort 並び順 (countで記事数順、nameで名前順) 以下が記事数順、1ページ目 https://qiita.com/api/v2/tags?page=1&per_page=100&sort=count 以下なら作成日時順、100ページ目 https://qiita.com/api/v2/tags?page=100&per_page=100 こんな感じになりますね。 記事数順 作成日時順 作成日時でみるとスパム多いな!...というのは一旦さておいて、 ひとまず記事数順にてページをめくれば 5171位にプリキュア 5711位にさだまさし 7698位、料理。 キャッチーなもの見つかる。 過去記事 同じような思考の人も見つけた。 そしてQiita進化 大衆向けサービスを開発するって大変だなあ。 まとめ 今回の成果物は結局いつものjQueryを使ってしまったので、メモ。 抜粋.html <!DOCTYPE html> <html lang="ja"> <head> <title>Qiita Tags</title> <script src="https://code.jquery.com/jquery-3.6.0.js" integrity="sha256-H+K7U5CnXl1h5ywQfKtSj8PCmoN9aaq30gDh27Xc0jk=" crossorigin="anonymous"></script> <script type="text/javascript"> function jumpPage(){ var page = $("#page").val(); $.getJSON("https://qiita.com/api/v2/tags?page=" +page+ "&per_page=100&sort=count", function(json){ //テーブルクリア $("#tbl").find("tr:gt(0)").remove(); var rows = ""; //テーブルとして表示するため、htmlを構築 for (i = 0; i < json.length; i++) { rows += "<tr>"; rows += "<td>"; rows += i+1+(page*100-100); rows += "</td>"; rows += "<td>"; rows += "<img border=0 src=\""+ json[i].icon_url +"\" width=\"32\" height=\"32\" alt=\"アイコン\"> "; rows += "</td>"; rows += "<td>"; rows += "<a href=\"https://qiita.com/tags/"+ json[i].id +"\" target=\"_blank\">"+ json[i].id +"</a>"; rows += "</td>"; rows += "<td>"; rows += json[i].items_count; rows += "</td>"; rows += "<td>"; rows += json[i].followers_count; rows += "</td>"; rows += "</tr>"; } //テーブルに作成したhtmlを追加する $("#tbl").append(rows); }); $(function() { //fetchボタンのクリック $("#fetch").click(function(){ jumpPage(); }); }); window.onload = jumpPage; </script> </head> <body> <p><label>Qiitaのタグランキング</label></p> <input id="page" type="number" size="10" value="1" max="100" min="1"><input id="fetch" type="button" value="ページ目のデータ取得" /> <table id="tbl"> <tbody> <tr> <th>順位</th><th>アイコン</th><th>タグ</th><th>記事数</th><th>フォロワー数</th> </tr> </tbody> </table> 以上Qiitaさんの中の人にねぎらいの念をこめつつ遊んだ記録。 お楽しみいただければさいわいです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[React Native] stateの更新と再描画のタイミング

0. 目的 stateの更新と再描画のタイミングについて調査する 1. 環境 React : 16.8.6 React Native : 0.63.4 2. ソースコード test_1 function App() { const [val_1, setVal_1] = useState('A'); const [val_2, setVal_2] = useState('A'); const [val_3, setVal_3] = useState('A'); useEffect(() => { setVal_1('B'); setVal_2('B'); setVal_3('B'); }, []); console.log('val_1 : ' + val_1); console.log('val_2 : ' + val_3); console.log('val_3 : ' + val_3); return ( <> </> ); } 実行結果_1 LOG val_1 : A LOG val_2 : A LOG val_3 : A LOG val_1 : B LOG val_2 : B LOG val_3 : B ⇒ ブロック内で複数のstateを更新した場合、最後のstateが更新されたタイミングで再描画される test_2 function App() { const [val_1, setVal_1] = useState('A'); useEffect(() => { setVal_1('B'); setVal_1('C'); setVal_1('D'); }, []); console.log('val_1 : ' + val_1); return ( <> </> ); } 実行結果_2 LOG val_1 : A LOG val_1 : D ⇒ ブロック内で同じstateを複数回setした場合、最後にsetした値で更新、再描画される(同期的) test_3 function App() { const [val_1, setVal_1] = useState('A'); const getStr = async (str) => { return str; }; useEffect(() => { async function init() { let val_1 = await getStr('B') setVal_1(val_1); setVal_1('C'); setVal_1('D'); } init(); }, []); console.log('val_1 : ' + val_1); return ( <> </> ); } 実行結果_3 LOG val_1 : A LOG val_1 : B LOG val_1 : C LOG val_1 : D ⇒ async functionを実行すると、全てのstateの更新時に再描画される
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

QR忘れ物アプリ (1) 環境構築 [Vue,Vuetify,Firebase]

はじめに Vue + Firebaseの記事が少なく手間取ったため、のちにプロジェクトを作るときの記録として残しておきます。 環境 Windows 10 WSL1 (ビルド、エミュレーター用) Node 10 Visual Studio Code Google Chrome UI フレームワーク Vue 2(Vue 3はVuetifyに未対応(2021/4 現在)) Vue CLI v4.5.12 Vuetify Firebase 8.3.1 Firebase CLI Firebase Auth Firebase Auth UI Firebase Realtime Database (RTDB) Firebase Hosting Firebase Functions Firebase Emulator Suite Auth, RTDB, Hosting, Functions Vue環境構築 node 10のインストール aptではnode 10はインストールできないので、nvmを使う1 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bash nvm install 10 nvm use 10 Vueのインストール2 npm install vue Vueプロジェクトをqr-lost-and-foundのディレクトリに作る vue create qr-lost-and-found # Preset ->Manually select features # Features -> Babel, Router, Vuex, Linter # Vue.js version -> 2.x # history mode for router -> Yes # Use history mode for router -> Yes # Linter / formatter config -> Prettier # Additional lint features -> Lint on save # Config location for Babel, ESLint -> In dedicated config files # Save this as a preset -> N Gitのinitial commitを行う cd qr-lost-and-found git config --global user.email {your email} git config --global user.name {your username} git add -A git commit -m "initial commit" Vuetifyを追加 vue add vuetify # preset -> Default Vueの設定変更 vue.config.jsにpublicPathを追加して、ローカルのindex.htmlファイルをクリックして閲覧できるようにする。3 vue uiからも設定の変更可能。その場合、vue uiコマンドで開いて、プロジェクト設定>Vue CLI>公開パスを./にする。 module.exports = { transpileDependencies: ["vuetify"], publicPath: './' }; Build 正常にインストールできているかを確認するためにビルドする。 npm run build ./dist/index.htmlをブラウザで開いてVuetifyの画面が表示されることを確認する。 Firebase環境構築 Firebaseプロジェクト作成 二番煎じになるので、詳しくは書きません。Firebaseプロジェクト作り方を参考に作ってください。 Firebase CLIをインストール Firebase CLIを使い、プロジェクトのテンプレートを作ることができます。 npm install firebase-tools Firebaseのアカウントにログインし、プロジェクトを初期化する。 firebaseはFirebase CLIのコマンドです。 通常の設定からの変更点としてはPublic Directoryをdistに設定にしていることが挙げられます。これは、Vueのビルド先がdist であるためです。 firebase login firebase init # Features: Database, Functions, Hosting, Emulator # Project: Use an existing project # Select project: Firebaseで自分の作ったプロジェクトを選択 # Database security rules: Enter # Language: Javascript # ESLint: No # Install dependencies now: Y # Public directory: dist # Configure as SPA: N # Automatic build and deploy: N # Overwrite public/index.html: N # Emulators setup: Authentication, Functions, Database, Hosting # Port for auth: Enter # Port for functions: Enter # Port for database: Enter # Port for hosting: Enter # Enable Emulator UI: Y # Port for Emulator UI: Enter # Download the emulators now: y Firebase SDKをインポート public/index.html内に登録します。 ?useEmulator=trueになっており、Emulatorを使う設定になっています。 <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width,initial-scale=1.0"> <!-- update the version number as needed --> <script src="/__/firebase/8.3.1/firebase-app.js"></script> <!-- include only the Firebase features as you need --> <script src="/__/firebase/8.3.1/firebase-auth.js"></script> <script src="/__/firebase/8.3.1/firebase-database.js"></script> <script src="/__/firebase/8.3.1/firebase-functions.js"></script> <!-- initialize the SDK after all desired features are loaded, set useEmulator to false to avoid connecting the SDK to running emulators. --> <script src="/__/firebase/init.js?useEmulator=true"></script> </head> <body> <div id="app"></div> <!-- built files will be auto injected --> </body> </html> Build 再びテストのためビルドします。先ほどのビルドより時間がかかります。 npm run build ./dist/index.htmlをブラウザで開いて先ほどと同じくVuetifyの画面が表示されることを確認します。 次回 Firebase Auth + Firebase Auth UIを用いてログインページを作ります。 コメント Firebase + Vueの組み合わせの設定方法で参考にしたリンク: https://qiita.com/fukuchan-senpai/items/4ea6cd97c196689d152e QRコードは(株)デンソーウェーブの登録商標です https://askubuntu.com/questions/1273438/how-to-install-node-js-version-10-on-ubuntu-18-04 ↩ https://vuejs.org/v2/guide/installation.html ↩ https://github.com/vuejs/vue-cli/issues/5162 ↩
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript filter()の参照値でハマった話

だいぶハマったので、備忘兼ねて投稿します。 とある開発で、配列の特定の要素を変更する処理を実装する場面がありました。 JS初心者のわたしはググった結果どうやらfilter()が使えそうだと思い、以下のうように実装しました。 const users = [ { id: 1, name: "sato", }, { id: 2, name: "suzuki", }, ] const user = users.filter((user) => { return user.id === 1 }) user[0].name = "tanaka" console.log(users) // [ { id: 1, name: 'tanaka' }, { id: 2, name: 'suzuki' } ] おや、最初に定義したusersが書き換わってる! ふむ、filterは配列の参照値受け継ぐのね。了解。 しばらくして、似たような処理を実装する場面があったので、今度はこのように実装。 const users = [ { id: 1, name: "佐藤", }, { id: 2, name: "鈴木", }, ] const user = users.filter((user) => { return user.id === 1 }) user[0] = { id: 3, name: "田中" } console.log(users) // [ { id: 1, name: '佐藤' }, { id: 2, name: '鈴木' } ] あれ?書き換わってない、、、!? ドキュメントをよく見てみると、filter()は「新しい配列を生成」しているみたいです。 filter() メソッドは、与えられた関数によって実装されたテストに合格したすべての配列からなる新しい配列を生成します。 今回の場合、配列の値であるオブジェクト自体は参照値を保持していたようで、元の変数が書き換わったのかなと思います。 おわり。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Canvas要素の使い方

ご注意! この記事は学生の勉強備忘録です。 曖昧な表現や、誤りが含まれる可能性があります。 閲覧いただく際は、あくまで参考程度に… また、修正やご意見などはドシドシいただけると大変うれしいです。 宜しくお願い致します。 Canvas要素とは Canvas要素とは、HTML5で追加されたウェブページ上でグラフィックスを描くための要素です。 従来、図形などを表示させたい場合、JPEG等の画像を埋め込むか、Flashなどを使っていた表現をHTMLとJavaScriptを用いて表示させることが出来ます。 Canvas要素の基本的な使い方 HTML <canvas id="ID" width="200" height="200"></canvas> まずは、HTMLでCanvasを用意します。 idは自由に変更して記入してください。 キャンバスの大きさは必要であれば記入してください。 次にJavaScriptです。 JavaScript var canvas = document.getElementById( "ID" ) ; var context = element.getContext( "2d" ) ; context.beginPath () ; この3行はほぼセットで必ず書くものとして覚えておいて大丈夫です。 一行目でcanvas要素のDOMオブジェクトをdocument.getElementByID()メソッドを用いて取得します。 二行目では、getContext()メソッドを用いて、canvasに描いていく準備としてコンテキストの参照を取得します。引数にある“2d”は、平面図形のことを指します。現状、3dなど他の値の指定はできません。 三行目のbeginPathメソッドは、主に複数の図形を描画するときに使用します。 「それまでのパスをリセットして新たにパスを始めますよ~」というような意味になります。 その為、2つ目の図形を描く際に、1つ目に書いたパスの影響を受けないようにするために書くものという感じです。 実際に試してみてみたい方はコチラ ➡ w3c scools 『HTML canvas beginPath() Method』 (↑2つ目の図形のbeginPathメソッドをコメントアウトすると、1つ目の図形も、2つ目の図形の色になります。) では、ここから実際に図形を描いてみましょう。 四角形の描画 HTML <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <script type ="text/javascript" src = "practive.js"></script> <title>Canvas要素を使って図形を描いてみよう</title> </head> <body> <canvas id = 'ID' width = "600" height ="200"></canvas> </body> </html> JavaScript window.onload = function(){ var canvas = document.getElementById('ID'); if(canvas.getContext){ var context = canvas.getContext('2d'); //塗りつぶしの四角形 context.fillRect(50,50,100,100); //線画の四角形 context.strokeRect(200,50,100,100); //塗りつぶしと線で色の変える四角形 context.beginPath(); context.rect(350,50,100,100); context.fillStyle = "yellow"; context.fill(); context.strokeStyle = "red"; context.lineWidth = 3; context.stroke(); } } 結果↓ ※if(canvas.getContext){ は、ブラウザがcanvas要素に非対応だった場合の実行を事前に防止するためのものです。 ※window.onload = function(){ は、HTMLを実行してからJSを実行するためのコードです。 ☆ 塗りつぶしの図形描画のメソッド:fillRect(X,Y,幅,高さ) 初期値では、塗りつぶしや線の色は#000(black)になります。 ※X,Y = 左上からの位置座標 ☆ 線だけの図形描画のメソッド:strokeRect(X,Y,幅,高さ) ☆ 四角形を作るメソッド:rect(X,Y,幅,高さ)  ↑このメソッド単体だと、透明なままで可視化することが出来ません。  strokeメソッドやfillメソッドを併用して、描画していきます。  それぞれ、strokeStyleやlineWidth、fillStyleを指定することで、  塗りつぶしと線の色が異なる四角形を描画することが出来ます。 多角形の描画 JavaScript context.moveTo(100,50); context.lineTo(20,130); context.lineTo(180,130); context.closePath(); context.fillStyle="rgba(0,0,255,0.1)"; context.fill(); context.strokeStyle = "rgb(0,0,0)"; context.stroke(); 結果↓ ☆最初の点の位置 : moveTo(X,Y) ☆2つ目、3つ目(ect)の点の位置 : lineTo(X,Y) ☆始点と終点を繋いでパスを閉じる : closePath() 円弧の描画 JavaScript context.arc(100,100,70,0,2*Math.PI); context.stroke(); context.beginPath(); context.arc(270,100,70,0,1*Math.PI); context.stroke(); context.beginPath(); context.arc(440,100,70,0,1*Math.PI,true); context.stroke(); 結果↓ ☆円弧の描画 : arc(X,Y,半径,円弧の開始地点,終了地点,時計回りor反時計回り) ※座標は、中心点の位置座標。 ※第5引数を2*Math.PIに指定することで円を描画できる。 ※第6引数は、指定しない場合「時計回り(false)」となる。  反時計回りにしたい場合は、「true」を指定する。 ◎参考:MDN『CanvasRenderingContext2D.arc()』 その他の使用方法について 簡単な図形は上記のメソッドを組み合わせることで描画できます。 ベジェ曲線など、ほかにも沢山あるCanvas要素の使い方は以下のサイトを紹介させていただきます… 参考:HTMLリファレンス『canvas』 おわりに ここまで読んでくださりありがとうございました。 ご指摘や参考サイト、アドバイスなどコメントもお待ちしております。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsアプリに非同期通信のコメント機能を実装

はじめに Railsアプリに非同期通信のコメント機能を実装しました。 非同期通信は一言で言えば、画面遷移をせずにページを更新する技術です。 備忘録として手順をまとめたいと思います。 実装イメージ 開発環境 macOS Catalina Ruby 2.6.5 Ruby on Rails 5.2 目次 1.モデルとアソシエーション 2.ルーティング 3.コントローラー 4.view 1. モデルとアソシエーション モデル構成とアソシエーションはよくある構成です。 前提としてuserとpostモデルは作成済みとします。 commentモデル作成 $ rails g model comment user:references post:references content:string $ rails g migrate アソシエーション user.rb has_many :posts, dependent: :destroy has_many :comments, dependent: :destroy post.rb belongs_to :user has_many :comments, dependent: :destroy comment.rb belongs_to :user belongs_to :post # バリデーション(カラの入力を無効に)  validates :content, presence: true 2. ルーティング コメントは投稿に紐付いているのでルーティングはネストさせて記述します。 ネストさせることでアソシエーション先のレコード(今回で言えば、投稿に紐づくコメント)のidをparamsに追加してコントローラーにわたすことができるようになります。 routes.rb resources :posts, only: [:index, :new, :create, :show, :destroy] do resources :comments, only: [:create, :destroy] end 3. コントローラー まずはcommentのコントローラーを作成します。 $ rails g controller comments 次にcreateとdestroyアクションを定義します。 comments_controller.rb comments_controller.rb class CommentsController < ApplicationController def create @comment = Comment.create(comment_params) respond_to do |format| if @comment.save format.html { redirect_back(fallback_location: root_path) } # 前のページに遷移 format.js # create.js.erbが呼び出される else format.html { redirect_back(fallback_location: root_path) } # 前のページに遷移 end end end def destroy @post = Post.find(params[:post_id]) @comment = current_user.comments.find_by(post_id: @post.id) @comment.destroy redirect_back(fallback_location: root_path) end private def comment_params params.require(:comment).permit(:content).merge(user_id: current_user.id, post_id: params[:post_id]) end end Comment.create(comment_params) ・comment_paramsではmergeメソッドでuser_idとpost_idをcommentテーブルのレコードに格納します。 respond_to do |format| ・処理の結果をHTML形式で返すかJS形式で返すかを分岐させます。 ・今回はコメントが保存されたらJS形式で返すように設定します。format.jsと記述することでcreate.js.erbというファイルを返します。 redirect_back(fallback_location: root_path) ・もしJS形式で返せなかった場合は同期通信でコメントを作成します。その際redirect_backでコメント作成ページにとどまることができます。fallback_location: root_pathはエラーが起きた際にroot_pathに遷移するという記述です。 posts_controller.rb viewで反映させるためにposts_controller.rbを編集します。 posts_controller.rb def show @post = Post.find(params[:id]) @comment = Comment.new @comments = @post.comments.all end 4. View 非同期通信をさせるためにcreate.js.erbと_comment.html.erbという部分テンプレートを作成します。 一例ですがディレクトリ構成は以下のようになります。 views |-posts | |-show.html.erb |-comments |-_comment.html.erb |-create.js.erb 投稿詳細ページは以下のようになります。 posts/show.html.erb <% if @comments %> <div class="commentOutline"> <%= render partial: "modules/comment", locals: { comments: @comments }%> </div> <% else %> <p>コメントはまだありません</p> <% end %> <div class="bottomInput"> <%= form_with(model: [@post, @comment], id: "new-comment") do |f| %> <%= f.text_field :content, class: "inputComment" %> <%= f.submit "コメントする", class: "submitComment" %> <% end %> </div> renderメソッドで_comment.html.erbを呼び出します。更にlocalsオプションでposts_controller.rbで定義した@commnetsの変数をcommentsとして部分テンプレート内で使用できるようにします。 form_withはデフォルトでremote: tureになっているのでこれでJS形式でレスポンスすることができます。 部分テンプレートは以下の通りです。 comments/_comment.html.erb <% comments.each do |comment| %> <li> <div class="topPosition"> <%= link_to user_path(comment.user.id), class: "commentUserLink" do %> <% if comment.user.icon? %> <%= image_tag comment.user.icon.url, class: "commentUserIcon"%> <% else %> <i class="fas fa-user-circle"></i> <% end %> <p class="commentUserName"><%= comment.user.nickname %></p> <% end%> <% if comment.user_id == current_user.id %> <%= link_to post_comment_path(comment.post_id, comment.id), method: :delete, class: "deleteCommentLink" do %> <i class="fas fa-trash"></i> <% end %> <% end %> </div> <div class="bottomPosition"> <p class="commentContent"><%= comment.content %></p> <p class="commentDatetime"><%= comment.created_at.strftime('%Y/%m/%d') %></p> </div> </li> <% end %> 最後にcreate.js.erbを編集します。 comments/create.js.erb $(".commentsArea").html("<%= j(render 'comments/comment', { comments: @comment.post.comments }) %>") $(".inputComment").val(''); 1行目でコメントが作成されたらcommentsAreaに部分テンプレートの内容を更新するという記述をしています。 2行目はコメントを入力するinputエリアの値をリセットする記述です。なお記述を簡略にするためにjQueryで記述しています。 以上で非同期でのコメント機能が実装できていると思います!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む