- 投稿日:2020-07-04T23:29:13+09:00
Vue.js における Component 間のデータの受け渡しまとめ
Overview
Vue.js を使っていて、この Component 間の関係だとどうやって data 渡すんだっけな...?
となることが多いので方法はいろいろあると思いますが、自分がよくやるやり方をこの記事にまとめておきます。
(他にもこんなやり方あるよ!という知見ありましたら教えていただけますと幸いです )親 → 子
親 Component から 子 Component へのデータの受け渡しは、
親 Component で data を v-bind して、子 Component で props で data を引き取ります。Sample Code
- Parent.vue ( v-bind で data を渡す)
<template> <div id="parent"> <child v-bind:messageFromParent="this.message" /> </div> </template> <script> import Child from './Child' export default { name: 'Parent', data () { return { message: 'Hello from Parent!' } }, components: { child: Child }, } </script>
- Child.vue ( props で引き取る)
<template> <div id="child"> {{ messageFromParent }} </div> </template> <script> export default { name: 'Child', props: ['messageFromParent'] } </script>子 → 親
逆に、子 Component から 親 Component への data の受け渡しは
親 Component で v-on でイベントハンドラを定義しておき、子 Component では $emit で data を持たせて 親 Component で提起しているイベントを発火させます。※ emit : 放つ、放射する
Sample Code
- Parent.vue ( v-on でイベントハンドラを定義する)
<template> <div id="parent"> <child v-on:sendMessage="receiveChildMessage" /> {{ this.childMessage }} </div> </template> <script> import Child from './Child' export default { name: 'Parent', data () { return { childMessage: '' } }, components: { child: Child }, methods: { receiveChildMessage (message) { this.childMessage = message } } } </script>
- Child.vue ( $emit で data を持たせつつ、親のイベントを発火する)
<template> <div id="child"/> </template> <script> export default { name: 'Child', data () { return { message: 'Hello from Child!' } }, created () { this.$emit('sendMessage', this.message) } } </script>子A → 子B
これは実装の方針が人によって分かれそうなのですが、自分は 2つ以上の Component にまたがって data の受け渡しを行う場合 (子A → 親 → 子B 、 親 → 子 → 孫 など) Vuex を使うことが多いです。
※ Vuex についの詳細はこちらをご参照ください。
https://vuex.vuejs.org/ja/Sample Code
- store.ts
import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) interface State { message: string } const initState: State = { message: '' }; export default new Vuex.Store({ state: initState, mutations: { setMessage(state, { message }: { message: string }) { state.message = message; } } })
- Parent.vue ( store を import する)
<template> <div id="parent"> <child-a /> <child-b /> </div> </template> <script> import store from './store' import ChildA from './ChildA' import ChildB from './ChildB' export default { name: 'Parent', store, components: { child-a: ChildA, child-b: ChildB }, } </script>
- ChildA.vue ( Vuex に data を store する)
<template> <div id="child-a"/> </template> <script> export default { name: 'ChildA', created () { this.$store.commit('setMessage', { message: 'Hello from ChildA!' }); } } </script>
- ChildB.vue ( Vuex から data を参照する)
<template> <div id="child-b"/> {{ this.message }} </template> <script> export default { name: 'ChildB', data () { return { message: '' } }, created () { this.message = this.$store.state.message } } </script>まとめ
- 親 Component → 子 Component
(親) v-bind:'value'
→(子) props['value']
- 子 Component → 親 Component
(子) this.$emit('childHandler', value)
→(親) v-on:childHandler="parentHandler"
- 子 Component A → 子 Component B
(子A) Vuex に data を store
→(子B) Vuex から data を参照
- 投稿日:2020-07-04T22:41:30+09:00
[JavaScript]エラーをthrowする場合はちゃんとカスタムエラーを作ろう
エラーをthrowする際は、カスタムエラーを定義してあげましょう。
例えば、意図して処理をスルーさせている場合は、コメントではなくカスタムエラーでやったほうがより良いコードとなります。(併用もOK)例
class GetUser404Error extends Error {}; function init() { try { const user = getUser(123): // ... } catch(e) { if (e.status === 404) { throw new GetUser404Error(); } throw e; } } try { init(); // ... } catch(e) { // データが取得できない場合は、エラーを表示せずに処理を終える if (e instanceof GetUser404Error) { return; } errorDialog(e.message); }参考
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Error
- 投稿日:2020-07-04T22:32:33+09:00
【JavaScript】FileReaderをasync/awaitでエレガントに使いたい
こんにちは。
皆さんもFileReaderを使う機会は多いと思います。
FileReaderは非同期なので、コールバック地獄に陥りがちです。しかし最近のJavaScriptには、非同期なコードを同期的に扱えるasync/awaitという素晴らしい機能が存在します。
FileReaderもasync/awaitの恩恵を受けたい!!
解決策
class FileReaderEx extends FileReader{ constructor(){ super(); } #readAs(blob, ctx){ return new Promise((res, rej)=>{ super.addEventListener("load", ({target}) => res(target.result)); super.addEventListener("error", ({target}) => rej(target.error)); super[ctx](blob); }); } readAsArrayBuffer(blob){ return this.#readAs(blob, "readAsArrayBuffer"); } readAsDataURL(blob){ return this.#readAs(blob, "readAsDataURL"); } readAsText(blob){ return this.#readAs(blob, "readAsText"); } }元のFileReaderクラスをPromiseでラップした拡張クラスを作成して万事解決。
#readAs()
は外部から見られたくないのでプライベートメソッドにしてあります。ビフォーアフター
今までコールバックにまみれていたコードも...
Beforeconst reader1 = new FileReader(); reader1.addEventListener("load", ()=>{ const buffer1 = reader1.result; const reader2 = new FileReader(); reader2.addEventListener("load", ()=>{ const buffer2 = reader2.result; const reader3 = new FileReader(); reader3.addEventListener("load", ()=>{ const buffer3 = reader3.result; }); reader3.addEventListener("error", ()=>{ alert(reader3.error.message); }); reader3.readAsArrayBuffer(blob3); }); reader2.addEventListener("error", ()=>{ alert(reader2.error.message); }); reader2.readAsArrayBuffer(blob2); }); reader1.addEventListener("error", ()=>{ alert(reader1.error.message); }); reader1.readAsArrayBuffer(blob1);async/awaitの手に掛かればこの通り!!
After(async()=>{ const buffer1 = await new FileReaderEx().readAsArrayBuffer(blob1); const buffer2 = await new FileReaderEx().readAsArrayBuffer(blob2); const buffer3 = await new FileReaderEx().readAsArrayBuffer(blob3); })();一目瞭然で見やすくなっています。
FileReader以外にも応用が利く、かなりオススメな手法です。
- 投稿日:2020-07-04T22:21:41+09:00
よく耳にするReactを自分なりにまとめてみた...
自己紹介
むちゃんです。
関西を拠点に活動しているフロントエンドエンジニアです。HAL大阪の2回生です (2020.7.4現在)
イベントなど回っているので是非大阪辺りの方は会いましょう!
1...REACTとは?
Facebook社が開発したWebのUIを作るためのJavascriptのライブラリ。
[公式ドキュメント]:https://ja.reactjs.org/
2...コンポーネントとは?
[見た目]+[機能]を合わせた物。
- 見た目(View)
- 機能(controller)
Webの構造(コンポーネントツリー)
・オブジェクトの階層構造のこと
なぜコンポーネントを使うのか?
- 再利用をするため
- 分割統治するため(お互いの依存関係をなくす)
- 変数に強くなるため
コンポーネントの種類
・Functional Component(ファンクショナルコンポーネント)
3...DOMについて
そもそもDOMって何?
〜Document Object Model (DOM)=要はインターフェース
HTMLにアクセスする窓口みたいな物
構造や見た目、コンテンツを変更Virtual DOMとは
〜Reactが管理しているDOM
ブラウザのレンタリングと別管理
->効率よくDOM操作をできる・通常のDOM操作
document.getElemntById('hoge').innerText='hoge'・ReactのVirtualDOM操作
render( <div id="hoge">hoge</div> )JSXについて(トランスコンパイル)
〜Javascropt内でHTMLっぽくる
ReactDOM.render( <div ClassName={hoge}> <h1>HelloWorld</h1> </div> )4...JSXの基本文法での注意点!!
・Reactのパッケージインストールが必要
import React from "react"・ClassはClassNameと記述
・1つで完了するタグは終了タグをつける
あとがき
今回は自分なりにReactについて簡潔にまとめてみした
次回はstateやライフサイクルについてより詳しくまとめてみたいと思います。
Twitter @HomgMuchan ぜひフォロー待っています❗️
- 投稿日:2020-07-04T22:21:12+09:00
コマンドラインからjavascriptでレンダリングされたHTMLソースを取得する方法
curl
からは、javascriptを有効にしたブラウザと同じHTMLソースを取得することが難しいことがあります。このような場合、phantomjs
を使用すると便利です。$ npm i -g phantomjs phantom公式サイトにてバイナリも配布されてるので、
node
からのインストールが依存関係などで失敗する場合、こちらをダウンロード、解凍して実行権限を与えると良いでしょう。https://phantomjs.org/download.html
$ curl -sLO https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-2.1.1-linux-x86_64.tar.bz2 $ aunpack phantomjs-2.1.1-linux-x86_64.tar.bz2 $ cd phantomjs-2.1.1-linux-x86_64/bin/ $ chmod +x phantomjs $ ./phantomjs --versionset.jsvar system = require('system'); var page = require('webpage').create();argument var url = system.args[1]; page.open(url, function () { console.log(page.content); phantom.exit(); });$ phantomjs set.js https://google.com
- 投稿日:2020-07-04T19:40:41+09:00
rails s 実行時のCould not find a JavaScript runtime.のエラー対応方法
macOSのバージョンを上げてしまい、開発中のRailsアプリのローカル環境でローカルサーバーが起動出来なくなり、以下のようなエラーが発生したため、忘備録のために投稿します。
macOS Catalina バージョン 10.15.5
環境:Ruby,Ruby on Rails,JavaScriptエラー内容
Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable)解決方法
Node.jsの最新版をインストールする。
以上です。これだけでした。
- 投稿日:2020-07-04T17:37:20+09:00
webpack.config.js の書き方をしっかり理解しよう
webpack.config.js
僕の中で少し苦手なイメージのある
webpack
について、改めて向き合って理解していこうと思います。
webpack
に苦手意識がある人のほとんどは、このwebpack.config.js
について理解していない人がほとんど。なので、今回は下記のwebpack.config.js
について丁寧に説明していきます。webpack.config.jsconst path = require('path') const webpack = require('webpack') module.exports = { entry: { index: path.join(__dirname, 'src', 'index.js') }, output: { path: path.join(__dirname, 'out'), filename: 'main.js' }, devtool: 'cheap-module-eval-source-map', target: 'node', module: { rules: [ { test: /.js$/, loader: 'babel-loader' } ] }, plugins: [ externalPlugins ] }
entry
とoutput
結論から言うと、
entry:
は、webpackがビルドする際に開始点となるJSファイルを設定しています。
output:
は、ビルドファイルの設定。どこにどのようなファイルを出力すればよいか設定しています。具体的にみていきます。
webpack.config.jsconst path = require('path') const webpack = require('webpack') module.exports = { entry: { index: path.join(__dirname, 'src', 'index.js') }, output: { path: path.join(__dirname, 'out'), filename: 'main.js' }, ... }
entry
の設定早速説明していきます。
簡単に理解できますので、安心してください(笑)ここでは、webpackがビルドした際に
「./src/index.js
というファイルを探して読み込んでね。」という設定をしています。まず
path.join
でパスを連結してくれていて、上記だと./src/index.js
というパスにしてくれています。これを使うメリットとしては、
macやwindowsなどの開発環境によって/
が¥
になったりするんですけど、それを防ぐことができます。
そしてNode.js
の標準モジュールのpath
を読み込ませて使用しているので、path.join
として使用できています。
__dirname
はNode.js
で用意されているグローバル変数で、現在実行中のソースコードが格納されているディレクトリパスが格納されています。簡単に言うと、手軽に絶対パスの記述ができるものです。
output
の設定
path:
とfilename:
がありますね。こちらも結論から言うと
path:
は、どのディレクトリに出力するか指定しています。
filename:
は、どのファイルに出力するか指定しています。なので、
「./src
というディレクトリ内のmain.js
というファイルに出力してね。」という設定をしています。
devtool
まず、結論です。
devtool:
を設定することによって、ソースマッピングのスタイルを選択して、デバッグプロセスを強化します。指定可能な値はstring
とfalse
です。
(参照:公式ドキュメントのdevtool)分からない用語が出てきた?ソースマッピングですかね?
分からなければ、しっかり調べていきましょう。
ソースマップについて
ソースマップとは、Babelなどトランスパイル後と前のコードの内容を紐付けしてデバッグしやするするもの。
トランスパイルについて
JavaSciprtではwebpackでコンパイルなどをする時に同時にトランスパイルという処理を行います。
具体的に何をしているのかというと、互換性のあるコードに変換をする処理。
(例)「CoffeeScriptとかを記載して、Javascriptへトランスパイルする。」みたいな感じです。
一般的にはBabelというライブラリを使います。
devtool: 'cheap-module-eval-source-map'
まとめると、ここでは
'cheap-module-eval-source-map'
を設定しており、公式ドキュメントによると、ビルドと再ビルトの処理速度としては遅めに処理されるようです。webpack.config.js... module.exports = { ... devtool: 'cheap-module-eval-source-map', ... }
target
結論から言うと、
target:
を設定することによって、特定の環境をターゲットにするようにwebpackに指示してくれます。ここでは、'node'
と設定しているので、Node.js
のような環境で使用するためにコンパイルしてます。webpack.config.js... module.exports = { ... target: 'node', ... }
module
module:
オプションは、プロジェクト内のさまざまなタイプのモジュールがどのように処理されるかを設定しています。「webpack が特定のmodule
をどう扱うか」を決めるってことです。webpack ではmodule
はJavaScript
やCSS
などのファイルを指す。
ここでは、Rule
モジュールについて設定されているので、詳しくみていきます。webpack.config.js... module.exports = { ... module: { rules: [ { test: /.js$/, loader: 'babel-loader' } ] }, ... }
Rule
まず結論から、
rule:
を設定すると、「条件」、「結果」、「ネスト」されたルール3つの部分に分けることができます。
Rule.test
公式ドキュメントによると、テストアサーションに合格したすべてのモジュールを含めます。 Rule.testオプションを指定した場合、Rule.resourceも指定できません。
と書いてありましたが、ちょっと「??」のままでした。いろいろ調べてみた結果を簡単にいうと、
「Rule.test
で拡張子を設定する」というイメージです。
webpackでいうmodule
(JavaScript
やCSS
など)を設定するってことです。
今回で言うと、拡張子が.js
なので、test: /.js$/
と設定しています。このまま一気に説明した方が理解がしやすいと思うので、一気に説明していきます。
Rule.loader
この2つですが、簡単にいうと
まずloader: 'babel-loader'
と設定することで、test: /.js$/
で設定したjsファイル
を'babel-loader'
として読み込む設定をしています。
plugins
最後
plugins:
です。
pluginsオプションは、さまざまな方法でwebpackビルドプロセスをカスタマイズするために使用されます。externalPlugins
というプラグインがカスタマイズされています。公式ドキュメントによると、
webpackには豊富なプラグインインターフェイスがあります。 webpack自体のほとんどの機能は、このプラグインインターフェイスを使用します。これにより、Webpackが柔軟になります。最後
最後の方は駆け足で説明することになりましたが、少しでも理解の手助けになれば幸いです。
こんなことを最後にいうことではありませんが、webpackについては公式ドキュメントに結構丁寧に説明が書いてあるので、そこをみれば理解できるかと思います。何か間違い等ありましたら教えていただけると幸いです。
- 投稿日:2020-07-04T17:12:02+09:00
【Vue】Vue開発で使えるいくつかの小技
Vueまとめパート4(小技集)
こちらの記事は、Adnan Babakan 氏によりDev.to上で公開された『 Vue advanced tricks cheat sheet 』の邦訳版です(原著者から許可を得た上での公開です)
DEV.toコミュニティの皆さん、こんにちは!
普通にVueで開発をしていると、解決するべき多くの問題に遭遇しますが、開発者にとって長時間苛立たせる問題を解決することほど気持ちいいことはないなと感じています!
ここで、私はいくつかの問題を解決するために使用したいくつかの小技を書いています。これを読んでいるあなたにもいつか役立つかもしれません。
vm.$forceUpdate()
- 強制的再レンダリング場合によっては仮想DOMの一部の変更を反映できないことがあるため、このような場合ではVueにコンポーネントを再レンダリングさせる必要がある。
このような場合には他にも
v-if
を使うなどいくつか方法があるが、forceUpdate
メソッドを使うのが正しいとされている。export default { methods: { forceUpdateMyComponent() { this.$forceUpdate() }, }, }Vueインスタンスでこのメソッドを使用するためには
$
をメソッド名の前につける必要があることに注意。使ってみるとわかるがどの算出プロパティも更新されず、コンポーネントのビューが強制的に再レンダリングされるだけだ。
ネストされたデータの監視
場合によっては、ネストされたデータを監視したい場合もあるだろう。このような場合は
.
チェーンで実現できる。export default { data() { return { myInfo: { firstName: 'Adnan' } } }, // `myInfo.firstName`とつなげることで監視プロパティにできる watch: { 'myInfo.firstName'() { console.log('Here') } } }上記のコードは、オブジェクト内の監視するプロパティどれかがわかっている場合に使える。
しかし、オブジェクト全体とその値を監視したい場合はどうすればいいか?
その場合は次のようにする。export default { data() { return { myFruits: {apple: 'red', banana: 'yellow'} } }, methods: { changeIt() { this.myFruits.apple = 'green' } }, watch: { myFruits: { // `deep`, `handler`プロパティを追加する deep: true, handler() { console.log('Fruits updated!') } } } }監視プロパティを関数としてではなくオブジェクトとして定義し、
deep
キーとその値をtrue
にセットする。これによってオブジェクト全体を監視でき、handler
と呼ばれる関数内にデータが変更されたときに発生させたいことを書くことができる。v-modelをサポートするカスタム(子)コンポーネント
ご存じのとおり
v-model
はコンポーネント(input要素)とデータプロパティを介した双方向バインディングを実現するために使われる。カスタム(子)コンポーネントを作成し、
v-model
をサポートさせたい場合は、input
キーワードを発行し、最初にコンポーネントに渡されるものをv-model
で受け取るために、value
と呼ばれるpropを取得する必要がある。より理解しやすくするために、次のコードを見てみよう。
<template> <div> <label>My custom test input: <input :value="inputValue" @input="emitTheData" /></label> </div> </template> <script> export default { props: ['value'], data() { return { inputValue: value } }, methods: { emitTheData() { this.$emit('input', this.inputValue) } } } </script>この単純なコンポーネントは、渡されたデータを2つの方向にバインドする。
注:ご覧の通り、
value
というpropをinputValue
というデータに割り当てて、それをinputタグで使用した。propをinput要素に直接渡すことができると思うかもしれないが、Vue公式で言われている通り、propsは直接変更しない方がよい。関数型コンポーネント
監視プロパティまたは算出プロパティとメソッドのないコンポーネントがあると想像してみよう。それを使うだけで、
props
を使ってコンポーネントをレンダリングすることが可能だ。これが可能であれば、ライフサイクルメソッドもないため描画時間を短縮することができる。それが関数型コンポーネントだ。
関数型コンポーネントは次のように定義できる。
<!-- `template`要素の中に`functional`含めると関数型コンポーネントになる --> <template functional> <div>{{ props.foo }}</div> </template>このコンポーネントは関数型コンポーネントであるため、
this
コンテキストは使えないことに注意。Vueの関数型コンポーネントの詳細については、以下のリンクから確認できる。
- 投稿日:2020-07-04T17:10:45+09:00
【Rails】FullCalendarを用いたスケジュール管理昨日の実装
目標
開発環境
・Ruby: 2.5.7
・Rails: 5.2.4
・Vagrant: 2.2.7
・VirtualBox: 6.1
・OS: macOS Catalina前提
下記実装済み。
・Slim導入
・Bootstrap3導入
・Font Awesome導入
・ログイン機能実装
・投稿機能実装実装
1.Gemを導入
Gemfile# 追記 gem 'jquery-rails' gem 'fullcalendar-rails' gem 'momentjs-rails'ターミナル$ bundle2.
application.scss
を編集application.scss*= require_tree . *= require_self *= require fullcalendar /*追記*/3.
application.js
を編集application.js//= require rails-ujs //= require activestorage //= require turbolinks //= require jquery //= require moment // 追記 //= require fullcalendar // 追記 //= require_tree .4.JavaScriptファイル作成・編集
ターミナル$ touch app/assets/javascripts/calendar.jscalendar.js$(function() { function eventCalendar() { return $('#calendar').fullCalendar({}); } function clearCalendar() { $('#calendar').html(''); } $('#calendar').fullCalendar({ events: '/events.json', titleFormat: 'YYYY年 M月', dayNamesShort: ['日', '月', '火', '水', '木', '金', '土'], header: { left: '', center: 'title', right: 'today prev,next', }, defaultTimedEventDuration: '03:00:00', buttonText: { prev: '前', next: '次', prevYear: '前年', nextYear: '翌年', today: '今日', month: '月', week: '週', day: '日', }, timeFormat: 'HH:mm', eventColor: '#63ceef', eventTextColor: '#000000', }); });【解説】
① turbolinks対策の為、カレンダーを読み込む関数と削除する関数をそれぞれ用意する。
function eventCalendar() { return $('#calendar').fullCalendar({}); } function clearCalendar() { $('#calendar').html(''); }② 日本語で表示する。
// カレンダー上部を年月で表示させる titleFormat: 'YYYY年 M月', // 曜日を日本語表示 dayNamesShort: ['日', '月', '火', '水', '木', '金', '土'],③ ボタンのレイアウトを整える。
header: { left: '', center: 'title', right: 'today prev,next', },④ イベントの設定をする。
// 終了時刻がないイベントの表示間隔を設定 defaultTimedEventDuration: '03:00:00', buttonText: { prev: '前', next: '次', prevYear: '前年', nextYear: '翌年', today: '今日', month: '月', week: '週', day: '日', }, // イベントの時間表示を24時間にする timeFormat: 'HH:mm', // イベントの色を変える eventColor: '#63ceef', // イベントの文字色を変える eventTextColor: '#000000',4.モデルを作成
ターミナル$ rails g model Event user_id:integer title:string body:text start_date:datetime end_date:datetime~_create_events.rbclass CreateEvents < ActiveRecord::Migration[5.2] def change create_table :events do |t| t.integer :user_id t.string :title t.text :body t.datetime :start_date t.datetime :end_date t.timestamps end end endターミナル$ rails db:migrate5.コントローラーを作成
ターミナル$ rails g controller events new show edit my_calendarevents_controller.rbclass EventsController < ApplicationController before_action :set_event, only: [:show, :edit, :update, :destroy] def new @event = Event.new end def create @event = Event.new(event_params) @event.user_id = current_user.id @event.save ? (redirect_to event_path(@event)) : (render 'new') end def index @events = Event.where(user_id: current_user.id) end def show end def edit unless @event.user == current_user redirect_to root_path end end def update @event.update(event_params) ? (redirect_to event_path(@event)) : (render 'edit') end def destroy @event.destroy redirect_to my_calendar_path end def my_calendar end private def set_event @event = Event.find(params[:id]) end def event_params params.require(:event).permit(:user_id, :title, :body, :start_date, :end_date) end end6.ルーティングを追加
routes.rb# 追記 resources :events get 'my_calendar', to: 'events#my_calendar'7.
json.jbuilder
ファイルを作成・編集index.json.jbuilderjson.array!(@events) do |event| json.extract! event, :id, :title, :body json.start event.start_date json.end event.end_date json.url event_url(event) end【解説】
①
index
アクションで抽出したレコードを繰り返し処理し、配列を作成する。json.array! @category_children do |children|② 各データを
①
で作成した配列に格納する。json.extract! event, :id, :title, :body json.start event.start_date json.end event.end_date json.url event_url(event)8.ビューを編集
new.html.slim
を編集① HTMLを作成する。
new.html.slim.row .col-xs-3 .col-xs-6 = form_with model: @event, url: events_path, local: true do |f| = f.label :title, 'スケジュール名' br = f.text_field :title, class: 'form-control' br = f.label :body, 'スケジュール内容' br = f.text_area :body, class: 'form-control' br = f.label :start_date, '開始日時' br = f.datetime_select :start_date, {}, class: 'form-control datetime-form' br br = f.label :end_date, '終了日時' br = f.datetime_select :end_date, {}, class: 'form-control datetime-form' br br = f.submit '新規登録', class: 'btn btn-success btn-block' .col-xs-3② CSSでデザインを整える。
application.scss// 追記 .datetime-form { display: inline-block; width: auto; }
show.html.slim
を編集show.html.slim.row .page-header h3 | スケジュール管理 .row #calendar br .row table.table.table-bordered tbody tr th.active | スケジュール名 td = @event.title tr th.active | スケジュール内容 td = @event.body tr th.active | 開始日時 td = @event.start_date.to_s(:datetime_jp) tr th.active | 終了日時 td = @event.end_date.to_s(:datetime_jp) .row - if @event.user === current_user .pull-right = link_to 'スケジュール登録', new_event_path, class: 'btn btn-success' = link_to '編集', edit_event_path(@event), class: 'btn btn-primary' = link_to '削除', event_path(@event), method: :delete, data: { confirm: '本当に削除しますか?' }, class: 'btn btn-danger'
edit.html.slim
を編集edit.html.slim.row .col-xs-3 .col-xs-6 = form_with model: @event, url: event_path(@event), local: true do |f| = f.label :title, 'スケジュール名' br = f.text_field :title, class: 'form-control' br = f.label :body, 'スケジュール内容' br = f.text_area :body, class: 'form-control' br = f.label :start_date, '開始日時' br = f.datetime_select :start_date, {}, class: 'form-control datetime-form' br br = f.label :end_date, '終了日時' br = f.datetime_select :end_date, {}, class: 'form-control datetime-form' br br = f.submit '保存', class: 'btn btn-primary btn-block' .col-xs-3
my_calendar.html.slim
を編集my_calendar.html.slim.row .page-header h3 | マイカレンダー .row = link_to 'スケジュール登録', new_event_path, class: 'btn btn-success' #calendar br注意
turbolinks
を無効化しないとカレンダーが表示されない事があるので、必ず無効化しておきましょう。
- 投稿日:2020-07-04T16:17:17+09:00
【JavaScript】プログラミングクイズでよく使う記法
結論
- プログラミングクイズでよく使う記法と知見をメモしています。
- 初〜中級者向けの内容です。
- 手短に結論だけ欲しい方は、各タイトルの見出し〜ソースまでをお読みいただければ十分です。
はじめに
プログラミングのクイズを200問解いてみました。
難易度は文を読む時間を含めて1分〜3分ほどで解ける簡単な問題が多かったですが、解き方を調べるうちに「 こうしたら短く書けるんだ! 」 とか、
「 この関数、こんな使い方できたんだ・・・ 」
などといった気づきを得ました。
基本的な文法も改めて学習することができて非常に有意義でした。
その時の知見をここにメモしようと思います。けっこう好みがわかれる書き方もしてると思います。
暇つぶし感覚で読んでみてください。
1.
console.log(値1,値2)
console.log()
にカンマ区切りで複数の値を渡すと、記述した順番通りにスペース区切りで値を出力してくれます。const month = 6; const date = 27; console.log(month, date); // 6 27 console.log(month, '/', date); // 6 / 27一応、MDNを読んでみました。
カンマ区切りで渡した値を出力する旨がきちんと書いてありました。
console.log(obj1 [, obj2, ..., objN]);
obj1 ... objN
出力する JavaScript オブジェクトのリスト。
各オブジェクトの文字列表現が記述順で出力されます。
MDN -console.log-これを知るまでは複数の値を出力する際は
変数1 + " " + 変数2
としたり、テンプレート文字${変数1} {変数2}
列を使っていましたが、どう頑張ってもカンマで区切るのが一番楽です!この書き方は超常識なのかもしれませんが、今まで読んだJavaScriptの学習教材には出てきませんでした。もっと早く知りたかった・・・。
2.
配列.map(Number)
配列.map(Number)
とすると、配列内の要素をすべてNumberオブジェクトに変換できます。['1','2','3'].map(Number) // [ 1, 2, 3 ]配列内の要素がNumberオブジェクトにできない値であれば、
NaN
が設定されます。['1', 'a', '3'].map(Number) // [ 1, NaN, 3 ]
map
といえばarr.map(x => x * 2);
のように引数を受け取って処理したものを返す使い方しか知らなかったのですが、Numberを渡すだけで処理してくれるみたいです。余談:
.map(parseInt)
について
.map(Number)
と同じ要領でparseIntを使ってもNumberオブジェクトになりそうですが・・・そうはなりません。parseIntをそのまま渡すと思わぬ結果が返ってくるので注意してください。["1", "2", "3"].map(parseInt); // [1, NaN, NaN]
parseInt
は引数を2つとるため、このような結果になります。
以下のように、引数を1つだと明示すれば想定通りの結果が得られます。['1', '2', '3'].map((str) => parseInt(str)); // [ 1, 2, 3 ]詳しくはMDNにわかりやくす書いてあるのでそちらを参照してください。
MDN - Array.prototype.map() トリッキーな使用例-3.
配列.map(Math.メソッド)
配列.map(Math.メソッド)
とすると、配列内の要素すべてに渡したメソッドを実行してくれます。
例えば小数点以下を切り捨てて整数にしてくれるMath.floor
を渡すと・・・[1.1, '2.2', 3.3].map(Math.floor) // [1, 2, 3];きちんと計算した配列を生成します。
こちらはStringオブジェクトでも数字であればよしなにNumberとして扱ってくれます。4.
[,変数名] = 配列
分割代入で
[,変数名] = 配列
とすると、不必要な値を定義せずに無視することができます。
例えば[, str] = ['a', 'b']
とすると、配列の0番目の要素('a'
)を無視し、配列1番目の要素('b'
)のみ左辺の変数(str
)に格納されます。const [, str] = ['a', 'b']; console.log(str); // bこれは何番目に何の値が入っているのかがわかりきっている場合によく使います。
例えば日時情報などです。
String(new Date()).split(' ')
には日付や時間などの情報がたくさん入っています。String(new Date()).split(' ') // [ 'Fri', 'Jun', '26', '2020', '29:39:55', 'GMT+0900', '(GMT+09:00)' ]ここから時間だけを取り出したい場合、
const [, , , , time]
に代入します。const [, , , , time] = String(new Date()).split(' '); console.log(time); // 19:39:55このように欲しい変数だけを定義すると、コードの見通しがよくなるのでオススメです。
詳しくはこちらを参照してください。
MDN -分割代入 返値の無視-余談: 残余パターン(
...arr
)との併用についてこの書き方は残余パターンと併用して使うことが結構ありました。
例えば[人数, 値1, 値2, 値3 ・・・]
という入力から、値1以降を変数に格納する場合は以下のように書きます。const [, ...arr] = ['人数', '値1', '値2', '値3']; console.log(arr); // [ '値1', '値2', '値3' ]ちなみにこれは
slice()
を使っても同じ結果が得られますが・・・const arr = ['人数', '値1', '値2', '値3'].slice(1); console.log(arr); // [ '値1', '値2', '値3' ]
slice()
は文の末尾に出てくるので、そこまで読まないとどこで値を区切るのかがわかりません。
その点分割代入なら、変数定義の時点で「いらない要素があるのか」とすぐに察することができるので、個人的にはこっちの方が好きです。まとめ
私が取り組んでいたクイズ問題の入力値は、数値なのにStringやスペース区切りの文字列として渡ってくることがほとんどでした・・・。
それを200問解いたら、文字列や配列を操作するお決まり文をスラスラ書けるようになりました。
また、最適な書き方を追求する過程で、この記事に書いたような注意点や良い記法があることを学べました。知見はまだあるような気がするので、思い出したら追記しようと思います。
(前に全く同じ記事を投稿してましたが、そちらは間違えて消してしまいました。。ストックしていただいてた方、すみません。。)
- 投稿日:2020-07-04T15:55:39+09:00
色相(Hue)の回転はHSL色空間よりLCH色空間のほうが自然に見える
色相(Hue)を少しずつずらして別の色を作ることがたまにありますが、その際、HSL色空間よりLCH色空間でずらしたほうが人間の知覚にとって自然に見えます。
それを視覚的に確認するためにデモサイトを作りました。
デモ: https://fujiharuka.github.io/color-rotation/
HSL 色空間で色相をずらした場合
左端の色が基準となる色で、HSL 色空間で色相を少しずつずらして色を生成しています。白を背景色とした場合、文字が読みにくい色があることがわかります。
LCH 空間で色相をずらした場合
左端の色が基準となる色で、今度は LCH 色空間で色相を少しずつずらして色を生成しています。白の背景色に対して文字が読みにくい色はありません。LCH のほうが自然に色のバリエーションが作れています。なぜこうなるのかというと、LCH色空間は人間の知覚差異を考慮した空間だからです。
参考
- 投稿日:2020-07-04T15:51:13+09:00
Ruby on Rails 非同期通信(ajax)についての振り返り
カリキュラム学習・ポートフォリオ作成中、この機能を実装するのに苦戦したので、メモ。
非同期通信にしたかったのは以下の二点。前提条件
テーブル
schema.rbcreate_table "users", force: :cascade do |t| (略) end create_table "products", force: :cascade do |t| (略) end create_table "favorites", force: :cascade do |t| t.integer "user_id", null: false t.integer "product_id", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false endモデル(アソシエーション)
favorite.rbbelongs_to :user belongs_to :product def favorited_by?(user) Favorite.where(user_id: user.id).exists? enduser.rbhas_many :favorites, dependent: :destroyproduct.rbhas_many :favorites, dependent: :destroy def favorited_by?(user) Favorite.where(user_id: user.id).exists? endルート
rutes.rbresources :products do resource :favorites, only: [:create, :destroy] end実装①(商品のお気に入り追加・削除)
productsコントローラは以下の通り
products_controller.rbdef show @product = Product.find(params[:id]) end次にお気に入りボタンをパーシャル化。(userディレクトリ内に作成)
views/users/products/_favorite_button.html.erb<% if product.favorites.where(user_id: current_user.id).exists? %> <%= link_to "お気に入りから削除", users_product_favorites_path(product_id: product.id), method: :delete, remote: true %> <% else %> <%= link_to "お気に入りに追加", users_product_favorites_path(product_id: product.id), method: :post, remote: true %> <% end %>ここで
remote: true
を付けることで、非同期通信が可能になる。products/show.html.erbで呼び出し
views/users/products/show.html.erb<div id="favorites_buttons_<%= @product.id %>"> <%= render 'users/products/favorite_button', product: @product %> </div>次はfavoritesのコントローラ。
fovorites_controller.rbdef create @product = Product.find(params[:product_id]) favorite = current_user.favorites.new(product_id: @product.id) favorite.save end def destroy @product = Product.find(params[:product_id]) @favorites = current_user.favorites favorite = current_user.favorites.find_by(product_id: @product.id) favorite.destroy endここでリダイレクト先を指定しなければ、JSの処理を探しに行ってくれる。
最後に作成・削除した場合のJS処理を作成する。views/users/favorites/create.js.erb$("#favorites_buttons_<%= @product.id %>").html("<%= j(render 'users/products/favorite_button', product: @product) %>");views/users/favorites/destroy.js.erb$("#favorites_buttons_<%= @product.id %>").html("<%= j(render 'users/products/favorite_button', product: @product) %>");show.html.erb内の、idで設定した範囲のみが、処理後書き換えられるようになる。
実装②(お気に入り一覧から削除)
fovoritesのコントローラは以下の通り作成
favorites_controller.rbdef index @favorites = current_user.favorites end非同期通信したい範囲をパーシャル化。
view/users/favorites/_form.html.erb<% if favorites.present? %> <table class="table"> <thead> <tr> <th colspan="3">商品</th> </tr> </thead> <tbody> <% favorites.each do |f| %> <tr> <td> <%= attachment_image_tag f.product, :image, :fill, 80, 80, format: 'jpeg', fallback: "no_image.jpg", size: '80x80' %> </td> <td><%= link_to f.product.name, users_product_path(f.product.id) %></td> <td><%= link_to "お気に入りから削除", users_product_favorites_path(product_id: f.product.id), method: :delete, remote: true %></td> </tr> <% end %> </tbody> </table> <% else %> <h3> お気に入りリストがありません。<br> 商品ページから追加してみましょう </h3> <% end %>fovorites/index.html.erbで呼び出し
<div class="row"> <div class="col-sm-6 offset-3" id="favorites_index"> <%= render 'users/favorites/form', favorites: @favorites %> </div> </div>JSの処理に以下の1行を追加
views/users/favorites/destroy.js.erb$("#favorites_index").html("<%= j(render 'users/favorites/form', favorites: @favorites) %>");これでお気に入り一覧にも非同期機能が実装できた。
実装した上での反省点・感想
- JSに記載しているインスタンス変数はそれぞれcreate・destroyメソッドから確認しているため、そっちでも定義してあげないといけないということが身にしみた。(考えてみれば当たり前だったが)
- 実装①についてはググればよく出てくるが、②は見かけないので自力で 解けて感動した。ただ流れを理解していれば迷うとこでも無かったなと感じた。
- 投稿日:2020-07-04T11:56:42+09:00
Identity Functor ってパイプライン演算子っぽい
ふと、「Identity Functorってパイプライン演算子っぽい」と思ったんですよ。何言ってんだって感じですが。とりあえず、JavaScript で下記のような関数を定義するとパイプラインっぽいことができるんじゃないかと思った次第です。
const wrap = x => ({ unwrap: x, map: f => wrap(f(x)), tap: f => wrap((f(x), x)) });tap と unwrap がある理由
Identity Functor に
tap
は不要です。Scala がpipe
(上のmap
に対応するメソッド)とともにtap
も導入したことを参考に入れてみました。副作用が身近な言語ならtap
は確かに便利ですね。Scala では、
pipe/tap
を呼び出すと暗黙の変換が発動して元の値がChainingOps
に包まれます。また、pipe/tap
の戻り値はChainingOps
に包まれていません。そのため明示的に包んだり、包装をほどいたりする必要はありません。それに比べると、上の JavaScript の実装は明示的なwrap/unwrap
が必要になります。なぜなら、暗黙的な変換はされませんし、map/tap
の戻り値が包まれた状態で返されるからです。余談ですが、
tap
は Ruby が源流です。逆に言うと Ruby のthen
はパイプラインっぽいかもしれませんね。map をパイプラインとして使ってみる
次のコードの「通常の関数適用」と「map を使った関数適用」の処理結果はどちらも
202
になります。map使用例.jsconst addOne = x => x + 1; const timesTow = x => 2 * x; // 通常の関数適用 timesTow(addOne(100)); // map を使った関数適用 wrap(100).map(addOne).map(timesTow).unwrap;
map
を使うと関数適用の入れ子が解消され、処理の順にそって関数を左から右に書けています。パイプラインっぽいことは十分できていますね。
this
を考慮しなければ、fn(…(f1(f0(x))…)
の形の関数適用の入れ子は一般に、wrap(x).map(f0).map(f1)….map(fn).unwrap
へ書き換え可能です。tap を使ってみる
上の map 使用例のコードに
tap
を挟み込むと、処理途中の結果をコンソールに表示できます。具体的に、処理結果は202
で変わりませんが、コンソールには100
,101
,202
と途中結果が表示されていきます。tap使用例.jsconst addOne = x => x + 1; const timesTow = x => 2 * x; // tap を使って処理の途中結果をコンソールに出力 wrap(100) .tap(console.log) .map(addOne) .tap(console.log) .map(timesTow) .tap(console.log) .unwrap;一次変数を用意したり、引数をそのまま返す関数を用意すれば通常の関数適用でも
tap
と同じ事はできるかもしれません。ただ、tap
の方がコードの追加は容易でしょうし、不要になった際の除去も簡単でしょう。ちなみに、応用すると下記のようなおどろおどろしい FizzBuzz を書くこともできます。
toFizzBuzz.jsconst toFizzBuzz = n => wrap(n) .map(num => ({num, str: ''})) .tap(x => { if(x.num % 3 === 0) x.str += 'Fizz'; }) .tap(x => { if(x.num % 5 === 0) x.str += 'Buzz'; }) .tap(x => { if(x.str === '') x.str += x.num.toString(); }) .map(x => x.str) .unwrap;関数合成
使いどころは不明ですが、同じような考えで関数合成のみを扱うオブジェクトも生成できます。
const unary = (f = x => x) => ({ run: f, mappend: g => unary(x => g(f(x))), tappend: g => unary(x => { const y = f(x); g(y); return y; }) });mappend_tappend使用例.jsconst addOne = x => x + 1; const timesTow = x => 2 * x; const compose = unary() .tappend(console.log) .mappend(addOne) .tappend(console.log) .mappend(timesTow) .tappend(console.log) .run; compose(100);型付け
TypeScript で型を意識するなら次のようになるでしょうか。
wrap
は素直に型が付きますね。unary
は、型が合わないためデフォルト引数を設定できません。別途、emptyUnary
を用意すれば、同じようにemptyUnary()
を起点に関数を合成していく事ができます。type Identity<A> = { unwrap: A, map: <B>(f: (x: A) => B) => Identity<B>, tap: <U>(f: (x: A) => U) => Identity<A> } const wrap = <A>(x: A): Identity<A> => ({ unwrap: x, map: f => wrap(f(x)), tap: f => wrap((f(x), x)) }); type UnaryBuilder<A, B> = { run: (x: A) => B, mappend: <C>(g: (x: B) => C) => UnaryBuilder<A, C>, tappend: <U>(g: (x: B) => U) => UnaryBuilder<A, B> } const emptyUnary = <A>(): UnaryBuilder<A, A> => unary(x => x); const unary = <A, B>(f: (x: A) => B): UnaryBuilder<A, B> => ({ run: f, mappend: g => unary(x => g(f(x))), tappend: g => unary(x => { const y = f(x); g(y); return y; }) });おわり
まあ、ごちゃごちゃ言ってないで、パイプライン演算子
|>
が使える環境なら、それを使えばいいですし。使えなくても次のような関数を定義すれば十分ですね。const pipe = (x, ...fs) => fs.reduce((v, f) => f(v), x);
- 投稿日:2020-07-04T11:48:41+09:00
iOS / Web版Twitterの指定したページへのリンクをホーム画面に追加する
TL;DR
Web版Twitterをホーム画面に追加する
-> PWAになり追加時のURLが反映されない別のページを経由して開けば解決
-> dataスキームdata URI scheme
- dataスキーム
- 外部リソースと同じようにデータを読み込む方法
- アドレスバーに入力する
iOSの「ホーム画面に追加」は後からURLを書き換えられない
-> 追加時用のTwitterを開かないパターンが必要
-> 端末がネットに繋がっているかで条件分岐(他にも方法はあると思う)data:text/html, <script> if (navigator.onLine) location.href='開くURL' </script>実際の手順
- 上記のdataスキームの「開くURL」部分を変更
- オフラインの状態でブラウザのアドレスバーに入力
- ホーム画面に追加
問題点
ホーム画面に表示されるアイコンが真っ白なので複数追加すると分かりにくい
↓↓↓
ページに背景色を設定する
<style> html {background: 色} <style>アイコンの画像を設定する
<link rel="apple-touch-icon-precomposed" href="画像のURL">以上
- 投稿日:2020-07-04T11:02:31+09:00
初心者なりにスタックとキューの実用例、検索アルゴリズム (深さ検索優先アルゴリズム 、幅検索優先アルゴリズム)についてまとめてみた
目的
- スタックとキューを前提にデータを取り扱えるようになる。
結論
- スタック、キューとは、データの取扱い処理方法のひとつである。
- データの集合を系統立てて管理、検索するために前提となる考え方。
つまり、アプリケーションなどの処理順序、処理効率と、検索のアルゴリズムに利用
※調査時、C言語で説明するサイトがとても多かったのでC言語の本が出てきます。
でも、C、わかりません。世界にこんにちはしたこともないです。
コンピューターサイエンスの基礎の勉強として役に立つ感触があるので概念だけでもそのうちやろうと思います。背景
クロージャーや再帰などの処理をするにあたって、
スタック、キューという言葉を比較的頻繁に見かけるので
そろそろ理解しておかねばいけないと感じた。スタック、キューとは
端的で、とてもわかりやすい説明を見つけました。
スタックは、例えば超忙しいときに新しい課題がぶっこまれたときとかにとりあえずそれを先に片付けるような感じ
キューは、人気ラーメン屋に並ぶ人々の待ち行列のように先に並んだ人が先にお店に入る感じ参考)https://qiita.com/drken/items/6a95b57d2e374a3d3292
上記のままでも十二分にわかりやすいが同世代的には
スタック = お菓子のペッツ
キュー = ロケット鉛筆のイメージをもった。実用例
世の中における様々な問題は、探索によって、考えられる場合を調べ尽くすことによって原理的には解決できるものが多いです。例えば、現在地から目的地まで最速でたどり着く方法を求める問題は、原理的には、現在地から目的地へ到達する経路をすべて列挙することで解決できます1。将棋やオセロの必勝法を求める問題は、原理的には、考えられる局面と局面遷移をすべて調べ上げることで求めることができます
参考)https://qiita.com/drken/items/4a7869c5e304883f539b
データ構造が違うことでどんなメリットが享受されるのか?
使い分け方は?どんな人に関係があるの?と思いましたwそこで、スタック、キューがあることで何が実現できるか、調査しました。
スタックで実現できること
- さまざまなソフトウェアの[元に戻す]ボタンに使用される。変更がスタックにプッシュされる。ブラウザーの戻るボタンでは、最近アクセスしたすべてのWebページがスタックにプッシュされるスタックの助けを借りて機能する。
- 深さ検索:とにかく行けるとこまで行ってそれ以上進めなくなったら1歩戻ってまた探索する(親子検索)
キューで実現できること
- プリンターの印刷順
- 画像のアップロード順
- 幅検索:出発点に近い点から順に探索する(兄弟検索)
配列では理解がしやすいですが、
グラフなど多元的な構造をもつデータの場合理解に苦しみました。そこで、本から問題を読んでみて理解することにしました。
練習問題
例題:スタックというデータ構造がある。スタックは配列のようにデータを蓄積するが、積み上げ(push)と、一番最後にpushされたデータの積み下ろし(pop)の2種類しか操作が存在しない。Stackクラスを実装せよ。また、Stackクラスを実際に使用してみよ。
回答
class Stack { constructor() { this._data = []; } push(data) { this._data.push(data); } pop() { return this._data.pop(); } }const stack = new Stack(); stack.push(1); stack.push(2); stack.push(3); console.log(stack.pop()); console.log(stack.pop()); console.log(stack.pop());例題:ソート済みの配列があるとする。このとき、配列の中からある値を探しだす方法として、二分探索(バイナリサーチ)という手法が使える。
1 : 配列の真ん中の値を見て、同じならtrueを返し終了する。真ん中より小さければ左半分を二分探索し、真ん中より大きければ右半分を二分探索する。
2 : 最後まで見つからなければfalseを返すこれを実装し、動作を確かめよ。回答
function binarySearch(array, target, start, end) { // startとendが逆転したら終わる if(start > end) { return false; } // 真ん中のインデックスを計算する const center = Math.floor((start + end) / 2); // 半分ずつ探す if(array[center] > target) { return binarySearch(array, target, start, center - 1); } else if(array[center] < target) { return binarySearch(array, target, center + 1, end); } else { return true; } }const sortedArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const arrayIncludes2 = binarySearch(sortedArray, 2, 0, sortedArray.length - 1); console.log(arrayIncludes2);その他参考資料(special Thanks!)
https://www.amazon.co.jp/プログラミングコンテスト攻略のためのアルゴリズムとデータ構造-渡部-有隆/dp/4839952957
https://sbfl.net/blog/2018/10/14/javascript-programming-5/
https://ja.wikipedia.org/wiki/深さ優先探索
https://www.iwass.co.jp/column/column-01.htmlhttps://ja.wikipedia.org/wiki/幅優先探索
遅延評価学習 候補
グラフ理論
ソート関係(バブルソートはOK)遅延評価学習について
本の最初のページから読み始めることは、ひとつのよい勉強法だ。
ただ、そのような先行評価的な勉強は、初学者か、感情的な不安解消のためのものだ。
何かあったときの先行投資、だというが、何かあったときはまずその学習だけじゃ足りないこと。
そして本を最初からよんで網羅的に学んでも忘れている。それよりは、緊急度や知識欲が高い問題から本気で解く中で、周辺知識としてつけた方が、結果的に良い気がする。
まずは極め横展開する。深さ優先検索的学習法
- 投稿日:2020-07-04T10:41:30+09:00
【Rails】jquery を使わずに javascript で flashメッセージをフェードアウトさせる
はじめに
Railsアプリを開発していてflashメッセージをフェードアウトさせたく、javascriptを用いて実装したので書く。
こういったやつ↓↓(Bootstrapのalertを適用させています)
これをフェードアウトさせる
環境
Ruby: 2.5.1
Rails: 5.2.4.2実装
実装イメージとしては徐々に透明度を高くしていき、最終的に非表示にします。
要素のstyle属性
①opacityの値を減少させていく
②opacityが0になったらdisplay: none; にするviewファイル
erbはこのようになっている
_flash_messages.html.erb<% flash.each do |message_type, message| %> <div class="alert alert-<%= message_type %>"><%= message %></div> <% end %>jsファイル
flash_message.js// turbolinks:loadでページ読み込み時に実行 document.addEventListener('turbolinks:load', () => { // flashメッセージ要素を取得 const flashMessage = document.querySelector('.alert'); // フェードアウトさせる(徐々に透過し,非表示にする)関数を定義 const fadeOutFlashMessage = () => { // setIntervalを特定するために返り値を変数timer_idに格納 const timer_id = setInterval(() => { // flashメッセージのstyle属性 opacityを取得 const opacity = flashMessage.style.opacity; if (opacity > 0) { // opacityの値が0より大きければ0.02ずつ値を減少させる flashMessage.style.opacity = opacity - 0.02; } else { // opacityの値が0になったら非表示に flashMessage.style.display = 'none'; // setIntervalをストップさせる clearInterval(timer_id); }; }, 50); // 今回は0.05秒ごとにsetIntervalを実行 } // flashメッセージがある場合のみ実行 if (flashMessage !== null) { // style属性opacityをセット flashMessage.style.opacity = 1; // 今回は表示から3秒後に上記で定義したフェードアウトさせる関数を実行 setTimeout(fadeOutFlashMessage, 3000); }; });参考文献
下記記事を参考にさせていただきました。
ありがとうございました。・turbolinksチートシート
・【JavaScript】Railsのflashをフェードアウトして消す方法最後に
読んでいただきありがとうございます。
間違いや、より効率的な記述方法等あればご指摘いただけると非常に嬉しいです。
- 投稿日:2020-07-04T06:46:47+09:00
npm(パッケージ管理ツール)の使い方
npm(Node Package Manager)とは、JavaScriptで記述されたパッケージ管理システムです。
無料で使えて、世界中のオープンソースソフトウェア(Node.jsのツール、モジュール)を開発に使用することができます。
使い方
1.初期化
Macのターミナルを使った手順を解説します。
まずmkdir
で新規ディレクトリを作成し、cd
で作成したディレクトリに移動します。$ mkdir npm_test $ cd npm_test
npm init
で初期化を行います。$ npm init
package.json
ファイル作成のユーティリティがスタートするので、
pacakge name:
にプロジェクト名を入力します。
デフォルトはディレクトリ名になっているので、そのままでよければエンターを押します。This utility will walk you through creating a package.json file. It only covers the most common items, and tries to guess sensible defaults. See `npm help json` for definitive documentation on these fields and exactly what they do. Use `npm install <pkg>` afterwards to install a package and save it as a dependency in the package.json file. Press ^C at any time to quit. package name: (npm_test)バージョンが表示されるので、問題なければエンターを押します。
version: (1.0.0)
プロジェクトの
description
(解説)を入力して、エンターを押します。description: This is test.
エントリーポイントとは、アプリケーションで一番最初に呼び出される部分のことです。今回の場合は、実際に最初に実行されるJavaScriptのファイルを指します。デフォルトで問題なければ、エンターを押します。
entry point: (index.js)
テストコマンドを入力することもできますが、未入力でもOKです。エンターを押します。
test command:
Git
のレポジトリを入力できますが、テストなので未入力でエンターを押します。git repository:
npm内で検索する場合のキーワードを入力できます。未入力でエンターを押します。
keywords:
著者を入力します。任意の名前を入力してエンターを押します。
author: tetsu
ライセンスの設定ができます。デフォルトでよければエンターを押します。
license: (ISC)
最後に、上記で設定した情報が一覧で表示されます。問題なければ
yes
と入力して実行します。About to write to /Users/tetsu/Documents/npm_test/package.json: { "name": "npm_test", "version": "1.0.0", "description": "This is test.", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "tetsu", "license": "ISC" } Is this OK? (yes) yes
ls
でnpm_test
ディレクトリの中を確認すると、pacage.json
ファイルが作成されていることがわかります。iMac:npm_test tetsu$ ls package.json
cat
で中身を表示すると、先ほど設定した情報が記述されていることが確認できます。iMac:npm_test tetsu$ cat package.json { "name": "npm_test", "version": "1.0.0", "description": "This is test.", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "tetsu", "license": "ISC" }2.インストール
パッケージのインストールには、2種類の選択肢があります。
- PC自体にインストール → どのプロジェクトからも参照可能(pathの設定が必要)
- このプロジェクトにインストール → このプロジェクトのみ利用可能(このディレクトリにダウンロードされる)
サンプルとして、
chalk
をダウンロードしてみます。
※chalk
は、console.log
に色付けできるパッケージです。$ npm install chalk少し待つと、実行結果が表示され、インストールの完了を確認できます。
npm notice created a lockfile as package-lock.json. You should commit this file. npm WARN npm_test@1.0.0 No repository field. + chalk@4.1.0 added 7 packages from 4 contributors and audited 7 packages in 4.911s 2 packages are looking for funding run `npm fund` for details found 0 vulnerabilities
ls
でnpm_test
ディレクトリの中を確認すると、
node_modules
ディレクトリと、package-lock.json
ファイルが新たに作成されていることがわかります。iMac:npm_test tetsu$ ls node_modules package-lock.json package.json
cat
で先ほどのpackage.json
を確認すると、dependencies
にインストールしたパッケージが追加されていることがわかります。
package.json
を読めば「このプロジェクトには、どんなパッケージが使われているか?」がひと目でわかるようになっています。iMac:npm_test tetsu$ cat package.json { "name": "npm_test", "version": "1.0.0", "description": "This is test.", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "author": "tetsu", "license": "ISC", "dependencies": { "chalk": "^4.1.0" } }
node_modules
の中を見ると、「chalk
本体のファイル」と、「chalk
と依存関係にあるファイル」がセットになってインストールされています。iMac:node_modules tetsu$ ls @types chalk color-name supports-color ansi-styles color-convert has-flagnpmの便利なところは、このように「使いたいパッケージをセットとしてまとめてプロジェクトにインストールできる利便性」です。
3.実際に使ってみる
index.js
を作成して、実際に実行してみます。
プロジェクトのディレクトリ直下にファイルを作成します。iMac:npm_test tetsu$ nano index.js
nano
でファイルを作成して、下記のコードを記述します。ctr + x
とy
で保存して終了します。
※エディターを使ってもOKです。index.jsconst chalk = require('chalk'); console.log(chalk.blue('Hello world!'));この段階で、一度実行してみます。
npm run start
を入力してみると、エラーになってしまいます。iMac:npm_test tetsu$ npm run start npm ERR! missing script: start npm ERR! A complete log of this run can be found in:パッケージを実行するには、
package.json
に実行するためのスクリプトを記述する必要があります。
script
内に"start": "node index.js"
を追記します。package.json{ "name": "npm_test", "version": "1.0.0", "description": "This is test.", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", // 「,」を入れて、 "start": "node index.js" // この行を追記 }, "author": "tetsu", "license": "ISC", "dependencies": { "chalk": "^4.1.0" } }
node index.js
は、npm run
で実行されるファイルを指しています。"start:"
には任意のコマンドを指定できます。この状態で、もう一度
npm run start
で実行してみます。iMac:npm_test tetsu$ npm run start > npm_test@1.0.0 start /Users/tetsu/Documents/npm_test > node index.js Hello world!下記の画像のようにコンソールに色が付いたら
chalk
が実行できています。
chalk
で遊んでみたい方は、下記のリンクを参照してください。参考
- 投稿日:2020-07-04T04:16:55+09:00
新Edgeの拡張機能を自作する (コンテキストメニュー編)
Microsoftのブラウザ、新Edgeの拡張機能を自作します。
コンテキストメニューからWikipediaやTwitterを検索する拡張機能を作成します。manifest.jsonの作成
まずは任意の開発フォルダを作成します。
そしたら中に次のようなmanifest.jsonを作成します。良く分からないと思うので、コピペでOK。
manifest.json{ "name": "Sample Extension", "description": "Extension Sample", "version": "1.0", "background": { "scripts": ["script.js"], "persistent": false }, "permissions": ["tabs", "contextMenus"], "manifest_version": 2 }"name"と"description"と"vertion"は変更可
本体スクリプトの作成
開発フォルダに、次のようなscript.jsを作成します。本体となるスクリプトです。
メニューを登録するコードと、メニューがクリックされた時のコードを記述します。script.js//メニューを登録するコード chrome.runtime.onInstalled.addListener(function(){ chrome.contextMenus.create({ id: "root", title: "拡張メニュー", contexts: ["all"], type: "normal", }) chrome.contextMenus.create({ parentId: "root", id: "wikipedia", title: "Wikipediaで開く", contexts: ["all"], type: "normal", }) chrome.contextMenus.create({ parentId: "root", id: "twitter", title: "Twitterで検索する", contexts: ["all"], type: "normal", }) }) //メニューがクリックされた時のコード chrome.contextMenus.onClicked.addListener(function(event) { if(event.menuItemId === "wikipedia"){ const url = "https://ja.wikipedia.org/wiki/" + event.selectionText chrome.tabs.create({url}) } else if(event.menuItemId === "twitter"){ const url = "https://twitter.com/search?f=realtime&src=typd&q=" + event.selectionText + "%20lang%3Aja" chrome.tabs.create({url}) } })「登録コード」はツリー形式にするために、"parentId"で親のidを指定しているのがポイント。
「クリックされた時のコード」はメニューのidで分岐させるのがポイント。拡張機能を登録する
ブラウザのメニューから拡張機能を開いたら
- 「開発者モード」をオンにする
- 「展開して読み込み」から開発フォルダを指定する
これでインストール完了です。
拡張機能を更新する
本体コードを変更した時は、更新作業を行う必要があります。
拡張機能のページにインストールした自作のプログラムが表示されます。
その中にある「再読み込み」から拡張機能を更新することができます。※マニュフェストを変更した時や「再読み込み」が表示されない場合は、登録からやり直してください。
課題
- ブラウザを起動させた時に警告ダイアログが表示される
- ブラウザに表示される拡張機能のアイコンを非表示にしたい
- 投稿日:2020-07-04T04:16:55+09:00
新Edgeの拡張機能の作り方 (コンテキストメニュー編)
Microsoftのブラウザ、新Edgeの拡張機能を自作します。
コンテキストメニューからWikipediaやTwitterを検索する拡張機能を作成します。マニフェストの作成
まずは任意の開発フォルダを作成します。
そしたら開発フォルダに次のようなmanifest.jsonを作成します。良く分からないと思うので、コピペでOK。
manifest.json{ "name": "Sample Extension", "description": "Extension Sample", "version": "1.0", "background": { "scripts": ["script.js"], "persistent": false }, "permissions": ["contextMenus", "tabs"], "manifest_version": 2 }"name"と"description"と"vertion"は変更可
本体の作成
開発フォルダに、次のようなscript.jsを作成します。本体となるスクリプトです。
メニューを登録するコードと、メニューがクリックされた時のコードを記述します。script.js//メニューを登録するコード chrome.runtime.onInstalled.addListener(function(){ chrome.contextMenus.create({ id: "root", title: "拡張メニュー", contexts: ["all"], type: "normal", }) chrome.contextMenus.create({ parentId: "root", id: "wikipedia", title: "Wikipediaで開く", contexts: ["all"], type: "normal", }) chrome.contextMenus.create({ parentId: "root", id: "twitter", title: "Twitterで検索する", contexts: ["all"], type: "normal", }) }) //メニューがクリックされた時のコード chrome.contextMenus.onClicked.addListener(function(event) { if(event.menuItemId === "wikipedia"){ const url = "https://ja.wikipedia.org/wiki/" + event.selectionText chrome.tabs.create({url}) } else if(event.menuItemId === "twitter"){ const url = "https://twitter.com/search?f=realtime&src=typd&q=" + event.selectionText + "%20lang%3Aja" chrome.tabs.create({url}) } })「登録コード」はツリー形式にするために、"parentId"で親のidを指定します。
「クリックされた時のコード」はメニューのidで分岐させます。拡張機能を登録する
作成した拡張機能を登録するには
- 「拡張機能」を開く (ブラウザのメニューにある)
- 「開発者モード」をオンにする
- 「展開して読み込み」から開発フォルダを指定する
これでインストール完了です。
拡張機能を更新する
本体コードを変更した時は、更新作業を行う必要があります。
拡張機能のページにインストールした自作のプログラムが表示されます。
その中にある「再読み込み」で更新できます。※マニュフェストを変更した時や「再読み込み」が表示されない場合は、登録からやり直してください。
課題
- ブラウザを起動した時に警告ダイアログが表示される
- ブラウザに表示される拡張機能のアイコンを非表示にしたい
- 投稿日:2020-07-04T03:16:30+09:00
【Nuxt/Rails】axiosとdevise_token_authを使ってPOSTした実装
Nuxt.jsとRuby on Railsでaxiosとdevise_token_authを利用してPOSTする時に、地味に詰まってしまったので備忘録がてらまとめます。
Ruby on Rails側の実装
devise_token_authの設定諸々は省きます。
以下のURLあたりが参考になりましたので、そちらをご覧いただけると良いかもしれません。
https://github.com/lynndylanhurley/devise_token_auth
https://qiita.com/Masahiro_T/items/6bc49a625b437a7c2f45
https://sainu.hatenablog.jp/entry/2018/08/11/194319FormObjectを導入してるので、そちらも込みで載せます。
articles_controller.rbclass ArticlesController < ApplicationController def create @article = ArticleForm.new(article_params) if @article.save # 省略 end end private def article_params json_request = ActionController::Parameters.new(JSON.parse(request.body.read)) json_request.permit( :title, :description, ).merge(user_id: current_user.id) end endarticles_form.rbclass ArticleForm include ActiveModel::Model attr_reader :title, :description validates :title, presence: true, length: { maximum: 50 } validates :description, length: { maximum: 300 } def initialize(article_params) @article = article_params end def save return false if valid? Article.create(@article) true end endこれで、POSTするまでの準備が整いました。
補足
POSTされる時にJsonをごにょごにょすることになったので、JsonをParseする処理を書いてあげる必要がありました。(ここに凄くハマりました。)
def article_params json_request = ActionController::Parameters.new(JSON.parse(request.body.read)) json_request.permit( :title, :description, ).merge(user_id: current_user.id) end以下の記事に救われた(というかJsonのParse部分はコピペ)ので載せておきます。
Nuxt.js側の実装
Componentに書いた実装は省いて、Vuexの中でやってるaxiosでPOSTした部分の実装だけ切り取ります。
article.jsexport const actions = { async postArticle({ dispatch }, article) { await this.$axios .post('/articles', article, { headers: { 'Content-Type': 'multipart/form-data', // サムネ画像を送信する想定のため 'access-token': this.state.user.userToken.accessToken, client: this.state.user.userToken.client, uid: this.state.user.userToken.uid, }, }) .then(() => dispatch('getArticleList')) .catch((error) => console.log(error)) }, }devise_token_authのtokenをCookieに詰めて、nuxtServerInit時にVuexに書き出す実装をしていて、リクエスト時にヘッダーにTokenを突っ込めるようにしました。
ちなみに、Ruby on Railsではありませんが、GOのechoを利用した時に似たような実装をしていて、その備忘録を以下にまとめています。