- 投稿日:2020-07-01T23:56:10+09:00
ブックマークレットで今見ているページのTwitter投稿を補助する
ブックマークレットで今見ているページのTwitter投稿を補助する
ニュースやブログなど、何らかのサイトを見ていて
Twitterで投稿したいけど、シェアボタンが無くて不便!
ボタンはあるけど文章がついていないなどの経験はありませんか?
特に古い記事、ブログ以外のサイトなど。そんな時にこのブックマークレットがあれば、
タイトルまたはH1タグまたは囲んだテキスト+
URL付きの投稿画面を表示出来ます。下記のコードをコピペして、ブラウザのブックマークの
URLに貼り付ける事で利用できます。
※PC版Chromeで動作確認しています。2020.07.01現在選択しているテキストを文章とする版
javascript:tt=window.getSelection().toString();window.open('https://twitter.com/intent/tweet?url='+encodeURI(location.href)+'&text='+encodeURI(tt),'_blank')
ページタイトル版
javascript:tt=document.title;window.open('https://twitter.com/intent/tweet?url='+encodeURI(location.href)+'&text='+encodeURI(tt),'_blank')
H1があればH1のテキストを、なければページタイトルを使う版
javascript:tt="";if(document.getElementsByTagName('h1')[0]==undefined){tt=document.title;}else{tt=document.getElementsByTagName('h1')[0].textContent;}window.open('https://twitter.com/intent/tweet?url='+encodeURI(location.href)+'&text='+encodeURI(tt),'_blank')コード解説(H1版)
すごく単純なものです。h1タグが存在するかを確認し、
存在無ければタイトルを、存在すればh1内のテキストを使います。
tt="";if(document.getElementsByTagName('h1')[0]==undefined){tt=document.title;}else{tt=document.getElementsByTagName('h1')[0].textContent;}
location.hrefでURLを取得します。
日本語に対応するためURLもテキストもエンコードします。
window.open('https://twitter.com/intent/tweet?url='+encodeURI(location.href)+'&text='+encodeURI(tt),'_blank')
利用イメージと利用方法
1.適当なブックマークを作ります。名前も適度に付けます。
2.そのURLを上記のコードにします。Javascript:~を貼り付ける
3.投稿したいサイトを開いている時にこのブックマークを押す画像はGoogleが対象サイトだった場合の例です。
- 投稿日:2020-07-01T23:56:10+09:00
今見ているページのTwitter投稿を補助するブックマークレット【選択したテキスト+URL】
ニュースやブログなど、何らかのサイトを見ていて
Twitterで投稿したいけど、シェアボタンが無くて不便!
ボタンはあるけど文章がついていないなどの経験はありませんか?
特に古い記事、ブログ以外のサイトなど。そんな時にこのブックマークレットがあれば、ワンボタンで
タイトルまたはH1タグまたは【選択したテキスト】+
URL付きの投稿画面を表示出来ます。下記のコードをコピペして、ブラウザのブックマークの
URLに貼り付ける事で利用できます。
※PC版Chromeで動作確認しています。2020.07.01現在選択しているテキストを文章とする版
javascript:tt=window.getSelection().toString();window.open('https://twitter.com/intent/tweet?url='+encodeURI(location.href)+'&text='+encodeURI(tt),'_blank')
ページタイトル版
javascript:tt=document.title;window.open('https://twitter.com/intent/tweet?url='+encodeURI(location.href)+'&text='+encodeURI(tt),'_blank')
H1があればH1のテキストを、なければページタイトルを使う版
javascript:tt="";if(document.getElementsByTagName('h1')[0]==undefined){tt=document.title;}else{tt=document.getElementsByTagName('h1')[0].textContent;}window.open('https://twitter.com/intent/tweet?url='+encodeURI(location.href)+'&text='+encodeURI(tt),'_blank')コード解説(H1版)
すごく単純なものです。h1タグが存在するかを確認し、
存在無ければタイトルを、存在すればh1内のテキストを使います。
tt="";if(document.getElementsByTagName('h1')[0]==undefined){tt=document.title;}else{tt=document.getElementsByTagName('h1')[0].textContent;}
location.hrefでURLを取得します。
日本語に対応するためURLもテキストもエンコードします。
window.open('https://twitter.com/intent/tweet?url='+encodeURI(location.href)+'&text='+encodeURI(tt),'_blank')
利用イメージと利用方法
1.適当なブックマークを作ります。名前も適度に付けます。
2.そのURLを上記のコードにします。Javascript:~を貼り付ける
3.投稿したいサイトを開いている時にこのブックマークを押す画像はGoogleが対象サイトだった場合の例です。
- 投稿日:2020-07-01T23:41:21+09:00
firebase functionsから別のfunctionsをキックする
やりたいこと
- functionsから別のfunctionsをキックしたい
先に代替案
firebase-queue というものがあるので、条件に合えばこれを使っても良さそう
- firebase realtime databaseを利用している
- 4年くらい更新されていないので怖い
ユースケース例
この記事では下記のような挙動を例にします
- チャットアプリ
- ユーザーAがメッセージを投稿する
- 同じチャットルームに属するユーザーB, C, Dにpush通知が届く
具体的には下記のような処理フローになります
- ユーザーAがメッセージを投稿するとfirestoreにドキュメントがinsertされる
- ドキュメント作成のイベントを受けて、functionsAがキックされる
- functionsAの中で
- 同じルームのメンバー一覧を取得
- メンバーそれぞれについて、push通知を発行するfunctionBをキックする
functionA
実装のイメージです
// ポイント1: functionsの中だけど、クライアント用のSDKを利用している import firebaseClient from "firebase"; firebaseClient.initializeApp(client_sdk_config); const functionsClient = firebaseClient .app() .functions("asia-northeast1"); // ポイント2: timeoutを指定することで、functionsBをキックするけど結果を待たない、ということができる const functionB = functionsClient.httpsCallable( "functionB", { timeout: 1000 } // msec ); const pushTargetUsers = ["user-B", "user-C", "user-D"]; const pushSendTasks = pushTargetUsers.map(async (userId) => { try { await functionB(userId); } catch (error) { if (e.code === "deadline-exceeded") { // functionBの完了は待たないので、timeoutエラーは無視する return; } throw e; } }); await Promise.all(pushSendTasks);ポイント
- client用のSDKを使う
- functions内部では普通はadmin SDKしか利用しない
- しかしadmin SDKにはfunctionsを呼び出す機能がない
- なのでclient用のSDKを利用することでfunctionsを呼び出す
functionsを呼び出すときにtimeout値を必要に応じてセットする
- timeoutをセットしない場合
- functionBが完了するまで待つことになる
- この場合、functionBの返り値を受け取ることができる
- 返り値を受け取りたい場合、わざわざ別functionに分離するメリットはあまりないので、このパターンはあまり利用機会がなさそう
- timeoutをセットする場合
- timeout以内にfunctionBが完了しなかった場合、
deadline-exceeded
のエラーがスローされる- そのエラーは想定内なので握りつぶす
functionsのタイムアウトは2種類ある
- x: functionそのものの実行時間のタイムアウト
- y: functionを呼び出す側がレスポンスを受け取るまでのタイムアウト
- これはfunctionを呼び出す側が、呼び出し時にセットする
↑xのタイムアウトを60秒、yのタイムアウトを10秒にセットしてfunctionを呼び出す、みたいなことができる
その場合、下記のような挙動になる
- 10秒以内に処理が完了した場合
- functionは正常にレスポンスを返す
- 10秒以上かかった場合
- 呼び出し側ではyのタイムアウトエラーがスローされる
- しかし、キックされたfunction自体は10秒を超えても動いており、60秒以内に処理が完了すれば正常終了となる
感想
AWS LambdaではSDKが同期呼び出し、非同期呼び出しなどをサポートしており、もっとシンプルに同じことがやれます
firebase functionsでも同じことがやりたいと思って試行錯誤してこの結論に辿り着いたのですが、SDKでサポートしてくれるとありがたいですね...
また、そもそもfunctionからfunctionを直接呼び出すというのはあまり良い手段ではなく、AWSではstep functionsを使うべき場面だと思います
firebaseでも同様に別の手段を模索するべきなのかなとちょっと思いました
- 投稿日:2020-07-01T23:38:00+09:00
firestoreのデータをvue-chartでグラフ化する時にハマった
firestoreからデータを引っ張り出しvue-chartでレンダリングしようとした時に躓き時間をだいぶ割いてしまったので同じように悩んでる人が居れば(多分いない)助けになりたいと思い共有
データの流れ
API(openweathermap)→ firestore → vue-chart
絵画してみる
chart.vuemounted() { this.renderChart(this.chartData, this.options); },???????????状態
データはしかっりと入っている事を確認できる
色々やってみる①
watchで監視
chart.vuewatch: { data: { handler() { this.renderChart(this.chartData, this.options); }, deep: true, immediate: false } }mounted()では表示されずwatchで表示される
色々やってみる②
試しに数字べた書きしてみる
chart.vuedata() { return { loaded: false, Today: null, data: { labels: [1,2,3], datasets: [ { label: "osaka", data: [200,300,400,], },原因
データが完全に移動する前にレンダリングしようとしているのが原因でした。
タブの大きさ変更などで表示されていたのは、その時にはデータが移動完了していたからですねchart.vue<allchart v-if="loaded" :data="data" :options="options" />chart.vuesnapshot.forEach(doc => { this.data.labels.push(doc.data().Timestamp2); this.loaded = true; }これで解決できます
おまけ
公式をしっかり読みましょう
API呼び出しが非同期だということです。 この時、データが到着する前にあなたはチャートを表示しようとしてしまいます。
これを防ぐには、単純な v-ifが最善の解決策です。
Qiita読み漁る前に公式をしっかり読みましょう
- 投稿日:2020-07-01T23:20:25+09:00
JavaScriptのループ処理等で日付が変わったら処理するのはDate型のgetDay()を使うと楽
前提
arrayにはこういう構造でデータが入っているものとする
const messages = [ { "date": new Date("2020/06/29") }, { "date": new Date("2020/06/30") }, ]コード
dateのgetDay()関数で曜日を示すindexが取れ、それが日付が変わることを示すのでそれを使った判定が楽だと思います。(日を取って比較してもいいがこちらのほうが手間がかからない)
formatにはdayjsを使っていますが気にしないでください
let day messages.forEach((message) => { // 次の日になったら日付を出力する if (message.date.getDay() !== day) { console.log(`\n${dayjs(message.date).format("YYYY/MM/DD")}`) } day = message.date.getDay() });
- 投稿日:2020-07-01T23:20:25+09:00
JavaScriptで日付が変わったら処理する
改訂
以下に書いてある記事は思いつきで書いたものです、当初は毎日入っているデータに対して曜日を取れば処理できると思っていましたが、色々考えた結果普通にgetDateを使ったほうが同じ手間で簡単でした
前提
arrayにはこういう構造でデータが入っているものとする
const messages = [ { "date": new Date("2020/06/29") }, { "date": new Date("2020/06/30") }, ]曜日比較コード
dateのgetDay()関数で曜日を示すindexが取れ、それが日付が変わることを示すのでそれを使った判定が楽だと思います。(日を取って比較してもいいがこちらのほうが手間がかからない)
formatにはdayjsを使っていますが気にしないでください
let day messages.forEach((message) => { // 次の日になったら日付を出力する if (message.date.getDay() !== day) { console.log(`\n${dayjs(message.date).format("YYYY/MM/DD")}`) } day = message.date.getDay() });備考
- 曜日取得で判定しているので一週間分急にデータがなく過ぎたらバグる
改良版日付比較コード
dateのgetDay()関数で曜日を示すindexが取れ、それが日付が変わることを示すのでそれを使った判定が楽だと思います。(日を取って比較してもいいがこちらのほうが手間がかからない)
formatにはdayjsを使っていますが気にしないでください
let date messages.forEach((message) => { // 次の日になったら日付を出力する if (message.date.getDate() !== date) { console.log(`\n${dayjs(message.date).format("YYYY/MM/DD")}`) } date = message.date.getDate() });備考
- 日付の数字しか取れないので、一ヶ月急に過ぎたらバグるが多分現実にあまりないと思うので気にしなくていい
- 投稿日:2020-07-01T23:14:07+09:00
JavaScriptの基礎文法
下記の解説をします。
let btn = document.querySelector("button");
ノードオブジェクトのbuttonをbtnに代入しています。
function printHello() {
console.log("Hello world");
printHello関数を定義しています。
2番目の記述で、コンソールにHello worldと出力していますbtn.addEventListener("click", printHello);
buttonのノードオブジェクトに対して、clickイベントとprintHello関数を紐付ける仕組みであるイベントリスナを追加する。
DOM
DOMとはDocument Object Model(ドキュメントオブジェクトモデル)の略です。HTMLを解析し、データを作成する仕組みです。HTMLで書かれたファイルは結局ただの文字情報なので、そのまま表示しても意味がありません。HTMLを解析し、CSSやJavascriptによる装飾を行ってから画面に映すという工程が必要です。
ノード
HTML1つ1つのタグが、DOMツリーの中ではノードと呼ばれます。
document.getElementById("id名");
documentは、開いているWebページのDOMツリーが入っているオブジェクトです。documentに対していくつかのメソッドを利用することで、DOMツリーに含まれる要素を抽出して取得することができます。.getElementById("id名")はDOMツリーから特定の要素を取得するためのメソッドの1つです。引数に渡したidを持つ要素を取得します。
document.querySelector("セレクタ名");
セレクタ名とは、CSSでスタイルを適用するために指定している要素です。セレクタ名を指定してDOMを取得する場合、querySelectorメソッドを使用します。HTML上から、引数で指定したセレクタに合致するもののうち一番最初に見つかった要素1つを取得します。
addEventListener
(ノードオブジェクト).addEventListener("イベント名", 関数);
上記のような記述により、この記述の読み込み以降で「ノードオブジェクト」に「イベント」が起きた時、「関数」を実行するようになります。addEventListener
「ノードオブジェクト」に「イベント」が起きた時、「関数」を実行します。
- 投稿日:2020-07-01T23:00:59+09:00
[Javascript]おばあとおじいの1週間の献立を無理やり再帰にする準備をする。
背景
「どうせ学んでいるのならアウトプットせい!」と
お叱り愛の叱咤激励をもらいました。
ごもっともなので毎日投稿を習慣化してみる。自己紹介(=
情報の信頼度)2020年4月から学習をはじめた駆け出しエンジニア
HTML、CSS、Javascript、React、Vue、Laravelを学習
AWSもamplifyとかS3、EC2あたりをちょこっと。ふくおか。今日のおはなし
〜おじいおばあの家〜
おばあ「今週のごはんは何にしようか。」
おじい「精のつくものとらねばねえ。」
おじい「しかし、ついつい昨日のごはんも忘れてしまうとよ。困ったもんちゃ!」
おばあ「では、私が「1週間の献立」をつくってみようかね」
おばあ「最近、ぷろぐらみんぐ教室で学んだの。」
お題
[朝、昼、夜]の献立表をつくる。
献立表=[
[?、?、?][?、?、?][?、?、?]
[?、?、?][?、?、?][?、?、?]
[?、?、?][?、?、?][?、?、?]
[?、?、?][?、?、?][?、?、?]
[?、?、?][?、?、?][?、?、?]
[?、?、?][?、?、?][?、?、?]
[?、?、?][?、?、?][?、?、?]
[?、?、?][?、?、?][?、?、?]
[?、?、?][?、?、?][?、?、?]
]※おじいさんとおばあさんは、カレー、寿司、ラーメンしかたべません。
再帰を学ぼう!
再帰とは
関数が自分自身を呼ぶこと。
たとえば、繰り返し処理(forループ)を、
こう書き換えられる。
この場合、
n==3であれば、x*pow(2,3) //-> 2*2
n==2であれば、x*pow(2,3) //-> 2*2*2
n==1になるとき、x*pow(2,3)//-> 8となっている。
再帰を考えるときのコツ
再帰を終了する条件:ベースケース
再帰を継続する条件:リカーシブケース
と呼ぶらしい。再帰関数自体にベースケース、リカーシブケースに関係する値を引数としていれておくこと。
実際に再帰で考えてみよう!
おばあ「まず、全体の流れを理解しよう。」
おばあ「すべての献立パターンがつくれて、関数を実行すると「献立表」が返ってくる。」
おばあ「食べ物は、食糧庫からとってこよう。」
おばあ「朝、昼、夜、の3食決まったら再帰は終了だね。完食はしないからね」
おばあ「じゃあ、まだ朝、昼までしか決まっていないときは、再帰を継続する必要があるね。」
おばあ「これでよしっと。」
おばあ「繰り返し処理になっている部分は、、と。」
おばあ「各食事に対してpantryからすべての食事を取り出す。」
おばあ「まず、[?、?、?]をつくるには、」
おばあ「1回目の食事を[?]いれて、2回目の食事にする。」
おばあ「2回目の食事を[?]いれて、3回目の食事にする。」
おばあ「3回目の食事を[?]いれて、3食分の献立をresultにいれる。」
おばあ「このあたりが繰り返しの処理っぽいな。」
おばあ「次に、[[?、?、?]をつくる。]
おばあ「途中まで[?、?]をつくってるから、その上に対して?を入れたいけど、どうしようか」
おばあ「作成途中の献立[?、?]を受けとれるようにしよう。」
おばあ「作成途中の献立を受け取れるように引数を変更して、」
おばあ「それらに対してpantryから次の食事を加えよう。」
おばあ「これで、自分自身を呼ぶことで、配列を作るrecuse関数ができあがった」
その後
おばあ「meal()[1]」
おじい「もぐもぐ」
おばあ「meal()[4]」
おじい「もぐもぐ」
補足
pushではなく、concatで記述している理由は、配列のままにしたいからです。
pushだと要素数を返す
=>数字になる。
=>昼のごはんが選べなくなる
=cannnot read proptyとなるため避けています。recuse関数外にresultを定義しているのはクロージャーを使いたいためです。
後日記事にします。
解釈間違い等、何かあればぜひコメントください。
誰か再帰の使いみちを教えてください・・・。
- 投稿日:2020-07-01T22:45:58+09:00
Error ExecJS::RuntimeUnavailable: 発生時の対処法
発生現象
AWSのEC2でWebサーバ、アプリケーションサーバの設定時に、環境変数の設定をする際の
$ rake secret
を実行した際に下記Errorが発生。terminalExecJS::RuntimeUnavailable: Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes. /var/www/chat-space/config/application.rb:7:in `<top (required)>' /var/www/chat-space/Rakefile:4:in `require_relative' /var/www/chat-space/Rakefile:4:in `<top (required)>' (See full trace by running task with --trace)→Javascriptがうまく走っていないので、Node.jsをinstallする。
install確認
local環境にて
terminal$ node --version v12.16.1AWSの本番環境にもinstallする
terminalsudo yum install nodejs --enablerepo=epel ←実行 読み込んだプラグイン:priorities, update-motd, upgrade-helper amzn-main | 2.1 kB 00:00:00 amzn-updates | 3.8 kB 00:00:00 epel/x86_64/metalink | 5.3 kB 00:00:00 epel | 4.7 kB 00:00:00 nodesource | 2.5 kB 00:00:00 (1/3): epel/x86_64/group_gz | 74 kB 00:00:00 (2/3): epel/x86_64/updateinfo | 789 kB 00:00:00 (3/3): epel/x86_64/primary_db | 6.1 MB 00:00:00 1073 packages excluded due to repository priority protections 依存性の解決をしています --> トランザクションの確認を実行しています。 ---> パッケージ nodejs.x86_64 2:6.17.1-1nodesource を インストール --> 依存性の処理をしています: python >= 2.6 のパッケージ: 2:nodejs-6.17.1-1nodesource.x86_64 --> トランザクションの確認を実行しています。 ---> パッケージ python26.x86_64 0:2.6.9-2.89.amzn1 を インストール --> 依存性の処理をしています: libpython2.6.so.1.0()(64bit) のパッケージ: python26-2.6.9-2.89.amzn1.x86_64 --> トランザクションの確認を実行しています。 ---> パッケージ python26-libs.x86_64 0:2.6.9-2.89.amzn1 を インストール --> 依存性解決を終了しました。 依存性を解決しました ========================================================================================== Package アーキテクチャー バージョン リポジトリー 容量 ========================================================================================== インストール中: nodejs x86_64 2:6.17.1-1nodesource nodesource 13 M 依存性関連でのインストールをします: python26 x86_64 2.6.9-2.89.amzn1 amzn-main 5.8 M python26-libs x86_64 2.6.9-2.89.amzn1 amzn-main 697 k トランザクションの要約 ========================================================================================== インストール 1 パッケージ (+2 個の依存関係のパッケージ) 総ダウンロード容量: 20 M インストール容量: 59 M Is this ok [y/d/N]: y Downloading packages: 警告: /var/cache/yum/x86_64/latest/nodesource/packages/nodejs-6.17.1-1nodesource.x86_64.rpm: ヘッダー V4 RSA/SHA512 Signature、鍵 ID 34fa74dd: NOKEY nodejs-6.17.1-1nodesource.x86_64.rpm の公開鍵がインストールされていません (1/3): nodejs-6.17.1-1nodesource.x86_64.rpm | 13 MB 00:00:00 (2/3): python26-libs-2.6.9-2.89.amzn1.x86_64.rpm | 697 kB 00:00:00 (3/3): python26-2.6.9-2.89.amzn1.x86_64.rpm | 5.8 MB 00:00:01 ------------------------------------------------------------------------------------------ 合計 16 MB/s | 20 MB 00:00:01 file:///etc/pki/rpm-gpg/NODESOURCE-GPG-SIGNING-KEY-EL から鍵を取得中です。 Importing GPG key 0x34FA74DD: Userid : "NodeSource <gpg-rpm@nodesource.com>" Fingerprint: 2e55 207a 95d9 944b 0cc9 3261 5ddb e8d4 34fa 74dd Package : nodesource-release-el7-1.noarch (installed) From : /etc/pki/rpm-gpg/NODESOURCE-GPG-SIGNING-KEY-EL 上記の処理を行います。よろしいでしょうか? [y/N]y Running transaction check Running transaction test Transaction test succeeded Running transaction インストール中 : python26-libs-2.6.9-2.89.amzn1.x86_64 1/3 インストール中 : python26-2.6.9-2.89.amzn1.x86_64 2/3 インストール中 : 2:nodejs-6.17.1-1nodesource.x86_64 3/3 検証中 : 2:nodejs-6.17.1-1nodesource.x86_64 1/3 検証中 : python26-2.6.9-2.89.amzn1.x86_64 2/3 検証中 : python26-libs-2.6.9-2.89.amzn1.x86_64 3/3 インストール: nodejs.x86_64 2:6.17.1-1nodesource 依存性関連をインストールしました: python26.x86_64 0:2.6.9-2.89.amzn1 python26-libs.x86_64 0:2.6.9-2.89.amzn1 完了しました!以上で本番環境でもjavascriptが走るようになりました。
- 投稿日:2020-07-01T20:29:45+09:00
Denoでファイルダウンロードをする
最近Denoでファイルダウンロードをする必要があったので、実装しました。
てきとうに書いたのであまり良くない書き方をしているかもなので、その時はコメントでご指摘ください。結論
import ky from "https://deno.land/x/ky/index.js"; const url = "https://raw.githubusercontent.com/denolib/high-res-deno-logo/master/deno_hr.png"; const path = "deno.png"; ky.get(url) .arrayBuffer() .then(buffer => { Deno.writeFileSync(path, new Uint8Array(buffer)); }); console.log("Completed download!");kyでファイルを取得し、ArrayBufferからUint8Arrayにしたあと保存しています。
やってみる
無事にダウンロードできました。
- 投稿日:2020-07-01T18:14:13+09:00
ncestryによる多階層構造データを表示、投稿!! ~Ajax~
はじめに
ancestryで作成したカテゴリーデータを用いて、選択肢を動的に変化させる機能を実装しました。
学習メモとして投稿します。
まだ、理解が浅いところもありますが参考になればと思います!完成形
https://gyazo.com/8a5adc080698873d544b8665855c0901
以下が完成コードです!
routesresources :products, except: [:index] do get 'new/children_category', to: 'products#children_category' get 'new/grandchildren_category', to: 'products#grandchildren_category' endpuroducts_controllerbefore_action :set_categories, only: [:edit, :update] 〜省略〜 def children_category @children_category = Category.where(ancestry: params[:parent_category_id]) render json: @children_category end def grandchildren_category @grandchildren_category = Category.where(ancestry: "#{params[:parent_category_id]}/#{params[:children_category_id]}") render json: @grandchildren_category endpuroducts/new_html_haml.input-field__contents .input-field__contents-data %p.subline 商品の詳細 .input-field__contents-image__headline .headlabel = f.label :category_id, "カテゴリー" %span.necessary 必須 .sell__about__right__wrap-box.parent %select.select-box1#parent %option{value: 0} --- - @parents.each do |parent| %option{value: "#{parent.id}"} #{parent.name} .child %select.select-box2#child .grand_child .select-box3 = f.collection_select(:category_id, [], :id, :name, {prompt: "---"}, {id: "grand_child"})category_js$(function(){ let buildPrompt = `<option value>---</option>` let buildHtmlOption = function(parent) { let option = `<option value ="${parent.id}">${parent.name}</option>` return option } $('#parent').change(function() { let parent_id = $(this).val(); $.ajax({ type: 'GET', url: 'products/new/children_category', data: {parent_category_id: parent_id}, dataType: 'json' }) .done(function(parent) { $('.child').css('display', 'block'); $('#child').empty(); $('.grand_child').css('display', 'none'); $('#child').append(buildPrompt); parent.forEach(function(child) { var html_option = buildHtmlOption(child); $('#child').append(html_option); }); }) .fail(function() { alert('エラー') }); }); $(this).on("change", "#child", function() { let parent_id = $("#parent").val(); let child_id = $("#child").val(); $.ajax({ type: 'GET', url: 'products/new/grandchildren_category', data: { parent_category_id: parent_id, children_category_id: child_id }, dataType: 'json' }) .done(function(parent) { $('.grand_child').css('display', 'block'); $('#grand_child').empty(); $('#grand_child').append(buildPrompt); parent.forEach(function(child) { var html_option = buildHtmlOption(child); console.log(buildHtmlOption(html_option)); $('#grand_child').append(html_option); }); }) }); })考え方
・親カテゴリーを選択しイベントを発火させたら、子カテゴリーをappend(追加)する
・子カテゴリーを選択しイベントを発火させたら、孫カテゴリーをappend(追加)する
・ajaxを使用を子カテゴリー及び孫カテゴリーが表示されるための通り道を作成する
・最終的には孫カテゴリーの値が保存される様にするざっくりとこんな感じです。
では、一つ一つ見ていきましょう!
ルーティング
プログラムの処理の流れとして、最終的にviewに子カテゴリーと親カテゴリーを表示させます。
それは実際に、コントローラーとjsで処理を行いますのでリクエストが会った際のコントローラーへの通り道を作成します。routesresources :products, except: [:index] do #children_categoryアクションに行くためのパス get 'new/children_category', to: 'products#children_category' #grandchildren_categoryアクションに行くためのパス get 'new/grandchildren_category', to: 'products#grandchildren_category' endコントローラー
前提として、ajax処理行うのでjsでajax処理が行われたあとはコントローラーに行きます。
その際、コントローラーではカテゴリーの値を探してjsに返してあげる必要があります。
したがって、以下の様に書きます。puroducts_controllerbefore_action :set_categories, only: [:edit, :update] 〜省略〜 def children_category #.whereを使ってancestryから値を探して、インスタンス変数に代入する @children_category = Category.where(ancestry: params[:parent_category_id]) #ancestryから探した値をjsに返してあげる render json: @children_category end def grandchildren_category #.whereを使ってancestryから値を探して、インスタンス変数に代入する @grandchildren_category = Category.where(ancestry: "#{params[:parent_category_id]}/#{params[:children_category_id]}") #ancestryから探した値をjsに返してあげる render json: @grandchildren_category endJSの処理
jsでは、カテゴリーが選択されるたびにイベントが発火する様にします。
具体的に、
・親カテゴリーが選択されたら、イベントが発火し子要素のカテゴリー表示させる
・子カテゴリーが選択されたら、イベントが発火し孫要素のカテゴリー表示させる処理としては、イベントが発火したらajaxでコントローラーから値を取得しforEachで全てを表させる流れになります。
category_js//①=====HTMLで表示させるviewを定義=========================== $(function(){ let buildPrompt = `<option value>---</option>` let buildHtmlOption = function(parent) { let option = `<option value ="${parent.id}">${parent.name}</option>` return option } //================================================= //②=====親カテゴリーが選択され子カテゴリーを呼び出す処理============ $('#parent').change(function() { let parent_id = $(this).val(); //ajaxでコントローラーに送る $.ajax({ type: 'GET', url: 'products/new/children_category', data: {parent_category_id: parent_id}, dataType: 'json' }) //以下はコントローラーからのレスポンス後の処理 .done(function(parent) { $('.child').css('display', 'block'); $('#child').empty(); $('.grand_child').css('display', 'none'); $('#child').append(buildPrompt); //コントローラーから取得した値をforEachで全て取得し、.appendでHTML要素に追加する parent.forEach(function(child) { var html_option = buildHtmlOption(child); $('#child').append(html_option); }); }) .fail(function() { alert('エラー') }); }); //============================================= //②=====子カテゴリーが選択され孫カテゴリーを呼び出す処理============ $(this).on("change", "#child", function() { let parent_id = $("#parent").val(); let child_id = $("#child").val(); //ajaxでコントローラーに送る $.ajax({ type: 'GET', url: 'products/new/grandchildren_category', data: { parent_category_id: parent_id, children_category_id: child_id }, dataType: 'json' }) //以下はコントローラーからのレスポンス後の処理 .done(function(parent) { $('.grand_child').css('display', 'block'); $('#grand_child').empty(); $('#grand_child').append(buildPrompt); //コントローラーから取得した値をforEachで全て取得し、.appendでHTML要素に追加する parent.forEach(function(child) { var html_option = buildHtmlOption(child); console.log(buildHtmlOption(html_option)); $('#grand_child').append(html_option); }); }) }); //============================================= })最後には、HTML
HTMLで注意する点は、jsのid属性とHTMLでのid属性に齟齬かないかぐらいです。
ただし、最後の孫カテゴリーの値を保存するためには少し工夫が必要です。
puroducts/new_html_haml.input-field__contents .input-field__contents-data %p.subline 商品の詳細 .input-field__contents-image__headline .headlabel = f.label :category_id, "カテゴリー" %span.necessary 必須 .sell__about__right__wrap-box.parent %select.select-box1#parent %option{value: 0} --- # 親カテゴリーの値を全て表示させる - @parents.each do |parent| %option{value: "#{parent.id}"} #{parent.name} .child # #childのところにjsで定義したviewが挿入される %select.select-box2#child .grand_child .select-box3 # id:grand_childのところにjsで定義したviewが挿入される # また、選択孫カテゴリーの値が保存正しく保存されるために以下の様に書きます。 = f.collection_select(:category_id, [], :id, :name, {prompt: "---"}, {id: "grand_child"})補足で、以下の記述については以下のサイトを参考にしましたのでご確認ください
f.collection_select(:category_id, [], :id, :name, {prompt: "---"}, {id: "grand_child"}) #参考記述 #collection_select(オブジェクト名, メソッド名, 要素の配列, value属性の項目, テキストの項目 [, オプション or HTML属性 or イベント属性])参考記事:
https://railsdoc.com/page/collection_select終わりに
処理としては、そこまで複雑ではないため1つ1つ確認しながら行ったら上手く行きました!
もし、エラーや上手く値が取得できていない場合は、binding.pryや、console.log();、debuggerで確認してみてください!
ありがとうございました!
- 投稿日:2020-07-01T17:52:11+09:00
CSSアニメーションとJSで『崩壊するサイト』を簡単に作る
今回はサイトに崩壊するイースターエッグ(隠し要素)みたいな物を、基本的なコードのみで実装してみたので方法をまとめます。
なので初学者の方でも比較的分かりやすいかと思います。インフラエンジニア「もっと手軽に崩壊できる。」
CSS職人「こんな崩壊にしてみました。」
やらかしエンジニア「崩壊しました。」
な話がある場合はぜひ教えてください!(自分は最近やらかしました。)
? 作ったもの
■ サンプルサイトURL(PCのみ)
https://aocattleya.github.io/Collapse-Site■ GitHub
https://github.com/aocattleya/Collapse-Siteサイト内のキャラクターアイコンをクリックすると、サイトが崩壊します。
今回は解説がしやすいのでjQueryで解説します。
アイデア重視で実際やってることは簡単なので、Vue.jsなどでも簡単に応用出来ます。
簡単な仕組み解説
クリックした場合にフェードアウトするアニメーションのクラスを追加する。
もっと簡単にいうと、クリックでクラスを追加する。
使用している物 説明 HTML, CSS --- javaScript jQuery, Vue.js, その他
クラスを追加出来れば何でもOKAnimate.css CSSアニメーションを簡単に
実装出来るライブラリ
Animate.css
もちろん自分でアニメーションを作っても良いですが、難しいアニメーションなんて作れない!という方に、フェードインや跳ねたりのアニメーションをクラスを付けるだけで簡単に実装出来るライブラリがあります。
・Animate.css(公式サイト)
https://daneden.github.io/animate.css簡単に使い方の紹介です。
1、ライブラリを読み込む
ダウンロードして読み込みも出来ますが、CDN(ヘッダーにURL置くだけ)でも使用出来ます。index.html<head> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.min.css"> </head>CDNのバージョンによってクラス名が違ったりするので公式のREADMEにて
npmなど他の方法でインストールしたい場合なども書かれています。
→ animate.css(GitHub)2、クラスを付ける
アニメーションを使用したいクラスに、
animate__animated
とアニメーションのクラス
を付与index.html<div class="animate__animated animate__fadeInUp">フェードインするよ</div>これでページを読み込んだ時に、すでに反映されています。簡単!
どんなアニメーションのクラスがあるかは、公式サイトで実際の動きを確認できます。
スピードを変えたりなどのオプションもあります。
→ 公式サイト
? クリックで落下
Animate.cssで簡単にアニメーションが実装出来る事が分かったところで、
次に、要素をクリックしてアニメーションのクラスを追加し、フェードアウトさせます。何でもOKですが、今回はjQueryでクラス追加してみます。
index.html<div class="collapse"> <div class="button">ボタン</div> </div>style.css.collapse { width: 200px; height: 200px; background-color: lightblue; }
button
クラスをクリックすると、
collapse
クラスにanimate__animated animate__hinge
の2つのクラスを追加する。※
hinge
は、animete.cssの中のポロッと落下するアニメーションの1つです。main.js// クリックでクラス追加 $('.button').click(function() { $(".collapse").addClass("animate__animated animate__hinge"); })
サンプル全部見たい人用(クリックで開く)
index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <link rel="stylesheet" href="style.css" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.min.css" /> <title>クリックで崩壊</title> </head> <body> <div class="collapse"> <div class="button">クリック</div> </div> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script src="main.js"></script> </body> </html>style.css.collapse { width: 200px; height: 200px; background-color: lightblue; }main.js$('.button').click(function() { $(".collapse").addClass("animate__animated animate__hinge"); })基本はこれだけです。
あとはアニメーションを要素に追加するタイミングを変えて全体が崩壊していくように見せます。
? 連続で落下し崩壊
ボタンクリック後に2つ目を一定時間後に実行させます。
一定時間後に特定の処理を行うにはsetTimeout()
を使います。index.html<div class="contents"> <div class="collapse1"> <div class="button">クリック</div> </div> <div class="collapse2"></div> </div>main.js$(".button").click(function () { // 1つ目に追加 $(".collapse1").addClass("animate__animated animate__hinge"); // 2つ目に追加(1秒後) setTimeout(function () { $(".collapse2").addClass("animate__animated animate__hinge"); }, 1000); });
今回はシンプルにこんな形で、あとはアニメーションをさせたい要素に好きなタイミングで付けていくと崩壊するサイトが完成です!
? まとめ
大きい要素をほど重くなるので、やりすぎは注意してください。
やっていること自体はとても簡単な物ですが、アイデア次第でとても面白い事が出来ますね!エイプリールフールネタなどに使ってみたりも面白いと思います。
他にも少し応用して、5回クリック後に崩壊させてみたり
main.jslet count = 0; $(".button").click(function () { count += 1; // クリックでcountが5回になったら if (count === 5) { $(".collapse1").addClass("animate__animated animate__hinge"); setTimeout(function () { $(".collapse2").addClass("animate__animated animate__hinge"); }, 1000); } });
画像を変えたり、別のアニメーションを追加するなど色々カスタマイズして、
自分ポートフォリオなどに実装しても面白いかもしれませんね!
(キャラアイコンをクリックし過ぎると怒って崩壊するイースターエッグ)
リンク
記事のLGTMなど貰えたらとても励みになります。
GitHub
https://github.com/aocattleya
https://twitter.com/aocattleya
? Qiita
https://qiita.com/aocattleya
- 投稿日:2020-07-01T17:52:11+09:00
CSSアニメーションとJSで崩壊するサイトを簡単に作る
今回はサイトに崩壊するイースターエッグ(隠し要素)みたいな物を、基本的なコードのみで実装してみたので方法をまとめます。
なので初学者の方でも比較的分かりやすいかと思います。インフラエンジニア「もっと手軽に崩壊できる。」
CSS職人「こんな崩壊にしてみました。」
やらかしエンジニア「崩壊しました。」
な話がある場合はぜひ教えてください!(自分は最近やらかしました。)
? 作ったもの
■ サンプルサイトURL(PCのみ)
https://aocattleya.github.io/Collapse-Site■ GitHub
https://github.com/aocattleya/Collapse-Siteサイト内のキャラクターアイコンをクリックすると、サイトが崩壊します。
今回は解説がしやすいのでjQueryで解説します。
アイデア重視で実際やってることは簡単なので、Vue.jsなどでも簡単に応用出来ます。
簡単な仕組み解説
クリックした場合にフェードアウトするアニメーションのクラスを追加する。
もっと簡単にいうと、クリックでクラスを追加する。
使用している物 説明 HTML, CSS --- javaScript jQuery, Vue.js, その他
クラスを追加出来れば何でもOKAnimate.css CSSアニメーションを簡単に
実装出来るライブラリ
Animate.css
もちろん自分でアニメーションを作っても良いですが、難しいアニメーションなんて作れない!という方に、フェードインや跳ねたりのアニメーションをクラスを付けるだけで簡単に実装出来るライブラリがあります。
・Animate.css(公式サイト)
https://daneden.github.io/animate.css簡単に使い方の紹介です。
1、ライブラリを読み込む
ダウンロードして読み込みも出来ますが、CDN(ヘッダーにURL置くだけ)でも使用出来ます。index.html<head> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.min.css"> </head>CDNのバージョンによってクラス名が違ったりするので公式のREADMEにて
npmなど他の方法でインストールしたい場合なども書かれています。
→ animate.css(GitHub)2、クラスを付ける
アニメーションを使用したいクラスに、
animate__animated
とアニメーションのクラス
を付与index.html<div class="animate__animated animate__fadeInUp">フェードインするよ</div>これでページを読み込んだ時に、すでに反映されています。簡単!
どんなアニメーションのクラスがあるかは、公式サイトで実際の動きを確認できます。
スピードを変えたりなどのオプションもあります。
→ 公式サイト
? クリックで落下
Animate.cssで簡単にアニメーションが実装出来る事が分かったところで、
次に、要素をクリックしてアニメーションのクラスを追加し、フェードアウトさせます。何でもOKですが、今回はjQueryでクラス追加してみます。
index.html<div class="collapse"> <div class="button">ボタン</div> </div>style.css.collapse { width: 200px; height: 200px; background-color: lightblue; }
button
クラスをクリックすると、
collapse
クラスにanimate__animated animate__hinge
の2つのクラスを追加する。※
hinge
は、animete.cssの中のポロッと落下するアニメーションの1つです。main.js// クリックでクラス追加 $('.button').click(function() { $(".collapse").addClass("animate__animated animate__hinge"); })
サンプル全部見たい人用(クリックで開く)
index.html<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8" /> <link rel="stylesheet" href="style.css" /> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.0.0/animate.min.css" /> <title>クリックで崩壊</title> </head> <body> <div class="collapse"> <div class="button">クリック</div> </div> <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script> <script src="main.js"></script> </body> </html>style.css.collapse { width: 200px; height: 200px; background-color: lightblue; }main.js$('.button').click(function() { $(".collapse").addClass("animate__animated animate__hinge"); })基本はこれだけです。
あとはアニメーションを要素に追加するタイミングを変えて全体が崩壊していくように見せます。
? 連続で落下し崩壊
ボタンクリック後に2つ目を一定時間後に実行させます。
一定時間後に特定の処理を行うにはsetTimeout()
を使います。index.html<div class="contents"> <div class="collapse1"> <div class="button">クリック</div> </div> <div class="collapse2"></div> </div>main.js$(".button").click(function () { // 1つ目に追加 $(".collapse1").addClass("animate__animated animate__hinge"); // 2つ目に追加(1秒後) setTimeout(function () { $(".collapse2").addClass("animate__animated animate__hinge"); }, 1000); });
今回はシンプルにこんな形で、あとはアニメーションをさせたい要素に好きなタイミングで付けていくと崩壊するサイトが完成です!
? まとめ
大きい要素をほど重くなるので、やりすぎは注意してください。
やっていること自体はとても簡単な物ですが、アイデア次第でとても面白い事が出来ますね!エイプリールフールネタなどに使ってみたりも面白いと思います。
他にも少し応用して、5回クリック後に崩壊させてみたり
main.jslet count = 0; $(".button").click(function () { count += 1; // クリックでcountが5回になったら if (count === 5) { $(".collapse1").addClass("animate__animated animate__hinge"); setTimeout(function () { $(".collapse2").addClass("animate__animated animate__hinge"); }, 1000); } });
画像を変えたり、別のアニメーションを追加するなど色々カスタマイズして、
自分ポートフォリオなどに実装しても面白いかもしれませんね!
(キャラアイコンをクリックし過ぎると怒って崩壊するイースターエッグ)
リンク
記事のLGTMなど貰えたらとても励みになります。
GitHub
https://github.com/aocattleya
https://twitter.com/aocattleya
? Qiita
https://qiita.com/aocattleya
- 投稿日:2020-07-01T16:53:43+09:00
Electronをセキュアな設定の状態で、jsのrequierを行う
Electronの基本おさらい
Electronは、
メインプロセス
と、そこから読みだされwebページ画面として動くレンダラープロセス
の2つの領域が存在します。
そのうち、表示を行うレンダラープロセスはセキュリティを厳しくすることが推奨されています。Electronのセキュリティ設定
Electronのドキュメント見ると推奨のセキュリティ設定が上げられています。
https://www.electronjs.org/docs/tutorial/security#reporting-security-issuesしかし、デフォルトとのセキュリティ要件だとレンダラー領域でrequierもできずnodeのライブラリを読めません。
もちろん、require('electron')
もできないのでレンダラープロセスとメインプロセスとの通信もできません。requireを渡す
preloadを経由することでrequireを渡すことができます
electronの仕様で、preload内でcontextBridgeを実行することでレンダリングプロセスのjsに紐づきます。
"windows.xx"のxxの部分に紐付けることができます。requireを使う例です。
メインプロセス//レンダラー読み出し部分 const mainWindow = new BrowserWindow({ width: 800, height: 600, webPreferences: { preload: path.join(__dirname, 'preload.js'),//<--ファイルを指定。ここでは同一階層にあるpreload.js contextIsolation: true,//<--requireを渡すために必要な設定 } })preload.jsの例
preload.jsconst { contextBridge, ipcRenderer} = require("electron") const fs = require('fs') contextBridge.exposeInMainWorld( "requires", {// <-- ここでつけた名前でひもづく。ここでは"window.requires" json5 : require("json5"),//npmで取得したライブラリを渡す時の例。レンダラーにそのまま渡す ipcRenderer : ipcRenderer,//ipcRendererも渡せるのでやり取りできる getSetting : () => {// fsも読み込める。レンダリングプロセスにそのまま渡さず、functionにしてできることを制限したほうがセキュリアそう。。。 const setting_path = 'c:/appSetting.json5'; return fs.existsSync(setting_path) ? fs.readFileSync(setting_path, 'utf8') : '{}' } } );レンダリングプロセス側の読み出し
レンダリングプロセスconst json5 = window.requires.json5;//requireしたライブラリを読み込み let app_setting_text = window.requires.getSetting();//preload内のファンクション実行 let app_setting = json5.parse( app_setting_text );// 普通にrequireしたように使える console.log( app_setting );まとめ
一工夫いるがElectronのセキュリティ設定を緩めずに、requiresを使用できました。
- 投稿日:2020-07-01T16:49:43+09:00
【javascript】 テンプレートリテラル とは 一言で。
【ゴール】
テンプレートリテラルの理解
【テンプレートリテラルとは、、、】
定数や変数を楽して出力できる
不使用の場合
*「+」を2回も。。面倒だな。。。hoge.jsconst name = "太郎"; console.log("私の名前は" + name +"です"); //私の名前は太郎ですが出力使用した場合
hoge.jsconst name = "太郎"; console.log(`私の名前は${name}です`); //私の名前は太郎ですが出力【具体的な使い方】
①console.log()の括弧内を「``」(バッククォーテーション)で囲む
※Macの方は「command + @」です②変数や、定数で指定したものを${}で囲む
以上。意外と使います。
【合わせて読みたい】
■ 【Javascript】 メソッド まとめ 基礎基本コード メモ
https://qiita.com/tanaka-yu3/items/2438798d159fa402b1d5■ 【Javascript】JS 変数 定数 違い 一言でまとめました
https://qiita.com/tanaka-yu3/items/51b8b0630a1e4e2d52c8■ 【JavaScript】 js 繰り返し文 for / while/ case
https://qiita.com/tanaka-yu3/items/942d9d4838ebe14be1c2
- 投稿日:2020-07-01T16:04:37+09:00
JavaScriptのデータ型を変換するの一番簡単な方法
JavaScriptのデータ型を変換するの一番簡単な方法がこの記事で説明します。
また、本記事で紹介するコードスニペットはV8エンジン(V8 8.1.307.32)を搭載するGoogle Chromeのバージョン81.0.4044.138 (64-bit)で検証されていました。
JavaScript は弱い型付けあるいは動的型付けの言語です。javascriptのどのデータも、他のデータ型のコンテキストに意味を持っていますのでデータ型変換するが簡単です。
文字列に変換する
+
演算子と空文字列を使って文字列に変換することができます。
例えば:
let val = 1 + "";
console.log(val); // 結果: "1"
console.log(typeof val); // データ型: "string"
数値に変換する:
データか変数の前
+
演算子を使ってそのデータとデータ型が数値になります。
例えば:
let val = "15.3";
val = +val;
console.log(val); // 結果: 15.3
console.log(typeof val); //データ型: "number"
浮動小数点数から整数に変換する:
2つ以上方法があります。しかし、ビット演算子の範囲は32ビットまでです。
1つ目は、データか変数の後に|
演算子と0
を追加してそのデータが浮動小数点数から整数になります。
例えば:
let pval = 23.9 | 0;
let nval = -23.9 | 0;
console.log( pval ); // 結果: 23
console.log( nval) ; // 結果: -23
2つ目は、データか変数の前に〜〜
演算子を追加してそのデータが浮動小数点数から整数になります。
例えば:
console.log( 〜〜23.9 ); // 結果: 23
console.log( 〜〜-23.9 ); // 結果: -23
ブール型に変換する:
データか変数の前に
!!
演算子を追加してそのデータとデータ型がブーリアンになります。
例えば:
let isFalse = !!0;
console.log(isFalse); // 結果: false
console.log(typeof isFalse); // データ型: "boolean"
- 投稿日:2020-07-01T15:20:49+09:00
IndexedDB + Vue CLI, Chart.js で、測定値の表示 ファイルのインポート可
概要
前と同様、IndexedDB + Vue CLIで
Dexie.js ライブラリを使用した構成となり。
測定値の登録、chart.jsグラフ表示・ブラウザ内 IndexedDBデータの エクスポート、インポート機能で
jsonファイル経由で可能で、
別PCや、外出先PCのブラウザにインポートできます。構成
Chrome 83
Vue CLI
dexie : 3.0.1
vue: 2.6.11
vue-router
chart.js
SinglePageApplication / SPA
Progressive Web Apps / PWA
参考
https://nori-life.com/vue-cli-chart-js/
npm 追加
Vue CLIで、chart.jsだけでは、描画できず。
vue-chartjs ライブラリも。追加しました。npm install vue-chartjs chart.js --savepackage.json
https://github.com/kuc-arc-f/vue_spa3b_4mdats/blob/master/package.json
chart.js sample
・canvas が、動作しないようで
chart部分を、コンポートネントにする例で。描画できそうでした
親の呼出し側chart_sample.vue
https://github.com/kuc-arc-f/vue_spa3b_4mdats/blob/master/src/components/IndexMdats/chart_sample.vueimport ChartView from './ChartView' // export default { name: 'LineSample', data () { return { } }, components: { ChartView } }・子の chart
ChartView.vue
https://github.com/kuc-arc-f/vue_spa3b_4mdats/blob/master/src/components/IndexMdats/ChartView.vueimport { Line } from 'vue-chartjs' export default { extends: Line, mounted () { this.renderChart({ labels: ['January', 'February', 'March', 'April', 'May', 'June', 'July'], datasets: [ { label: 'Data-1', fill: false, backgroundColor: '#FF6384', borderColor: '#FF6384', data: [40, 39, 10, 40, 39, 80, 40] } ] }, { responsive: true, maintainAspectRatio: false } ) } }画面
・リスト
上部分に、エクスポート、インポートの
ボタンを配置
Vue components
・create
https://github.com/kuc-arc-f/vue_spa3b_4mdats/blob/master/src/components/IndexMdats/new.vue・index
https://github.com/kuc-arc-f/vue_spa3b_4mdats/blob/master/src/components/IndexMdats/Index.vue・chart
https://github.com/kuc-arc-f/vue_spa3b_4mdats/blob/master/src/components/IndexMdats/chart.vue
参考のページ
https://knaka0209.hatenablog.com/entry/indexed_db_4mdats
IndexedDB + Dexie.js で CRUDの作成。ファイルインポート可、Vue CLI版
https://qiita.com/knakaqi/items/765a1fb37a53a26278e9
- 投稿日:2020-07-01T14:59:14+09:00
指定したHTML要素にスクロール
JavaScript で指定した HTML 要素にスクロールさせます。自動的にフォーカスを移動させることなどを想定しています。
よくある処理だと思いますが、そのものズバリのサンプルが見当たらなかったので、実装を残しておきます。
See the Pen ensureVisible by 七誌 (@7shi) on CodePen.
このサンプルでは [Start] をクリックすると、0.5 秒ごとに選択位置を移動して、それに追随してスクロールします。
実装
.NET Framework の
ListView
にはEnsureVisible
というメソッドがあり、指定した項目が画面外にある場合はスクロールして表示することができます。それを真似て HTML でも
ensureVisible
を実装しました。マージンと HTMLElement を指定します。HTMLElement は複数指定できます。function ensureVisible(margin, ...elems) { let rs = elems.map((elem) => elem.getBoundingClientRect()); let tp = Math.min(...rs.map((r) => r.top )) - margin; let bt = Math.max(...rs.map((r) => r.bottom)) + margin; if (tp < 0) { let top = pageYOffset + tp; scroll({ top: top, behavior: "smooth" }); } if (bt > innerHeight) { let top = pageYOffset + tp - (innerHeight - (bt - tp)); scroll({ top: top, behavior: "smooth" }); } }位置の指定
getBoundingClientRect は画面上の位置(相対座標)を取得します。スクロールすると変化します。
scroll はドキュメント上の位置(絶対座標)を指定します。そのため表示の開始位置
pageYOffset
を足すことで補正します。今回の実装ではスムーズスクロールを利用しています。scroll({ top: top, behavior: "smooth" });スクロール量を指定する scrollBy もありますが、スムーズスクロールをサポートしていないため利用しませんでした。
サンプル
サンプルでは 0.5 秒のウェイトを Promise で処理しています。
function wait(timeout) { return new Promise((resolve, reject) => setTimeout(resolve, timeout)); } start.onclick = async () => { for (let td of Array.from(table.getElementsByTagName("td"))) { ensureVisible(20, td); td.classList.add("selected"); await wait(500); td.classList.remove("selected"); } ensureVisible(20, moveToTop); };詳細は以下の記事を参照してください。
経緯
音声合成の読み上げ位置を追うために実装しました。
- 投稿日:2020-07-01T14:47:44+09:00
rubyの変数をjavascriptで使う。
- 投稿日:2020-07-01T13:48:11+09:00
【chart.js】X軸の間隔の調整
ラズパイで室温と湿度ラズパイのCPU温度をchart.jsで書いてみたけど、X軸の間隔が微妙すぎる。
理想は1時間単位のX軸にしたい。
取得時の時刻をタイムスタンプにする
取得時の日時をタイムスタンプとすることでX軸でのメモリ幅のオプション
stepSize
に任意な数字を入れて区切ることができるのではないかと思った。...中略... ticks: { // 目盛り // fontColor: "red", // 目盛りの色 // fontSize: 14 // フォントサイズ begintZero: true, minRotation: 0, // ┐表示角度水平 maxRotation: 0, // ┘ maxTicksLimit: 24, stepSize: 20, } ...中略...しかし、Y軸のstepSizeは機能するのにX軸のstepSizeが機能しない。
Y軸の区切りを10単位に変更。X軸区切りは1500000000
Y軸の区切りが20単位に変更。X軸区切りは1500000000
このようにX軸の区切りはなぜか変わらない。
chart.jsの使い方から始めなければいけないのか。。
- 投稿日:2020-07-01T12:50:03+09:00
公式サポートされたM5Stack 用obnizOSをインストールしてみよう!
2020/7/1にリリースされたobnizOS 3.3.0より、公式でM5StackやM5Stickを扱えるようになりました。
いままでだってうごいてたじゃんか!っていうのはありますが、今回のアップデートでちゃんと起動時にディスプレイから設定ができるようになったりして、いままで不便だったところが解決して obniz Board と同じように扱えるようになりました!!
公式サポートになってできることを確認してみましょう。
- 起動直後のディスプレイ対応
- スイッチのサポート
- 設定画面のサポート
- Wi-Fiの設定画面のサポート
だいぶ便利になったのがわかるでしょうか?
さてそんな便利になったobnizOSをインストールしてみましょう。
インストール済みのデバイスを購入した場合は、次の項目の「obnizOSのセットアップ」は不要になります。
obnizOSのセットアップ
obniz-cliを使い、M5StackやM5Stickにセットアップをしていきます。
まず、obniz-cliをインストールしていきます。
詳しい使い方は次のサイトをご覧ください。
https://docs.obniz.io/ja/guides/obnizos-for-esp32/quick-start/
https://github.com/obniz/obniz-cli
obniz-cliのインストール
前もって次の環境を整えてください。
- python 2.7以降 or python 3.4以降
- esptoolのインストール
pip install esptool
でインストールできます- Node.js 12以降
obniz-cliをインストールします。次のコマンドにてインストールできます。
npm i obniz-cli -gobniz-cliでobnizOSをインストールする
obniz-cliでM5StackやM5StickにobnizOSをインストールしていきます。
まずは、USBでPCとM5Stackを接続します。
どのポートに接続しているか確認しましょう。
obniz-cli os:ports
と入力すると書き込めそうなポートを教えてくれます。何もでない場合は必要なドライバーがインストールされていないかもしれません。M5StackやM5Stickであれば、下記のリンクの「CP2104 Driver」からダウンロードできます。
$ obniz-cli os:ports ===Found Serial Ports=== COM11こんな感じで確認できます。この例であればCOM11がそのポートになります。
複数あるときは抜き差したときの差分を確認して判断してください。
あと、サインインする必要があります!
$ obniz-cli signin
上記のコマンドを実行すると、ブラウザが表示されます。
ログインしてくださいと出るのでログインして許可してあげてください。
ログインが完了すると、次のようにログインできたことが確認できます!
$ obniz-cli signin Local Server Created xxxx Authenticating... Logged in as "xxxxx@xxx.com"接続できていることを確認したうえで、obnizOSをインストールしていきます。
設定項目がありますので、確認してください。
これから書き込むものによって
-h
以降で適切なものを選んでください。
- M5Stack : m5stack_basic
- M5StickC : m5stickc
- DevKitC : esp32w
あとは先ほど確認した、ポートを入力してください。
-p
以降でポートを入力します。$ obniz-cli os:flash-create -h m5stickc -p COM11実行するとこんな感じでログが出ます!
$ obniz-cli os:flash-create -h m5stickc -p COM11 3.2.1 is the latest for m5stickc. going to use it. Downloading https://s3-ap-northeast-1.amazonaws.com/obniz-os/obnizos__m5stickc__3.2.1.bin Downloading https://s3-ap-northeast-1.amazonaws.com/obniz-os/obnizos__m5stickc__3.2.1__bootloader.bin Downloading https://s3-ap-northeast-1.amazonaws.com/obniz-os/obnizos__m5stickc__3.2.1__partition.bin *** flashing obnizOS serialport: COM11 baud: 1500000 hardware: m5stickc version: 3.2.1 *** esptool.py v2.8 Serial port COM11 Connecting.... Chip is ESP32D0WDQ6 (revision 1) Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None Crystal is 40MHz MAC: 3c:71:bf:9a:1f:6c Uploading stub... Running stub... Stub running... Changing baud rate to 1500000 Changed. Configuring flash size... Auto-detected Flash size: 4MB Compressed 10928 bytes to 7680... Wrote 10928 bytes (7680 compressed) at 0x00001000 in 0.1 seconds (effective 1390.8 kbit/s)... Hash of data verified. Compressed 757712 bytes to 497701... Wrote 757712 bytes (497701 compressed) at 0x00010000 in 6.7 seconds (effective 907.9 kbit/s)... Hash of data verified. Compressed 3072 bytes to 137... Wrote 3072 bytes (137 compressed) at 0x00008000 in 0.0 seconds (effective 3508.7 kbit/s)... Hash of data verified. Leaving... Hard resetting via RTS pin... *** created one device on obniz Cloud. obnizID: xxxx-xxxxx region: jp description: obniz-cli going to flash Devicekey to connected device. ***インストールできたら、ディスプレイに情報が表示されるのを確認できるでしょう!
obnizOSの設定
インストールが完了したらobnizOSの設定をしていきます。
インストール済みのものを購入した場合は、この項目からセットアップをしていきましょう!
一旦こちらのクイックスタートをごらんください!!
- 投稿日:2020-07-01T12:50:03+09:00
公式サポートされた M5Stack + M5StickC 用obnizOSをインストールしてみよう!
2020/7/1にリリースされたobnizOS 3.3.0より、公式でM5StackやM5Stickを扱えるようになりました。
いままでだってうごいてたじゃんか!っていうのはありますが、今回のアップデートでちゃんと起動時にディスプレイから設定ができるようになったりして、いままで不便だったところが解決して obniz Board と同じように扱えるようになりました!!
公式サポートになってできることを確認してみましょう。
- 起動直後のディスプレイ対応
- スイッチのサポート
- 設定画面のサポート
- Wi-Fiの設定画面のサポート
だいぶ便利になったのがわかるでしょうか?
さてそんな便利になったobnizOSをインストールしてみましょう。
インストール済みのデバイスを購入した場合は、次の項目の「obnizOSのセットアップ」は不要になります。
obnizOSのセットアップ
obniz-cliを使い、M5StackやM5Stickにセットアップをしていきます。
まず、obniz-cliをインストールしていきます。
詳しい使い方は次のサイトをご覧ください。
https://docs.obniz.io/ja/guides/obnizos-for-esp32/quick-start/
https://github.com/obniz/obniz-cli
obniz-cliのインストール
前もって次の環境を整えてください。
- python 2.7以降 or python 3.4以降
- esptoolのインストール
pip install esptool
でインストールできます- Node.js 12以降
obniz-cliをインストールします。次のコマンドにてインストールできます。
npm i obniz-cli -gobniz-cliでobnizOSをインストールする
obniz-cliでM5StackやM5StickにobnizOSをインストールしていきます。
まずは、USBでPCとM5Stackを接続します。
どのポートに接続しているか確認しましょう。
obniz-cli os:ports
と入力すると書き込めそうなポートを教えてくれます。何もでない場合は必要なドライバーがインストールされていないかもしれません。M5StackやM5Stickであれば、下記のリンクの「CP2104 Driver」からダウンロードできます。
$ obniz-cli os:ports ===Found Serial Ports=== COM11こんな感じで確認できます。この例であればCOM11がそのポートになります。
複数あるときは抜き差したときの差分を確認して判断してください。
あと、サインインする必要があります!
$ obniz-cli signin
上記のコマンドを実行すると、ブラウザが表示されます。
ログインしてくださいと出るのでログインして許可してあげてください。
ログインが完了すると、次のようにログインできたことが確認できます!
$ obniz-cli signin Local Server Created xxxx Authenticating... Logged in as "xxxxx@xxx.com"接続できていることを確認したうえで、obnizOSをインストールしていきます。
設定項目がありますので、確認してください。
これから書き込むものによって
-h
以降で適切なものを選んでください。
- M5Stack : m5stack_basic
- M5StickC : m5stickc
- DevKitC : esp32w
あとは先ほど確認した、ポートを入力してください。
-p
以降でポートを入力します。$ obniz-cli os:flash-create -h m5stickc -p COM11実行するとこんな感じでログが出ます!
$ obniz-cli os:flash-create -h m5stickc -p COM11 3.2.1 is the latest for m5stickc. going to use it. Downloading https://s3-ap-northeast-1.amazonaws.com/obniz-os/obnizos__m5stickc__3.2.1.bin Downloading https://s3-ap-northeast-1.amazonaws.com/obniz-os/obnizos__m5stickc__3.2.1__bootloader.bin Downloading https://s3-ap-northeast-1.amazonaws.com/obniz-os/obnizos__m5stickc__3.2.1__partition.bin *** flashing obnizOS serialport: COM11 baud: 1500000 hardware: m5stickc version: 3.2.1 *** esptool.py v2.8 Serial port COM11 Connecting.... Chip is ESP32D0WDQ6 (revision 1) Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None Crystal is 40MHz MAC: 3c:71:bf:9a:1f:6c Uploading stub... Running stub... Stub running... Changing baud rate to 1500000 Changed. Configuring flash size... Auto-detected Flash size: 4MB Compressed 10928 bytes to 7680... Wrote 10928 bytes (7680 compressed) at 0x00001000 in 0.1 seconds (effective 1390.8 kbit/s)... Hash of data verified. Compressed 757712 bytes to 497701... Wrote 757712 bytes (497701 compressed) at 0x00010000 in 6.7 seconds (effective 907.9 kbit/s)... Hash of data verified. Compressed 3072 bytes to 137... Wrote 3072 bytes (137 compressed) at 0x00008000 in 0.0 seconds (effective 3508.7 kbit/s)... Hash of data verified. Leaving... Hard resetting via RTS pin... *** created one device on obniz Cloud. obnizID: xxxx-xxxxx region: jp description: obniz-cli going to flash Devicekey to connected device. ***インストールできたら、ディスプレイに情報が表示されるのを確認できるでしょう!
obnizOSの設定
インストールが完了したらobnizOSの設定をしていきます。
インストール済みのものを購入した場合は、この項目からセットアップをしていきましょう!
一旦こちらのクイックスタートをごらんください!!
- 投稿日:2020-07-01T12:01:09+09:00
[Rails]画像の複数投稿を実装してみた
はじめに
某プログラミングスクールでフリマアプリを作成しました。画像の複数投稿・編集を担当し、その備忘録として記事を投稿します。今回は、特に苦労した画像の複数投稿について記述していきます。また、プログラミング初学者であるため、拙い箇所や間違ってる解釈があるかもしれません。ご了承の程、よろしくお願い致します。
また、下記の記事を参考にさせて戴きました。
https://qiita.com/gakinchoy7/items/ac1d8e64e33c3ddd377b
https://qiita.com/shinnosuke960801/items/66f2a511803d7dac53a3
https://qiita.com/mylevel/items/bae2204f8a40ff1d2d37仕様
1、10枚まで投稿ができるようにしました。
2、5枚画像を選択した後、2段目に移動するようにしました。
3、1枚ずつプレビューされるようにしました。
4、削除ボタンを押すと、プレビューが消えるようにしました。
(下記URLで挙動の確認ができます)
https://i.gyazo.com/baa9fdb1aaa4c2ffc4a1dc2225043486.mp4
https://i.gyazo.com/6a1b8af554c9c34792c8870cfd796580.mp4
https://i.gyazo.com/c73679d9c9bc3437b1abec53e63ce78c.mp4モデルのアソシエーション
user.rbhas_many :productsimage.rbbelongs_to :productproduct.rbbelongs_to :user has_many :images, dependent: :destroy画像付きで出品できるように
まず最初に、実装に必要なGemfileを編集します。
gemfilegem 'carrierwave' gem 'mini_magick' gem 'jquery-rails'インストールが終わったら、モデルに追記します。
image.rbmount_uploader :image, ImageUploaderproduct.rbaccepts_nested_attributes_for :images, allow_destroy: trueproductが保存される時に、imageが紐づいて保存されるようになります。
次に、コントローラを編集します。
products_controller.rbdef new @product = Product.new @product.images.new end def create @product = Product.create(product_params) if @product.save redirect_to root_path # image以外のデータも送信する想定で下記のように記述しています。 else unless @product.images.present? @product.images.new render 'new' else render 'new' end end end private def product_params params.require(:product).permit(images_attributes: [:image, :_destroy, :id]).merge(user_id: current_user.id) endform内で異なるモデルを編集する際に、fields_forというメソッドを使用します(今回だと、productを編集してimageを保存)。その際に、newアクションの
@product.images.new
という記述がないと、viewにフォームが表示されません。また、createアクションですが、画像が送信されたかそうでないかで条件分岐させています。単にrender 'new'
だと画像を送信せずにページに戻ってきた際にフォームが表示されません。また、画像を送信しているにも関わらず@product.images.new
の記述があると、フォームが2個表示されてしまいます。続いて、viewを作成していきます。
new.html.haml.content-bg-gray .shadowed-rounded-rectangle = form_with(model: @product, local: true, class: "product-new-form") do |f| %section.mainbox-product-header %h2.mainbox-header__text 商品の情報を入力 .product__block__form %span.label_title.profile-form__label 出品画像 %span.require 必須 %p.upload_limit 最大10枚までアップロードできます .product-new__field__uploader = f.fields_for :images do |image| %ul#previews %li.input %label.upload-label .upload-label__text %p クリックしてファイルをアップロード .input-area = image.file_field :image, class: "hidden image_upload"_new.scss.content-bg-gray{ background-color: #EFEFEF; padding-top: 80px; padding-bottom: 100px; .shadowed-rounded-rectangle{ width: 700px; box-shadow: 1px 1px 10px 1px rgba(0,0,0,0.1); margin: 100px auto 0; background-color: #ffffff; border-radius: 60px; padding: 30px; .product-new-form{ width: 434px; margin: auto; .product__block__form{ .product-new__field__uploader{ margin-bottom: 50px; width: 100%; ul{ .input{ display: flex; flex-wrap: wrap; width: 100%; .upload-label{ width: 100%; height: 121px; background-color: rgb(245, 245, 245); .upload-label__text{ text-align: center; position: absolute; top: 50%; z-index: 0; width: 100%; transform: translate(0, -50%); } .input-area{ display: none; } } } } } } } } } .mainbox-product-header{ margin: 30px 0 8px; width: 434px; &__text{ margin-bottom: 20px; } } .require{ display: inline; width: 32px; height: 15px; padding: 2.5px 5.5px; background-color: #68C7CC; color: #ffffff; border-radius: 7px; }BEM規則に基づいて命名できていないので、分かりづらいかもしれませんがviewの完成です。
次に、画像を載せるごとに入力欄を変化させるようにします。
ます、jQueryを使えるよに下記ファイルを編集します。application.js//= require turbolinks //= require jquery //= require jquery_ujs //= require_tree .
new_image.js
ファイルを作成し、編集していきます。new_image.js$(function () { # プレビュー機能 # 'change'イベントでは$(this)で要素が取得できないため、 'click'イベントを入れました。 # これにより$(this)で'input'を取得することができ、inputの親要素である'li'まで辿れます。 $(document).on('click', '.image_upload', function () { # inputの要素はクリックされておらず、inputの親要素であるdivが押されています。 # だからdivのクラス名をclickした時にイベントが作動します。 # div(this)から要素を辿ればinputを指定することが可能です。 # $liに追加するためのプレビュー画面のHTMLです。 var preview = $('<div class="image-preview__wapper"><img class="preview"></div><div class="image-preview_btn"><div class="image-preview_btn_delete">削除</div></div>'); # 次の画像を読み込むためのinputです。 var append_input = $(`<li class="input"><label class="upload-label"><div class="upload-label__text"><i class="fa fa-camera fa-4x"></i><div class="input-area display-none"><input class="hidden image_upload" type="file"></div></div></label></li>`) $ul = $('#previews') $li = $(this).parents('li'); $label = $(this).parents('.upload-label'); $inputs = $ul.find('.image_upload'); # inputに画像を読み込んだら、"プレビューの追加"と"新しいli追加"処理が動きます。 $('.image_upload').on('change', function (e) { # inputで選択した画像を読み込みます。 var reader = new FileReader(); # プレビューに追加させるために、inputから画像ファイルを読み込みます。 reader.readAsDataURL(e.target.files[0]); # 画像ファイルが読み込んだら、処理が実行されます。 reader.onload = function (e) { # previewをappendで追加する前に、プレビューできるようにinputで選択した画像を<img>に'src'で付与させます。 # つまり、<img>タグに画像を追加させます。 $(preview).find('.preview').attr('src', e.target.result); } # inputの画像を付与した,previewを$liに追加します。 $li.append(preview); # 生成したliの横幅を決めます。 $('#previews li').css({ 'width': `80px` }) # プレビュー完了後は、inputを非表示にさせます。これによりプレビューだけが残ります。 $label.css('display', 'none'); # inputを非表示にします。 $li.removeClass('input'); # inputのクラスはjQueryで数を数える時に邪魔なので除去します。 $li.addClass('image-preview'); # inputのクラスからプレビュー用のクラスに変更しました、 $lis = $ul.find('.image-preview'); # クラス変更が完了したところで、プレビューの数を数えます。 # 画像が9枚以内なら文字とインプットを追加させます。 if ($lis.length < 10) { $ul.append(append_input) $('#previews li:last-child').css({ 'width': `80px` }) } # inputの最後の"data-image"を取得して、input nameの番号を更新させています。 # これをしないと、それぞれのinputの区別ができず、最後の1枚しかDBに保存されません。 # 全部のプレビューの番号を更新することで、プレビューを削除して、新しく追加しても番号が1,2,3,4,5,6と綺麗に揃います。そのため、全部の番号を更新させます。 $inputs.each(function (num, input) { # nameの番号を更新するために、現在の番号を除去します。 $(input).removeAttr('name'); $(input).attr({ name: "product[images_attributes][" + num + "][image]", id: "images_attributes_" + num + "_image" }); }); }); }); # 削除ボタンをクリックしたとき、処理が動きます。 $(document).on('click', '.image-preview_btn_delete', function () { var append_input = $(`<li class="input"><label class="upload-label"><div class="upload-label__text"><i class="fa fa-camera fa-4x"></i><div class="input-area display-none"><input class="hidden image_upload" type="file"></div></div></label></li>`) $ul = $('#previews') $lis = $ul.find('.image-preview'); $li = $(this).parents('.image-preview'); # "li"ごと削除して、previewとinputを削除させます。 $li.remove(); $lis = $ul.find('.image-preview'); # クラス変更が完了したところで、プレビューの数を数えます。 # 画像が10枚以内なら文字とインプットを追加させます if ($lis.length == 9) { $ul.append(append_input) } $('#previews li:last-child').css({ 'width': `80px` }) }); });jsファイルで追加されたセレクタをcssで編集させていきます。
_new.scss.content-bg-gray{ background-color: #EFEFEF; padding-top: 80px; padding-bottom: 100px; .shadowed-rounded-rectangle{ width: 700px; box-shadow: 1px 1px 10px 1px rgba(0,0,0,0.1); margin: 100px auto 0; background-color: #ffffff; border-radius: 60px; padding: 30px; .product-new-form{ width: 434px; margin: auto; .product__block__form{ .product-new__field__uploader{ margin-bottom: 50px; width: 100%; #previews{ list-style: none; display: flex; flex-wrap: wrap; .image-preview__wapper{ width: 80px; height: 80px; .preview{ width: 80px; height: 80px; } } .image-preview_btn{ text-align: center; padding: 8px; border-top: 1px solid #cccccc; cursor: pointer; &:hover{ transition: 0.5s; background-color: #cccccc; } } .input{ display: flex; flex-wrap: wrap; width: 100%; .upload-label{ width: 100%; height: 121px; background-color: rgb(245, 245, 245); .upload-label__text{ text-align: center; position: absolute; top: 50%; z-index: 0; width: 100%; transform: translate(0, -50%); } .input-area{ display: none; } } } } } } } } } .mainbox-product-header{ margin: 30px 0 8px; width: 434px; &__text{ margin-bottom: 20px; } } .require{ display: inline; width: 32px; height: 15px; padding: 2.5px 5.5px; background-color: #68C7CC; color: #ffffff; border-radius: 7px; }
#previews
の中に.preview
と.input
を横並べにし、flex-wrap: wrap;
をかけます。5枚×2段になるようにサイズを調整しています。以上で画像の複数投稿の実装は終わりです。
おわりに
リファクタリングが出来ていなかったり、BEMに従って命名出来ていなかったり、他にも課題はいくつかあると思います。機会があれば、画像の枚数に応じてフォームの大きさを小さくしていくするような実装をしてみたいと思います。質問や間違っている点がございましたら、コメントで指摘してくださると幸いです。
- 投稿日:2020-07-01T11:46:49+09:00
Node.jsを使用してWebアプリケーションからAlibaba Cloud Object Storage Serviceにコンテンツをアップロード
このチュートリアルでは、Node.jsを使ってWebアプリケーションからAlibaba Cloud Object Storage Serviceにコンテンツをアップロードする方法を検討します。
本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。
必要条件
このチュートリアルを実行するには、以下のものが必要です。
1、Alibaba Cloud Object Storage Serviceに登録し、秘密のアクセスキーを取得します。
2、マシン上でNode.jsとnpmを実行します。Node.js Downloadsには、Nodeのインストール方法についての詳細な情報が記載されています。
まとめると、Alibaba Cloudのアカウント、Object Storage Service、Node.js、npm、OSSサービスのアクセスキーが必要になります。アクセスキーをクレデンシャルファイルに追加する方法
そもそも、Alibaba Cloud OSSはRESTfulなAPI操作をサポートしており、ほとんどの言語に対応したSDKを提供しています。このチュートリアルでは、オープンソースのOSS JavaScript SDK for node.jsを使用します。まずはこのように ali-oss をインストールする必要があります。
npm install ali-oss注目すべきは、同期モードと非同期モードの2つのモードから選択できることです。
同期モードで使用するには、さらにcoと組み合わせて使用することができます。 coをインストールするようにしてください。
npm install co非同期モードはコールバックをサポートしています。
クライアントの初期化
このメソッドは同期モードで適用されます。このメソッドでは、以下のような app.js オブジェクトを作成します。
var co = require('co'); var OSS = require('ali-oss'); var client = new OSS({ region: '<Your region>', accessKeyId: '<Your AccessKeyId>', accessKeySecret: '<Your AccessKeySecret>' });Alibaba Cloudでは、OSSサービスに加入している場合、地域を指定しなければならなかったので、リージョンフィールドは非常に重要です。OSSノードの完全なリストは以下の通りです。
さらに、OSS Nodesのリストにないエンドポイントもありますが、その場合は以下のパラメータを設定する必要があります。
1、secure: リージョンと組み合わせて使用します。secureをtrueに指定した場合はHTTPを使ってアクセスします。
2、endpoint: エンドポイントを指定した場合、リージョンを無視しても大丈夫です。endpointにHTTPSを指定してもOKです。
3、bucket:バケットが指定されていない場合は、まず useBucket インターフェースを呼び出す必要があります。
4、timeout: デフォルトでは60秒です。OSS APIのタイムアウトを指定するためのパラメータです。
見た目は問題なさそうですが、次のステップに進みます。
依存関係のインストール
このステップではnode.jsの依存関係をインストールして、ノードアプリケーションが置かれるディレクトリを作成します。このチュートリアルでは、OSS-node-appで以下のようにアプリを作成します。
mkdir sites/OSS-node-app && cd sites/OSS-node-app次に、package.jsonファイルを作成し、そこに以下のコードを貼り付けます。
{ "name": "OSS-node-app", "version": "1.0.0", "main": "server.js", "scripts": { "start": "node server.js" }, "license": "MIT" }上記のサンプルでは、アプリの名前、バージョン、ライセンスを記述した package.json ファイルを作成しています。scriptsパラメータは、node server.jsではなく、npm startを使用してnode serverを実行できるようにします。
インストールする必要のあるすべての依存関係は、npm installコマンドを使用してインストールされます。次に4つの依存関係が続き、このプロジェクトでは以下のようにインストールされます。
npm install ali-oss express私たちの依存関係は、OSSのAPIを使って作業したり、Webサーバーを立ち上げたり、ファイルのアップロードを処理したりすることを可能にしています。上記のコマンドはまた、package.jsonを更新します。
1、ali-OSS - 私たちがJavaScript APIにアクセスできるようにするためのJavaScript用のAlibaba SDKです。
2、express - Expressは、サーバーの迅速かつ効率的なセットアップを可能にします。
プロジェクトの場所と依存関係がすべてセットアップされたので、サーバーを起動してフロントエンドのビューをセットアップすることができます。すべての依存関係は、それ以降のすべてのNode.jsバージョンではデフォルトでpackage.jsonファイルに保存されることに注意してください。フロントエンドアプリの作成
アプリケーションのパブリックビュー用のファイルを作成することから始めましょう。index.html、succeed.html、error.htmlを使ってパブリックフォルダを作成します。これら3つのファイルは、以下に示すようなHTMLの骨格を持っていますが、内容が異なるだけです。全てのファイルに以下のコードを貼り付けます。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>OSS Node Tutorial</title> <link rel="stylesheet" href="./style.css"> </head> <body> <!—your content here --> </body> </html>このように、それぞれのファイルにエラーメッセージを書き込んでください。
<h1>Something went wrong!</h1> <p>Uploading the file was unsuccessful.</p>このように、それぞれのファイルに成功メッセージを書き込んでください。
<h1>Success!</h1> <p>File uploaded successfully.</p>ここでは、index.htmlにmultipart/form-dataを含むHTMLフォームを作成します。フォームには、送信ボタンも用意しなければなりません。
<h1>OSS Tutorial</h1> <p>Please select a file and submit form to upload.</p> <form method="post" enctype="multipart/form-data" action="/upload"> <label for="file">Upload a file</label> <input type="file" name="upload"> <input type="submit" class="button"> </form>では、インターフェイスを見やすくするためにstyle.cssを作成します。
html { font-family: sans-serif; line-height: 1.5; color: #333; } body { margin: 0 auto; max-width: 500px; } label, input { display: block; margin: 5px 0; }無事にアップロード用のファイル、成功ページとエラーページの作成が完了しましたので、弊社のNode.jsアプリをご紹介します。
Expressを使ったサーバーの設定
今回はExpressフレームワークを使ってnode.jsサーバーを濡らしていきます。まず、プロジェクトのルートディレクトリにserver.jsファイルを作成します。次に、require()を使用して4つの依存関係をロードし、以下のようにExpressのアプリインスタンスを経由してアプリをルーティングする必要があります。
server.js // Load dependencies const ali = require(ali-oss'); const express = require('express'); const app = express();アプリケーションのフロントエンドは公開リポジトリにあるので、依存関係の設定は以下のように設定しなければなりません。
// Views in public directory app.use(express.static('public'));そして、3つのファイルをサーバのルートに相対的にルーティングするように進めます。
// Main, error and success views app.get('/', function (request, response) { response.sendFile(__dirname + '/public/index.html'); }); app.get("/success", function (request, response) { response.sendFile(__dirname + '/public/success.html'); }); app.get("/error", function (request, response) { response.sendFile(__dirname + '/public/error.html'); });ここでは、リスニングに使用するポートを設定する必要があります。
server.js app.listen(5000, function () { console.log('Server listening on port 5000.'); });これを保存して、npm startを使ってサーバーを起動します。
npm start Output > node server.js Server listening on port 5000.ブラウザのアドレスバーにhttp://localhost:5000と入力すると、アップロードフォームにアクセスできるはずです。また、http://localhost:5000/success と http://localhost:5000/error にアクセスして反応を試してみてください。
ファイルのアップロード
サーバーはすべてセットアップされているので、最初のファイルをAlibaba OSSにアップロードできるようにするために、フォームをali-ossに統合しなければなりません。
server.js const app = express(); // Set endpoint to Alibaba OSS const spacesEndpoint = new OSS.Endpoint(' http://oss-cn-hangzhou.aliyuncs.com.'); const s3 = new OSS({ endpoint: spacesEndpoint });新しい OSS() を使用して OSS クライアントに接続する必要があります。
この最初のインスタンスでは、単純なローカルファイルのアップロードを実装しています。
var co = require('co'); var OSS = require('ali-oss') var client = new OSS({ region: '<Your region>', accessKeyId: '<Your AccessKeyId>', accessKeySecret: '<Your AccessKeySecret>', bucket: 'Your bucket name' }); co(function* () { var result = yield client.put('object-key', 'local-file'); console.log(result); }).catch(function (err) { console.log(err); });2 番目のインスタンスでは、putStream インターフェイスを使用してアクセスされたストリームのアップロードを使用します。固有の読み取り可能なストリームを持つ任意のオブジェクトは、このメソッドで有効になります(オブジェクトとネットワーク ストリームの両方)。putStream インターフェースが使用されると、SDK は自動的にチャンク化されたエンコーディングの HTTP PUT を開始します。
var co = require('co'); var OSS = require('ali-oss'); var fs = require('fs'); var client = new OSS({ region: '<Your region>', accessKeyId: '<Your AccessKeyId>', accessKeySecret: '<Your AccessKeySecret>', bucket: 'Your bucket name' }); co(function* () { // use 'chunked encoding' var stream = fs.createReadStream('local-file'); var result = yield client.putStream('object-key', stream); console.log(result); // do not use 'chunked encoding' var stream = fs.createReadStream('local-file'); var size = fs.statSync('local-file').size; var result = yield client.putStream( 'object-key', stream, {contentLength: size}); console.log(result); }).catch(function (err) { console.log(err); });バッファ内のオブジェクトの内容をアップロードするには、put インターフェースを介してアクセスします。
var co = require('co'); var OSS = require('ali-oss'); var client = new OSS({ region: '<Your region>', accessKeyId: '<Your AccessKeyId>', accessKeySecret: '<Your AccessKeySecret>', bucket: 'Your bucket name' }); co(function* () { var result = yield client.put('object-key', new Buffer('hello world')); console.log(result); }).catch(function (err) { console.log(err); });非常に大きなファイルをアップロードする場合、multipartUploadインターフェースはリクエストをより小さな実行可能なリクエストに分割することができます。この方法の利点は、ファイル全体を修正するのとは対照的に、失敗した部分だけをアップロードする必要があることです。以下のパラメータを使用します。
1、name {String}:オブジェクト名
2、file {String|File}:ファイルパスまたは HTML5 Web ファイル
3、[options] {Object}:オプションのパラメータ1、[checkpoint] {Object}: 再開可能なアップロードで使用するエンドポイントのチェックポイントです。このパラメータを設定すると、エンドポイントからアップロードが開始されます。設定されていない場合は、アップロードを再開します。
2、[partSize] {Number}: パーツサイズ
3、[progress] {Funtion}: ジェネレーター機能。3つのパラメータを含む。
-----1.(percentage {Number}: アップロードの進捗状況 (0から1までの10進数)
-----2.(checkpoint {Object}.:エンドポイントチェックポイント
-----3.(res {Object}):単一のパーツが正常にアップロードされた後に返されるレスポンス
4、[meta] {Object}: x-oss-meta-という接頭辞を持つユーザによって定義されたヘッダメタ情報。
5、[headers] {Object}: 余分なヘッダ。詳細は RFC 2616 を参照してください。
-----1.'Cache-Control':HTTP リクエストとレスポンスでコマンドを指定してキャッシュメカニズムを実装するために使用される一般的なヘッダ。例えば、以下のようになります。Cache-Control: public, no-cache
-----2.'Content-Disposition':これは、内部参照(ウェブページやページの一部)や、ローカルにダウンロードして保存された添付ファイルである可能性があります。例えば、以下のようになります。Content-Disposition: somename
-----3.'Content-Encoding':特定のメディアタイプのデータを圧縮するために使用されます。例えば Content-Encoding: gzip
-----4.'Expires':有効期限。例えば Expires. 3600000var co = require('co'); var OSS = require('ali-oss') var client = new OSS({ region: '<Your region>', accessKeyId: '<Your AccessKeyId>', accessKeySecret: '<Your AccessKeySecret>', bucket: 'Your bucket name' }); co(function* () { var result = yield client.multipartUpload('object-key', 'local-file', { progress: function* (p) { console.log('Progress: ' + p); } meta: { year: 2017, people: 'test' } }); console.log(result); var head = yield client.head('object-key'); console.log(head); }).catch(function (err) { console.log(err); });上記のprogressパラメータは、アップロードの進捗状況を取得するためのcalback関数です。
var progress = function (p) { return function (done) { console.log(p); done(); }; };結論
おめでとうございます。Alibaba Cloud Object Storage Service (OSS) スペースにオブジェクトをアップロードするためのExpressアプリケーションの設定に成功しました! Alibaba Cloudのアカウントをお持ちでない方は、アカウントを登録してください。アカウントにサインアップして、最大1200ドル相当の40以上の製品を無料でお試しください。Alibaba Cloudの詳細については、「Get Started with Alibaba Cloud」を参照してください。
アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ
- 投稿日:2020-07-01T11:37:48+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-01T10:40:50+09:00
CentOS 7にOpenProjectをインストールする
今回は、Alibaba Cloud ECS CentOS 7サーバーにOpenProjectをインストールします。
本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです 。
前提条件
1、Alibaba Cloud Elastic Compute Service (ECS)インスタンスが有効化されており、有効な支払い方法を確認している必要があります。新規ユーザーの場合は、Alibaba Cloudアカウントで無料アカウントを取得することができます。ECSインスタンスのセットアップ方法がわからない場合は、このチュートリアルまたはクイックスタートガイドを参照してください。ECSインスタンスは、少なくとも1GBのRAMと1つのCoreプロセッサを搭載している必要があります。
2、Alibaba Cloudから登録されたドメイン名。すでにAlibaba Cloudまたは他のホストからドメインを登録している場合は、そのドメインネームサーバーレコードを更新することができます。システムのアップグレード
このチュートリアルでは、インストールプロセス全体にrootユーザ権限を使用しています。以下のコマンドを使用して、非 root ユーザから root ユーザに切り替えることができます。
sudo -iOpenProjectをインストールする前に、利用可能なリポジトリやパッケージをアップグレードすることをお勧めします。アップグレードは以下のコマンドで行うことができます。
yum -y updateOpenProjectのインストール
まずはCentOS 7用のOpenProjectリポジトリを追加します。以下のコマンドを実行してください。
sudo wget -O /etc/yum.repos.d/openproject-ce.repo https://dl.packager.io/srv/opf/openproject-ce/stable/7/installer/el/7.repoリポジトリが追加されたら、OpenProjectをインストールします。以下のコマンドを実行してインストールします。
yum -y install openprojectインストールが完了すると、ターミナルにInstalled as resultが表示されるはずです。
OpenProjectの設定
OpenProjectのインストールが完了したら、設定をしなければなりません。MySQLを使ったデータベースの設定、Apacheを使ったウェブサーバの設定、ドメイン名の設定、GitとSVNのサポート追加、メール通知の設定、OpenProjectのパフォーマンスを向上させるためのMemcachedの有効化を行います。以下のコマンドを実行してOpenProjectを設定します。
openproject configureコマンドを実行すると、以下のようなインターフェースが表示されます。
セットアップウィザードで必要なMySQLデータベースを自動的に作成するかどうか聞かれるので、インストールオプションを選択してMySQLサーバをローカルにインストールして設定します。OpenProjectで使用するPostgreSQLデータベースを使用したい場合は、スキップを選択します。既存のデータベースを使用したい場合は、再利用オプションを選択します。手動でデータベースを設定する手間を省くために、インストールオプションを選択してください。
次のインターフェースでは、Webサーバの設定を求められます。単に'Install apache2 server'を選択して'OK'をクリックしてください。これで自動的にapache2ウェブサーバーがインストールされ、OpenProjectアプリケーションのための仮想ホストが設定されます。
次の画面では、Openprojectアプリケーションのドメイン名の入力を求められますので、ドメイン名を入力してOKボタンをクリックしてください。
次のステップでは、サーバーパスのプレフィックスを聞かれますが、空白のままでも構いません。アプリケーションへのパスを提供したい場合は、ここに入力してください。それ以外の場合は、インストールを進めてください。
次に、SSLの設定を求められます。SSL証明書を持っている場合は「Yes」を選択し、そうでない場合は「No」を選択して先に進みます。
次に、SubversionとGitサポートの設定を求められます。Yesを選択してこれらの機能をインストールすると、Apacheを使ってアプリケーションにSubversionとGitのリポジトリを作成してホストできるようになります。
次に、アプリケーションのメール設定を尋ねられます。アプリケーションにメールを送信させたくない場合は、「Skip」を選択し、必要に応じて選択して「OK」ボタンをクリックして先に進みます。
最後にmemcached serverのインストールを求められます。memcached serverは強力で高性能な分散メモリオブジェクトキャッシングシステムです。OpenProjectのパフォーマンスを向上させるためにインストールしておきましょう。
openpojectインストールのためのすべてのパッケージを自動的にインストールして設定します。
ウェブインタフェース
お気に入りのWebブラウザを開き、OpenProjectアプリケーションへのパスを入力するか、Fully qualified domain nameにアクセスしてください。以下のようなOpenProjectのデフォルトのWebページが表示されます。
ログインボタンをクリックして管理画面にアクセスし、ユーザー名とパスワードに「admin」を使用します。
次に、現在のログインパスワードの変更を求められますので、お好きなように変更して、保存ボタンをクリックして先に進みます。
最後に、以下のようなOpenProjectの管理画面が表示されます。
おめでとうございます。これで、Alibaba Cloud Elastic Compute Service (ECS) CentOS 7サーバーにOpenProjectをインストールし、設定することができました。これで、VPSまたはクラウドサーバー上でOpenProjectを簡単に設定・設定できるようになりました。
アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ
- 投稿日:2020-07-01T10:37:43+09:00
JavaScript FizzBuzz問題の作り方
目次
- はじめに
- FizzBuzz問題の作り方
- HTML
- JavaScript
- 学んだこと
- おわりに
はじめに
今回はJavascriptの基本的な概念
DOM
を操作して、FizzBuzz問題を作っていきます。
イチから全部作り方を書くというよりは、作っていく流れを書き、
実際に作る中で僕が躓いたり、ここは大事だなと感じた箇所などを説明していきます。
同じようなことで行き詰まっている初心者の方のお力になれれば幸いです。FizzBuzz問題の作り方
基本的にHTMLとJavaScriptを使って作成します。
CSSは特に使いません。
HTMLの要素も至ってシンプルです。
なぜなら、今回の実装ではJavaScriptを使ってDOMを操作することが目的だからです。
HTMLで作った画面を作り、それに対してJavaScriptで色々操作していく、という流れになります。HTML
まずはHTMLから作っていきます。
HTMLの作りはとてもシンプルで簡単なものです。見出し
h1タグ
2つの数値入力ボックスpタグ
実行ボタンbuttonタグ
「出力」pタグ
それだけです。
見た目としては以上になります。そして、大事な
scriptタグ
このタグがあることで簡単にHTMLとJavaScriptを繋げることが出来ます。
このタグがないとJavaScriptを発動出来ません。
これだけは忘れずに書いてください。色々決まりはありますが、
基本的にscriptタグ
はbodyの末尾辺りに置く慣習になっています。JavaScriptは違うページに専用ファイルとして書いていきます。
(文が長くなって読みにくくなるのを防ぐためであったり、それもまた慣習となっています。)
そのためsrc属性を使って、リンク先だけを書いておきます。<body> <h1>FizzBuzz問題</h1> <p>FizzNum: <input type="text" name="fizzes" id="fizz" placeholder="整数値を入力してください"></p> <p>BuzzNum:<input type="text" name="buzzes" id="buzz" placeholder="整数値を入力してください"></p> <button value="実行" id="btn">実行</button> <div id="output"> <p>【出力】</p> </div> <script src="main.js"></script> </body>またJavaScriptを使ってDOMを操作するのに必要になるのが
id
です。
何箇所にidがセットされているのがわかるかと思います。
このidが後々非常に役に立ちます。HTMLは以上で完了です。
ブラウザに映る元々の表示の役割を担ってくれています。
出来ることはここまでです!
これから先の実装はJavaScriptに任せることとします。JavaScript
//FizzNum, BuzzNumに数値を入力し、実行を押したときの挙動// //HTMLの中からIdがbtnの要素を取得 const btn = document.getElementById('btn'); //ボタン要素のクリックイベントをトリガーにコールバック処理を作成 btn.addEventListener('click', () => {次にJavaScriptでの処理です。
別ファイルに作ったmain.jsに書き込んでいきます。基本的にコードは始まりはこれを書かなければいけないという厳密な決まりはないので、なにから書いてもいいはずです。
ただ、通常コードは上から下に読み込まれていくので、その点は考える必要があります。まず、要素を
id
から取得します。そのためにhtmlで要素にid
を付与する訳です。
その要素に対してイベント(何かしらの処理を行う)を発動する行為が、EventListener
です。
この流れはJavaScriptでは鉄板です。何回も使います。
指が覚えるくらい繰り返してもいいと思います。//入力値の取得// //HTMLの中からIdがfizzの要素を取得 const elemFizzNumber = document.getElementById('fizz'); //fizzNumのinputに入力された数字から値を取得 const fizzNumber = elemFizzNumber.value; //HTMLの中からIdがbuzzの要素を取得 const elemBuzzNumber = document.getElementById('buzz'); //BuzzNunのinputに入力された数字から値を取得 const buzzNumber = elemBuzzNumber.value;この次も要素の取得の流れは続きます。
要素を取得し、更に要素に入力された値を取得しています。
value
が登場してきます。
取得した要素を後でたくさん使うので、先に色々と取得しておく必要があります。
この辺りの考え方もプログラミングならでは、ですね。//結果情報のベース作成// //HTMLの中からIdがoutputの要素を取得 const outputArea = document.getElementById('output'); //結果要素の子要素を取得 outputArea.innerHTML = ''; //pタグの要素を作成 const ptag = document.createElement('p'); //pタグの要素に結果情報ヘッダーの固定値をセット ptag.textContent = '【出力】'; //結果要素の子要素としてpタグの要素を追加 outputArea.appendChild(ptag);必要な要素が取得できたら、次はその要素をブラウザに表示するための処理が必要です。
次にcreateElement
が出てきます。
要素を取得したり、要素を作り出したり、なんせ要素は大事です。
この辺りも一連の流れになっているので、繰り返せば頭に自然に入ってくるはずです。一文一文に意味があり、
やること(必要な処理)を流れで考えている、のがわかるかと思います。//結果情報の中身を作成// //fizzbuzz問題のループ文 // 変数iを定義し、iが100より小さい場合、iに1ずつ足していく処理を実行 for (let i = 1; i < 100; i++) { //バリューを取得するための変数を定義 let value = ''; //iがfizzes、buzzesの両方の数値の倍数である場合の処理 if (i % fizzNumber === 0 && i % buzzNumber === 0) { //ブラウザに表示するための文字列と取得した値をバリューとして用意 value = "FizzBuzz" + " " + i; //がiがfizzesの倍数である場合の処理 } else if (i % fizzNumber === 0) { //ブラウザに表示するための文字列と取得した値をバリューとして用意 value = "Fizz" + " " + i; //がiがbuzzesの倍数である場合の処理 } else if (i % buzzNumber === 0) { //ブラウザに表示するための文字列と取得した値をバリューとして用意 value = "Buzz" + " " + i; //iがどちらの倍数でもない場合の処理 } else { //何も表示させないものとして用意 value = ''; }次は肝心のFizzBuzz文です。
for文
の中でif文
を用います。
最初に取得した要素もここで使用する訳です。
必要なものを先に準備して、後で使う。それがイメージできれば、構文も考えやすくなります。言葉にはできてもコードで書くのは難しいです。
for文
にしろ、if文
にしろ、プログラミングには書き方(法則)があるので、それを自分の脳内に落とし込む作業が必要になります。
頭に入ってしまえば、後は必要なときに取り出すだけです。これだけあれば、FizzBuzz文をブラウザに表示できます。
後は、エラーが出たときの処理を付け足したりして、より強固な機能を付け加えるといいかと思います。以上が、FizzBuzz問題の作り方です。
以下、全体のコード
`use strict` //FizzNum, BuzzNumに数値を入力し、実行を押したときの挙動// //HTMLの中からIdがbtnの要素を取得 const btn = document.getElementById('btn'); //ボタン要素のクリックイベントをトリガーにコールバック処理を作成 btn.addEventListener('click', () => { //入力値の取得// //HTMLの中からIdがfizzの要素を取得 const elemFizzNumber = document.getElementById('fizz'); //fizzNumのinputに入力された数字から値を取得 const fizzNumber = elemFizzNumber.value; //HTMLの中からIdがbuzzの要素を取得 const elemBuzzNumber = document.getElementById('buzz'); //BuzzNunのinputに入力された数字から値を取得 const buzzNumber = elemBuzzNumber.value; //結果情報のベース作成// //HTMLの中からIdがoutputの要素を取得 const outputArea = document.getElementById('output'); //結果要素の子要素を取得 outputArea.innerHTML = ''; //pタグの要素を作成 const ptag = document.createElement('p'); //pタグの要素に結果情報ヘッダーの固定値をセット ptag.textContent = '【出力】'; //結果要素の子要素としてpタグの要素を追加 outputArea.appendChild(ptag); //結果情報の中身を作成// //fizzbuzz問題のループ文 // 変数iを定義し、iが100より小さい場合、iに1ずつ足していく処理を実行 for (let i = 1; i < 100; i++) { //バリューを取得するための変数を定義 let value = ''; //iがfizzes、buzzesの両方の数値の倍数である場合の処理 if (i % fizzNumber === 0 && i % buzzNumber === 0) { //ブラウザに表示するための文字列と取得した値をバリューとして用意 value = "FizzBuzz" + " " + i; //がiがfizzesの倍数である場合の処理 } else if (i % fizzNumber === 0) { //ブラウザに表示するための文字列と取得した値をバリューとして用意 value = "Fizz" + " " + i; //がiがbuzzesの倍数である場合の処理 } else if (i % buzzNumber === 0) { //ブラウザに表示するための文字列と取得した値をバリューとして用意 value = "Buzz" + " " + i; //iがどちらの倍数でもない場合の処理 } else { //何も表示させないものとして用意 value = ''; } //要素を追加するためのpタグ要素を作成 const fizzbuzz = document.createElement('p'); //テキストの内容に、ループ文で取得した値を入力する fizzbuzz.textContent = value; //親要素であるdivタグの要素を取得 const div = document.querySelector('div') //div要素の子要素として追加 outputArea.appendChild(fizzbuzz); } });学んだこと
一から何かを作るのは大変です。
私は一ヶ月ほどかかって今回のFizzBuzz問題の作り方を理解しました。
時間はかかりましたが、考えを頭に落とし込むにはそのくらいの時間をかけてもいいと思います。例え簡単なFizzBuzz文であっても、本当にわからないことだらけでした。
わからないながらでも作っていき、その中で学んでいくことが大半です。コード自体は学習したことをそのまま応用しているだけなので、難しいことは書いていません。
しかし、学んだことを応用する作業が非常に難しいです。何を書けばいいかわからない(頭に浮かんでこないから)という問題は、私も例外なく陥りました。
そのくらい、プログラミングは生活から切り離されているもの(馴染みのないもの)なんだなと感じました。
こればっかりは繰り返して学んでいくしかないので、
プログラミングという概念を頭に埋め込んでいく時間が必要です。また、DOM操作も初心者が躓く概念だと思います。
普段の生活でDOMのような考え方はありませんから、最初はよくわからないのも当然です。
しかし、JavaScriptを学ぶ上でDOMの概念は切っても切れないので、しっかり脳内に刻み込んでおく必要があります。ここは踏ん張りどころです。一つ一つ乗り越えていくしかありません。
その過程で出来ないことが出来るようになっていくのはとても嬉しいものです。教材で学習するのは最低限必要ですが、その先は何かを作れ、という先人達のアドバイスは本当にその通りだと思いました。
おわりに
今回は私が初めて行った課題について説明しました。
今振り返ると簡単なことでも、振り返ると当初は全然わからず、頭を抱えていました。
初歩的なことでも初心者はわからないものです。振り返ると、自分も少しは理解できていることに気づきます。
きっとプログラミング学習は、そんな小さな喜びの連続なんだと思っています。毎日勉強して、新しいことを吸収していきたいものです。
人に説明するのも難しいものです。
と同時に、自身の学びにも繋がるので、随時アウトプットしていきます。ありがとうございました。
- 投稿日:2020-07-01T10:27:57+09:00
varとletとconstの違いメモ
自分的なメモです。
varとletとconstの違いを説明してみろと言われた時のための
- varはES6より前で使われていた宣言方法。
- letとconstは、ES6から採用された、新しい宣言方法。
再宣言、再代入に関する違い
var
再宣言、再代入が可能。
//再代入できる var hoge = 'りんご'; hoge = 'みかん'; console.log(hoge); // 結果:みかん // 同じ変数名を使って別のデータを再宣言できる var hoge = 'すいか'; console.log(hoge); // 結果:すいかlet
letは代入し直すことはできるけど、同じ変数名を使って別のデータを再宣言するとエラーが出る。
// 再代入できる let hoge = 'りんご'; hoge = 'みかん'; console.log(hoge); // 結果:みかん let hoge = 'すいか'; console.log(hoge); // 結果:シンタックスエラー再宣言はできないので
Uncaught SyntaxError: Identifier 'hoge' has already been declared
っていうシンタックスエラーが出ますよconst
constは変数の中でも一番厳格で、再代入も再宣言もできない。constのことを変数ではなく「定数」と定義する場合もある
一度しか代入できないので、長いプログラムを書いた時に変数名が被らない。// 再代入できる const hoge = 'りんご'; hoge = 'みかん'; console.log(hoge); // 結果:シンタックスエラー const hoge = 'すいか'; console.log(hoge); // 結果:シンタックスエラーこちらもシンタックスエラー
Uncaught SyntaxError: Identifier 'hoge' has already been declared
が出ますスコープの違い
var,let,contはそれぞれ影響範囲(スコープ)が異なります。
varの場合
関数スコープなので、関数の外にある変数に格納されているデータを関数内で出力することができません。
var hoge = 'りんご'; console.log(hoge); // 結果:りんご function fugafuga(){ console.log(hoge); // 結果:エラー var hoge = 'みかん'; console.log(hoge); // 結果:みかん } fugafuga();letの場合
- 関数の外から呼び出すことは可能。外から呼び出した値を上書きすることもできる。
- だけど再宣言は禁止されているのでエラーがでる
- if{}やfor{}内で宣言したらその中でしか使用できなくなるブロックスコープなので、影響範囲が少なくなる
let hoge = 'りんご'; console.log(hoge); // 結果:りんご function fugafuga(){ console.log(hoge); // 結果:りんご hoge = 'みかん'; console.log(hoge); // 結果:みかん let hoge = 'みかん'; console.log(hoge); // 結果:Uncaught ReferenceError: Cannot access 'hoge' before initialization } fugafuga();constの場合
- 関数の外から呼び出すことは可能。
- 再代入もできず再宣言もできないのでエラーがでる。より厳格に定数内に収められた値を扱うことができる。
- if{}やfor{}内で宣言したらその中でしか使用できなくなるブロックスコープなので、影響範囲が少なくなる
const hoge = 'りんご'; console.log(hoge); // 結果:りんご function fugafuga(){ console.log(hoge); // 結果:りんご hoge = 'みかん'; console.log(hoge); // 結果:Uncaught TypeError: Assignment to constant variable. const hoge = 'みかん'; console.log(hoge); // 結果:Uncaught ReferenceError: Cannot access 'hoge' before initialization } fugafuga();とりとめのない感じになったけど、頭の中では整理できたので良しとする。
結論としては、今後はvarできるだけ使わないほうが良いよということでしょうか。以上です。
- 投稿日:2020-07-01T10:25:49+09:00
AltJS製のNodeモジュールをGitHubから直接インストールすると同時に、そのライブラリのビルドを自動化するpackage.jsonの設定
この投稿では、NodeモジュールをGitHubから直接インストールする際、そのライブラリをインストールのタイミングでビルドする方法を紹介します。
この手法はどういうときに使うか?
普段、
yarn add ライブラリ名
をすると、npmjs.orgのレジストリにアップロードされたモジュールが、手元のnode_modulesに入ってきます。仮に、ライブラリのソースコードがTypeScriptやBebelなどのAltJSであったとしても、node_modulesにインストールされるのは普通ビルド後のJavaScriptです。これはライブラリ開発者がnpmjs.orgに公開するとき、AltJSからJavaScriptにビルドしたものをアップロードしてくれているからです。なので、モジュール利用者は、ライブラリのソースコードがもともとどんな言語で書かれているかを気にする必要がありませんし、ましてや、モジュール利用者が
npm install
しするたびに、わざわざライブラリのソースコードからビルドする必要は普通ありません。どうしてもソースコードからインストール&ビルドしたい場合に使う
しかし、場合によっては「普通」じゃないインストールをしたい場合があります。どうしてもソースコードからインストールする必要がある場合です。
例えば、「ライブラリのGitHubにプルリクエストを送って取り込まれたけれど、npmjs.orgには未公開。でも、すぐにそのリビジョンのコードを使いたい」といったケースです。
このケースでは、どこにもビルド済みのコードがなく、TypeScriptやBebelなどのAltJSのコードがGitHub上にあるだけ、という状況になります。
YarnやNPMでは、GitHubのURLを指定してmasterブランチをnode_modulesにインストールすることができます:
npm install 'https://github.com/suin/isObject.js.git#master' # or yarn add 'https://github.com/suin/isObject.js.git#master'しかし、この方法ではAltJSのコードしかインストールされず、必要となるビルド後のJSファイルが手に入りません:
ls -a1 node_modules/@suin/is-object CHANGELOG.md index.ts # TypeScriptしかない! LICENSE package.jsonこのままでは、モジュールを
import
/require()
しても使えません。モジュールのインストールと同時にビルドを実行する必要があるのです。
モジュールインストール時にビルドを実行するためのpackage.jsonの設定
では、
npm install
やyarn add
と同時に、インストールしたモジュールのビルドを実行するにはどうしたらいいか、どういう設定をpackage.jsonに施したらいいかについて説明します。ステップ1: モジュールのビルド方法を調べる
まず、自プロジェクトのpackage.jsonをいじる前に、ビルドするパッケージのpackage.jsonを開いて情報収集します。
例えば、
@suin/is-object
パッケージのpackage.jsonは以下のようになっています:package.json{ "name": "@suin/is-object", "version": "1.1.3", "description": "TypeScript friendly isObject function", ... "main": "index.js", ... "scripts": { "build": "tsc", ... }, "devDependencies": { ... } }この中で注目するのは、
main
フィールドとscripts
フィールドです。1つ目の
main
フィールドはビルド後の成果物のファイル名です。ライブラリ利用者の我々としては、これが欲しいのでこのファイル名をメモしておきます。2つ目の
scripts
フィールドには、たいていビルドするためのコマンドが定義されています。多くの場合、build
という名前で定義することが多いです。ここはライブラリの開発者次第なので、よく調べてみる必要があります。ライブラリによってはREADME.mdやCONTRIBUTING.mdにビルドの手順が書いてあるかもしれないので、そこも目を通しておきます。この@suin/is-object
パッケージの例では、build
コマンドだけで良いことがわかります。また、パッケージがYarnとNPMどちらでビルドしているのかも調べておきます。これは、yarn.lockとpackage-lock.jsonどちらがあるかで判断できます。
ステップ2:
postinstall
にビルドのスクリプトを組む上の調査で、ビルド後の成果物ファイル名とビルド方法が分かったら、自プロジェクトの
package.json
に手を加えて、モジュールのインストール時にビルドを実行するように設定します。
yarn add
やnpm install
後に何かコマンドを実行するには、postinstall
をscripts
フィールドに追加します:package.json{ "name": "my-app", "scripts": { "postinstall": "echo インストール後になにかするよ" } }
postinstall
にはシェルスクリプトが書けるので、ここにモジュールをビルドするためのコマンドを書いていきます。@suin/is-object
パッケージの例では、次のようなステップでビルドします。
- 成果物ファイルの
index.js
があるか確認する。- あればビルドしない。→ 終了
- なければ、
- モジュールが依存するパッケージをインストールする。
- モジュールをビルドする。 → 終了
これをシェルスクリプトに起こすと、次のようになります:
set -eux # 成果物ファイルの`index.js`があるか確認する。 if [ ! -f ./node_modules/@suin/is-object/index.js ] then # なければ、 cd ./node_modules/@suin/is-object # モジュールが依存するパッケージをインストールする。 yarn install --frozen-lockfile # モジュールをビルドする。 yarn build fiこのシェルスクリプトを
postinstall
に書いておきます:package.json{ "scripts": { "postinstall": "set -eux; if [ ! -f ./node_modules/@suin/is-object/index.js ]; then cd ./node_modules/@suin/is-object; yarn install --frozen-lockfile; yarn build; fi" }この状態で、モジュールをGitHubからインストールしてみます:
yarn add 'https://github.com/suin/isObject.js.git#master'実行結果は次のようになります。ライブラリのインストール後に、ライブラリのビルドが行われました:
以上で、package.jsonの設定は完了です。
これにより、AltJS製のNodeモジュールをGitHubから直接インストールすると同時に、そのライブラリのビルドが自動化されます。
- 投稿日:2020-07-01T08:39:26+09:00
共通jsの発火位置を秒で特定する
はじめに
.js-accordionとクラス名に当てるとアコーディオンになるみたいな、
良く使われるjs処理がどこで記述されてるかを一瞬で見つける方法です。結論を先に書くと
Networkタブで検索かけたら一瞬で見つかりました!使い所
サイト全体で共通のjsファイルなんかを読み込んでおいて、
その中で良く使われるアコーディオンなり、アンカーリンクなりの処理を書いておいて、どこでも呼び出せるようにみたいなことって良くあるかと思います。ただ、ある程度長く運用を続けているサイトになってくると、
共通で読み込んでるjsファイルが数が増えてきちゃって
- base.js
- share.js
- utilty.js
みたいな後から入った人からするとどこに何が書かれてるか分かんない状況になっちゃうんですよね。
で、アコーディオンの挙動に問題があった場合とかまず調査するためにその処理がどこに書かれてるか探すところから始めるかと思いますが、これを僕は検証機能の「source」タブを使って読み込まれたjsファイルを一つ一つ開いて「.js-accordion」で検索かけたりして探してたんですよね。これを「Network」タブの方でCtrl+fしたら左側にこんな検索メニューでてくるんですよ。
ここで「.js-accordion」とかって検索すると、
それが書かれてるファイルと行数がパッと出てくるんでそれで解決です。誤操作でたまたまこの探し方発見したんですけど感動しました。