20200409のJavaScriptに関する記事は24件です。

Pixabay APIでフリー画像を取得しLINE botで送信する

やること

 Pixabay APIという、フリー素材の画像、イラスト、動画を提供しているAPIを利用して取得した画像をLINE botへ送信してみます。
 LINE botからユーザに画像を送信する方法は次の記事にまとめました。
【LINE botから画像送信する方法】問いかけると柴犬の画像を返してくれるLINE botを作ってみた

image.png

できたもの

 何かテキストを入力すると、ビーバーの画像を返してくれる。(本当は、テキストに応じた画像を出したかった...。)

作ってみよう!

 実際にPixabay APIを利用して画像を取得し、LINE botから画像を送信してみます。

手順は次の通り。

  1. Pixabayに登録し、アクセスキーを取得
  2. 試しにURLを作ってアクセスしてみる
  3. プログラムからアクセス(Node.js) 
  4. LINE botに送信!

 サーバサイドの処理はローカルPCで動かし、ngrokでトンネリングしています。
構成はこんな感じです。

image.png

1.Pixabayに登録し、アクセスキーを取得

 登録方法はこちらのとおりに進めました。いきなりPixabayのサイトに行くと、「どこにAPIの説明があるんだ?」状態となります(~_~;)

2.試しにURLを作ってアクセスしてみる

Google chormを開いてURLを入力、実行!!画面いっぱいにJsonデータが表示されれば確認OKです。URLは次のフォーマットで入力します。APIキーが必要なので、その部分は入力が必要となっています。この例では、ビーバーの画像を検索しています。

https://pixabay.com/api/?key=APIキーを入れる&q=beaver

image.png

3.プログラムからアクセス(Node.js)

 今回はローカルPC上でNode.jsのサーバーを実行し、Pixabayから画像を取得するようにしました。ここでは重要な処理だけ解説し、全ソースコードは最後に載せています。

 LINE botで画像送信するために必要な情報は画像のURlです。この情報をPixabay APiで取得したjsonデータの中から抜き出します。次の2行で必要なデータをログに表示することができます。

console.log(response.data.hits[0].previewURL);
console.log(response.data.hits[0].userImageURL);

 このログは次のように表示されます。このURLをブラウザに入力すると画像を確認することができます。

https://cdn.pixabay.com/photo/2020/03/19/02/22/beaver-4946032_150.jpg
https://cdn.pixabay.com/user/2020/03/14/23-23-43-766_250x250.jpg

4.LINE botに送信!

 取得した画像のURLを次のファーマっとに当てはめれば完了です!

return client.replyMessage(event.replyToken, {
    type: 'image',
    originalContentUrl: 'オリジナルサイズの画像URL', 
    previewImageUrl: 'LINEアプリのトーク画面にプレビューされるサイズの画像URL'
});

 例では次のようになります。

return client.replyMessage(event.replyToken, {
    type: 'image',
    originalContentUrl: 'https://cdn.pixabay.com/user/2020/03/14/23-23-43-766_250x250.jpg', 
    previewImageUrl: 'https://cdn.pixabay.com/photo/2020/03/19/02/22/beaver-4946032_150.jpg'
  });

おわりに

 ビーバーの画像を出したいのに、カピバラがいたりする。Pixabayはユーザ投稿型なので、本来欲しい画像出なかったりするのはしょうがない気もする...。画像解析で対象物のマッチングするとフィルタリングできそう。

あと、Pixabayの404 Not Foundページはいいセンスしている

image.png

ソースコード

'use strict';

const express = require('express');
const line = require('@line/bot-sdk');
const axios = require('axios');
const PORT = process.env.PORT || 3000;

const config = {
  channelSecret: 'LINE botのチャンネルシークレット',
  channelAccessToken: 'LINE botのアクセストークン'
};

const app = express();

app.post('/webhook', line.middleware(config), (req, res) => {
    console.log(req.body.events);

    //ここのif文はdeveloper consoleの"接続確認"用なので後で削除して問題ないです。
    if(req.body.events[0].replyToken === '00000000000000000000000000000000' && req.body.events[1].replyToken === 'ffffffffffffffffffffffffffffffff'){
      res.send('Hello LINE BOT!(POST)');
      console.log('疎通確認用');
      return; 
    }

    Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
});

const client = new line.Client(config);

async function handleEvent(event) {
  var url = 'https://pixabay.com/api/?key=PixabayのAPIキーを入力する&q=beaver&image_type=photo&category=animals';

  console.log('start');
  if (event.type !== 'message' || event.message.type !== 'text') {
    //console.log('errpr message');
    return Promise.resolve(null);
  }

  // LINE botからのリクエストで実行される処理を書く
  console.log("access to Pixabay");

  // Pixabayにアクセス
  const response = await axios.get(url);

  console.log(response.data.hits[0].previewURL);
  console.log(response.data.hits[0].userImageURL);

  return client.replyMessage(event.replyToken, {
    type: 'image',
    originalContentUrl: response.data.hits[3].userImageURL, 
    previewImageUrl: response.data.hits[3].previewURL
  });
}

app.listen(PORT);
console.log(`Server running at ${PORT}`);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pixabay APIでフリー素材の画像を取得しLINE botで送信する

やること

 Pixabay APIという、フリー素材の画像、イラスト、動画を提供しているAPIを利用して取得した画像をLINE botへ送信してみます。
 LINE botからユーザに画像を送信する方法は次の記事にまとめました。
【LINE botから画像送信する方法】問いかけると柴犬の画像を返してくれるLINE botを作ってみた

image.png

できたもの

 何かテキストを入力すると、ビーバーの画像を返してくれる。(本当は、テキストに応じた画像を出したかった...。)

作ってみよう!

 実際にPixabay APIを利用して画像を取得し、LINE botから画像を送信してみます。

手順は次の通り。

  1. Pixabayに登録し、アクセスキーを取得
  2. 試しにURLを作ってアクセスしてみる
  3. プログラムからアクセス(Node.js) 
  4. LINE botに送信!

 サーバサイドの処理はローカルPCで動かし、ngrokでトンネリングしています。
構成はこんな感じです。

image.png

1.Pixabayに登録し、アクセスキーを取得

 登録方法はこちらのとおりに進めました。いきなりPixabayのサイトに行くと、「どこにAPIの説明があるんだ?」状態となります(~_~;)

2.試しにURLを作ってアクセスしてみる

Google chormを開いてURLを入力、実行!!画面いっぱいにJsonデータが表示されれば確認OKです。URLは次のフォーマットで入力します。APIキーが必要なので、その部分は入力が必要となっています。この例では、ビーバーの画像を検索しています。

https://pixabay.com/api/?key=APIキーを入れる&q=beaver

image.png

3.プログラムからアクセス(Node.js)

 今回はローカルPC上でNode.jsのサーバーを実行し、Pixabayから画像を取得するようにしました。ここでは重要な処理だけ解説し、全ソースコードは最後に載せています。

 LINE botで画像送信するために必要な情報は画像のURlです。この情報をPixabay APiで取得したjsonデータの中から抜き出します。次の2行で必要なデータをログに表示することができます。

console.log(response.data.hits[0].previewURL);
console.log(response.data.hits[0].userImageURL);

 このログは次のように表示されます。このURLをブラウザに入力すると画像を確認することができます。

https://cdn.pixabay.com/photo/2020/03/19/02/22/beaver-4946032_150.jpg
https://cdn.pixabay.com/user/2020/03/14/23-23-43-766_250x250.jpg

4.LINE botに送信!

 取得した画像のURLを次のファーマっとに当てはめれば完了です!

return client.replyMessage(event.replyToken, {
    type: 'image',
    originalContentUrl: 'オリジナルサイズの画像URL', 
    previewImageUrl: 'LINEアプリのトーク画面にプレビューされるサイズの画像URL'
});

 例では次のようになります。

return client.replyMessage(event.replyToken, {
    type: 'image',
    originalContentUrl: 'https://cdn.pixabay.com/user/2020/03/14/23-23-43-766_250x250.jpg', 
    previewImageUrl: 'https://cdn.pixabay.com/photo/2020/03/19/02/22/beaver-4946032_150.jpg'
  });

おわりに

 ビーバーの画像を出したいのに、カピバラがいたりする。Pixabayはユーザ投稿型なので、本来欲しい画像出なかったりするのはしょうがない気もする...。画像解析で対象物のマッチングするとフィルタリングできそう。

あと、Pixabayの404 Not Foundページはいいセンスしている

image.png

ソースコード

'use strict';

const express = require('express');
const line = require('@line/bot-sdk');
const axios = require('axios');
const PORT = process.env.PORT || 3000;

const config = {
  channelSecret: 'LINE botのチャンネルシークレット',
  channelAccessToken: 'LINE botのアクセストークン'
};

const app = express();

app.post('/webhook', line.middleware(config), (req, res) => {
    console.log(req.body.events);

    //ここのif文はdeveloper consoleの"接続確認"用なので後で削除して問題ないです。
    if(req.body.events[0].replyToken === '00000000000000000000000000000000' && req.body.events[1].replyToken === 'ffffffffffffffffffffffffffffffff'){
      res.send('Hello LINE BOT!(POST)');
      console.log('疎通確認用');
      return; 
    }

    Promise
      .all(req.body.events.map(handleEvent))
      .then((result) => res.json(result));
});

const client = new line.Client(config);

async function handleEvent(event) {
  var url = 'https://pixabay.com/api/?key=PixabayのAPIキーを入力する&q=beaver&image_type=photo&category=animals';

  console.log('start');
  if (event.type !== 'message' || event.message.type !== 'text') {
    //console.log('errpr message');
    return Promise.resolve(null);
  }

  // LINE botからのリクエストで実行される処理を書く
  console.log("access to Pixabay");

  // Pixabayにアクセス
  const response = await axios.get(url);

  console.log(response.data.hits[0].previewURL);
  console.log(response.data.hits[0].userImageURL);

  return client.replyMessage(event.replyToken, {
    type: 'image',
    originalContentUrl: response.data.hits[3].userImageURL, 
    previewImageUrl: response.data.hits[3].previewURL
  });
}

app.listen(PORT);
console.log(`Server running at ${PORT}`);
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

2020年版 VSCodeの良さげな拡張機能紹介

日々VSCodeをより気持ちよく使用するために拡張機能の探求は欠かせません。
今回は私の独断と偏見で良さげな拡張機能を紹介します。

見た目編

Peacock

https://marketplace.visualstudio.com/items?itemName=johnpapa.vscode-peacock
image.png

VSCodeのウィンドウをおしゃれにできます。
私は複数の環境(プロジェクトやプログラミング言語)を使い分けるので、ウィンドウごと色を変えることで気持ちも一緒に切り替えています。

ウィンドウ
公式より拝借

Ubuntu VSCodeTheme

https://marketplace.visualstudio.com/items?itemName=ThiagoLcioBittencourt.ubuntuvscode
image.png

見た目をUbuntuっぽくできます。ただの気分です。
私はMacの壁紙をUbuntuにしています(Ubuntu使えというツッコミは受け付けません)

Ubuntu
公式より拝借

Rainbow CSV

https://marketplace.visualstudio.com/items?itemName=mechatroner.rainbow-csv
image.png

CSVを開いたときに色分けしてくれて見やすくなります。

csv
公式より拝借

indent-rainbow

https://marketplace.visualstudio.com/items?itemName=oderwat.indent-rainbow
image.png

インデントがカラフルになってちょっと見易くなります。

共通機能系

Settings Sync

https://marketplace.visualstudio.com/items?itemName=Shan.code-settings-sync
image.png

複数のPCでVSCodeを使用する場合、各PCでVSCodeの設定を行う必要がありますが、こちらの拡張機能を使えば設定を共有できます!

Vim

https://marketplace.visualstudio.com/items?itemName=vscodevim.vim
image.png

VSCodeをVimっぽく使いたい方におすすめ。ただし、コテッコテのVimmerは痒いところに手が届かない可能性あり。
私は普段VimとVSCodeのハイブリットなので重宝しています。

GitLens

https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens
image.png

Git使うならまず入れた方が良いです。
Git操作をせずにいつ誰が該当のソースを修正したのかがパッと分かります。

ex
ex
公式より拝借

言語別

ここからは言語別の拡張機能紹介です。
デバッガーやLanguage Supportは書くまでも無いと思うので割愛します。

PHP

PHP Intelephense

https://marketplace.visualstudio.com/items?itemName=bmewburn.vscode-intelephense-client
image.png

Language Supportは書かないって言ったくせに早速書きました。
PHPは他のLanguage Supportもあるのですが、こちらの拡張機能の方が頭が良い気がしたので紹介します。

PHP DocBlocker

https://marketplace.visualstudio.com/items?itemName=neilbrayfield.php-docblocker
image.png

PHPDocを自動で作ってくれます。intって書いてもintegerと書かれるお茶目さがあります。
(多分自分で設定すれば修正できますが)

PHP Namespace Resolver

https://marketplace.visualstudio.com/items?itemName=MehediDracula.php-namespace-resolver
image.png

Classのインポートを自動でやってくれます。(IDEから乗り換えた人はとりあえず入れて見てください。ストレスが減ります)
一括インポートもできるので結構良いです。

Ruby

endwise

https://marketplace.visualstudio.com/items?itemName=kaiwood.endwise
image.png

自分でendって書くの面倒ですよね?
こちらの拡張機能は自動で付与してくれます。

ruby-rubocop

https://marketplace.visualstudio.com/items?itemName=misogi.ruby-rubocop
image.png

別途Gemの追加が必要ですが、静的チェックとフォーマットをやってくれるので入れておきましょう。
自分の悪いところも容赦無く教えてくれたり、修正してくれるので勉強にもなります。

ifじゃなくてunless使いな〜とか、ネスト減らせるよ〜とか、
eachじゃなくてmap使いなーとか言ってくれるのでレビュワーの負担も減ると思います。

JavaScript

Prettier

https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode
image.png

JS触っている人なら当たり前だと思いますが、自動でコード整形してくれるので便利です。
ただ、保存時に整形する設定を入れると差分がめっちゃ出るリポジトリもあったりすると思うので程々に。

Debugger for Chrome

https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome
image.png

デバッガーも書かないと言ったくせに書いてしまいました。
JSのデバッグをVSCode上で行えるので便利です。もうconsole.logデバッグとはおさらば。

その他

Remote-SSH

https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-ssh
image.png

SSH先のPC上のソースを編集することができます。
私は自宅のUbuntuや、Vagrantに対してSSHして開発する際に使用しています。
ただソースを編集できるだけでなく、ポートフォワードも行えるのでリモート先の8080ポートをローカルの8080にすることもできたりします。
とても便利なのでSSHで開発する方はぜひ使ってみてください。

Remote-Container

https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers
image.png

Dockerコンテナの中のソースをVSCodeから修正できちゃいます。
もちろんコンテナなのでVolumeマウントしないと修正内容が飛んでしまいますが、開発環境も含めてコンテナ化しちゃうような時は良さげ。

昔別記事書いてます。
https://qiita.com/MasanoriIwakura/items/fe1bb1fade98c0aa65d2
https://qiita.com/MasanoriIwakura/items/e7a2045b2de28c76ccd9

REST Client

https://marketplace.visualstudio.com/items?itemName=humao.rest-client
image.png

なんと、VSCode上からREST APIを試すことができます!
デバッグや外部APIの確認に便利。


まだまだ拡張機能は沢山あるので、気が向いたタイミングで更新していきます!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Kinx ライブラリ - Double

Double

はじめに

「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。言語はライブラリが命。ということでライブラリの使い方編。

今回は Double です、... と言いつつ、Double 組み込み特殊メソッドは少ないので、短い記事です。

Double 特殊オブジェクト

Double オブジェクトに対して関数定義する例は以下の通り。

Double.minus1 = function(value) {
    return value - 1;
};
var val = 100.5.minus1();
System.println(val);

実行してみよう。

99.5

他の特殊オブジェクトと同様、レシーバーが第 1 引数に来ます。

Double

組み込み特殊メソッド

メソッド 意味
Double.toString(val, format) val を文字列に変換する。format% で始まり、aAeEfFgG のいずれかで終わる文字列。省略時は %g
Double.toInt(val) val を Integer に変換する。

format は現在プレビュー版ではサポートされていません。正式版ではサポート予定です。というのも、これを書いていてあったほうが良いなー、と思ったということで。

Math オブジェクト・メソッド

Double オブジェクトには Math オブジェクトと同じ特殊メソッドが存在する。詳細は以下を参照。

具体例で書くと、例えば以下のように書ける。

var a = 2.0.pow(10);   // Math.pow(2.0, 10) と同じ  => 1024
var b = (-10.8).abs(); // Math.abs(-10.8)   と同じ  => 10.8

単項マイナス(-)は関数呼び出しより優先順位が低いため、カッコで括る必要があることに注意。

おわりに

Integer 同様、ここ を見ながら今サポートしてないメソッドとかを順次サポートしていこうかなー。

ではまた次回。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

webpack-dev-serverのhot reloadでCPU使用率が上がる問題対応

概要

タイトル通り
nodeの使用率が100%前後になってMacが高熱になってて辛かった

方法

webapck-dev-serverのconfigに以下を追記する

watchOptions: {
  poll: 1000,
  ignored: ['node_modules']
}

結果

追加前は100%前後だったCPU使用率が20%前後に落ち着いた
Macが熱くて火傷しそうだったので助かった

参考

github issueにあった
https://github.com/webpack/webpack-dev-server/issues/2140

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Powershellでクリップボードを監視しよう

Powershellでクリップボードを監視しよう

windows標準環境(Powershell+ブラウザ)でクリップボードを監視しようではないかという投稿です。

背景

色々とセキュリティが厳しい昨今、PCにソフト1つ入れるのも難しい環境がお有りではないでしょうか。
クリップボード監視ツールなんてフリーソフトでcliborとかいろいろあるんですが、
そのへんが導入できない壁があり、
windows標準環境だけでクリップボード監視できるものを作ってみようと思い立ちました。

この記事の対象者

  • 会社でWindows使っている方+なかなかソフトが自由に入れられない方
  • Powershellでクリップボード監視する方法に興味がある方

今回作るもの

overview.png

フォルダ構成

■フォルダ
│  backup.bat
│  clipboard-monitor.ps1
│  clipboard.html
│  start.bat
│
├─backup
│      yyyymmdd_HHMMSS_clipboard.html
│
└─tmpl
        clipboard.html

ソース一覧

主処理起動用のバッチ(start.bat)

powershellを呼び出すバッチを作っておきます

@echo off
powershell -ExecutionPolicy RemoteSigned .\clipboard-monitor.ps1

主処理 監視バッチ(clipboard-monitor.ps1)

次にstart.batから呼ばれるpowershellです。

ソースコード
# set window title
(Get-Host).UI.RawUI.WindowTitle = $MyInvocation.MyCommand

# get clipboard text
Add-Type -AssemblyName System.Windows.Forms
$clipText = [Windows.Forms.Clipboard]::GetText()
$dateFormat = Get-Date -Format "yyyyMMdd"

#Write-Host $clipText

Write-Host ""
Write-Host " ■■■■■■■■■■■■"
Write-Host " ■"
Write-Host " ■ CLIP BOARD START!"
Write-Host " ■"
Write-Host " ■■■■■■■■■■■■"
Write-Host ""

while ($true) {
  # check the data, whether it do change or not.
  $latestClipText = [Windows.Forms.Clipboard]::GetText()
  if ($latestClipText -ne $clipText) {
    # if changed, update data.
    $clipText = $latestClipText

    # output console.
    Write-Host "- - -"
    Write-Host $clipText

    # write data to history file.
    $baseClipText = ${clipText}
    $baseClipText = ${baseClipText} -replace "&", "&"
    $baseClipText = ${baseClipText} -replace "'", "'"
    $baseClipText = ${baseClipText} -replace "``", "`"
    $baseClipText = ${baseClipText} -replace '"', """
    $baseClipText = ${baseClipText} -replace "<", "&lt;"
    $baseClipText = ${baseClipText} -replace ">", "&gt;"
    $baseClipText = ${baseClipText} -replace "\r\n", "<br/>"
    $baseClipText = ${baseClipText} -replace "\n", "<br/>"
    $time = Get-Date -Format "yyyy/MM/dd HH:mm:ss"
    $fileOutput = "<tr class='cb-tr-clip'><td><span>${time}</span></td><td>${baseClipText}</td></tr>"
    Write-Output ${fileOutput} | Add-Content "clipboard.html" -Encoding UTF8
  }
  # 1秒スリープ
  Start-Sleep -Seconds 1
}

補足

  • HTMLファイル出力するので、手前でescape処理を挟んでいます。
  • 1秒おきにクリップボードを監視して、変更があればHTML(clipboard.html)に書き込みに行っています。

クリップボードテキスト出力先ファイル(clipboard.html)

出力先のHTMLファイルです。
ブラウザで表示した時に確認しやすいようにスタイルをJS処理を書いています。

ソースコード
<head>
  <!-- Required meta tags -->
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />

  <!-- Bootstrap CSS -->
  <link
    rel="stylesheet"
    href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
    integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
    crossorigin="anonymous"
  />
  <!-- fontawesome -->
  <link
    rel="stylesheet"
    href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css"
    integrity="sha256-UzFD2WYH2U1dQpKDjjZK72VtPeWP50NoJjd26rnAdUI="
    crossorigin="anonymous"
  />
  <link href="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/toastr.min.css" rel="stylesheet" />
  <title>Clip4Win</title>
  <style>
    .header-title-text {
      font-size: 5rem;
      letter-spacing: -5px;
      line-height: 140px;
      font-weight: bold;
      color: white;
    }
    .sticky_table thead th {
      /* 縦スクロール時に固定する */
      position: -webkit-sticky;
      position: sticky;
      top: -1px;
      /* tbody内のセルより手前に表示する */
      z-index: 10;
      background-color: black;
    }
    .sticky_table tbody td span {
      /* 縦スクロール時に固定する */
      position: -webkit-sticky;
      position: sticky;
      top: 55px;
      z-index: 9;
    }
    .cb-tr-clip:hover {
      color: #ccc !important;
    }
    .cb-reload {
      position: fixed;
      bottom: 100px;
      right: 100px;
      cursor: pointer;
    }
  </style>
</head>
<body class="container bg-dark text-white" onload="init()">
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
  <script
    src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
    integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
    crossorigin="anonymous"
  ></script>
  <script
    src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
    integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
    crossorigin="anonymous"
  ></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script>
  <script>
    function init() {
      document.querySelectorAll(".cb-tr-clip").forEach(function (el) {
        // console.log("innerHTML > " + el.getElementsByTagName("td")[1].innerHTML);
        // console.log("innerText > " + el.getElementsByTagName("td")[1].innerText);
        // console.log("textContent > " + el.getElementsByTagName("td")[1].textContent);
        //el.getElementsByTagName("td")[1].textContent = escapeHtml(el.getElementsByTagName("td")[1].innerHTML);
        el.getElementsByTagName("td")[1].dataset.text = el.getElementsByTagName("td")[1].innerText;
        el.getElementsByTagName("td")[1].setAttribute("title", el.getElementsByTagName("td")[1].innerText);
        el.getElementsByTagName("td")[1].innerText = tripleReader(el.getElementsByTagName("td")[1].innerText);
        el.addEventListener("click", function () {
          // console.log("click");
          const text = el.getElementsByTagName("td")[1].dataset.text;
          if (execCopy(text)) {
            // alert("Copied!!");
            toastr.options = {
              positionClass: "toast-bottom-right",
              timeOut: "2000",
            };
            toastr.success("Success to Copy");
          } else if (window.getSelection) {
            alert("Failed to copy...");
          }
        });
      });
      document.querySelector("#reload").addEventListener("click", function () {
        window.location.reload();
      });
      var tbody = $("table tbody");
      tbody.html($("tr", tbody).get().reverse());
    }
    /**
     * コピー処理
     */
    function execCopy(string) {
      // console.log("target string: " + string);
      // 空div 生成
      var tmp = document.createElement("div");
      // 選択用のタグ生成
      var pre = document.createElement("pre");

      // 親要素のCSSで user-select: none だとコピーできないので書き換える
      pre.style.webkitUserSelect = "auto";
      pre.style.userSelect = "auto";

      tmp.appendChild(pre).textContent = string;

      // 要素を画面外へ
      var s = tmp.style;
      s.position = "fixed";
      s.right = "200%";

      // body に追加
      document.body.appendChild(tmp);
      // 要素を選択
      document.getSelection().selectAllChildren(tmp);

      // クリップボードにコピー
      var result = document.execCommand("copy");

      // 要素削除
      document.body.removeChild(tmp);

      return result;
    }
    function tripleReader(text, count) {
      if (!count) count = 30; // default 20
      if (text.length > count) {
        text = text.split("\n")[0].substr(0, count) + "";
      }
      return text;
    }
  </script>
  <div class="d-flex align-items-center justify-content-center w-100" style="height: 200px;">
    <h1 class="header-title-text">CLIPBOARD</h1>
  </div>
  <div class="cb-reload">
    <div class="d-flex flex-column">
      <i id="reload" class="fas fa-sync-alt fa-3x"></i>
    </div>
  </div>
  <table class="w-100 table table-hover text-white cb-table-clip sticky_table table-sm">
    <thead class="cb-th-clip">
      <th style="width: 200px;">日付</th>
      <th>コンテンツ</th>
    </thead>

テンプレートファイル(clipboard.html)

./tmpl/clipboard.html に、clipboard.htmlと同じファイルをおいておきます。

バックアップバッチ(backup.bat)

バックアップを取得するためのバッチです。
出力先のclipboard.htmlが肥大化してきたら実行

ソースコード
@echo off
set date_str=%date:~-10,4%%date:~-5,2%%date:~-2,2%
rem echo %date_str%@
set time_str=%time: =0%
set time_str=%time_str:~0,2%%time_str:~3,2%%time_str:~6,2%
rem echo %time_str%

echo F | xcopy /Y clipboard.html backup\%date_str%_%time_str%_clipboard.html
xcopy /Y tmpl\clipboard.html clipboard.html

バックアップフォルダ(backup)

  • バックアップバッチの出力先として作成しておく!

実行方法

上記のstart.batを実行するだけでOK!

実行イメージ

image.png

ブラウザからclipboard.htmlを確認

image.png

  • 一覧で履歴を確認できる
  • クリックでコピーができる
  • マウスホバーでテキスト全体を表示できる

Github

以下に完成形のソースを上げています。
よろしければご確認ください。

さいごに

すごく走り書きですが以上です。
ご参考になれば幸いです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【初心者向け】Vue.jsをToDoアプリを作りながら学ぼう

この記事では、Todoアプリを作りながら学んだVue.jsの基本をまとめています。
超初心者向け、超入門です。難しいことは書けないのでご容赦ください。。

筆者の学習時の技術レベル

  • プログラミング学習開始して5ヶ月、エンジニア実務経験 (Rails) 3ヶ月
  • バックエンド Rubyを中心に勉強しており、今後もバックエンドがメインのつもり
  • フロントエンドはJavaScript、jQueryの基本的な読み書きはできる程度

必要な前提知識

  • HTML/CSS/JavaScriptの基本的な知識

どうやってVue.jsを勉強したの?

超Vue JS 2 完全パック - もう他の教材は買わなくてOK! (Vue Router, Vuex含む)

僕は動画のほうが入りやすいので、まずUdemyですね。

タイトルにもあるように、他の教材を買わなくてもいいくらい充実した内容です。
Todoアプリを作るくらいなら全部やらなくても大丈夫ですが、
「Vue Routerを使ったSPA(single page application)を作りたい」
「Vue.jsで大規模開発を経験したい」といった人の導入にもおすすめです。

基礎から学ぶ Vue.js

こちらは定番のVue.js入門書です。

公式サポートサイトも充実しており、
今回のTodoアプリ作成もこのサイトをかなり参考しました。

公式ドキュメント(日本語)

Vue.jsは日本語の公式ドキュメントも充実しています。
上記のような教材で学びつつ公式情報を確認するのが、個人的にはいいかなと思いました。

Vue.jsってなにがいいの?

Vue.jsは、JavaScriptフレームワークの1つです。
要は「JavaScriptを簡単に使いやすくしたもの」で、他にはjQueryやReactがあります。

※フレームワークとライブラリの違いは割愛→こちらを参考にしてください

Vue.jsの特徴は主に下記3つです。(他にも色々とありますが、、)

1. 学習コストが低い

  • 日本語ドキュメント情報が充実している
  • 書籍や動画などの教材もわりと多い
  • 構造がシンプルで記述量も少ないため、比較的早く習得することができる

2. MVVMモデルが採用されている

  • MVVMモデルはModel(M)-View(V)-ViewModel(VM)の設計思想です。
  • なんか難しそうなので超簡単にイメージだけ説明します。

mvvm.png
(公式ガイドより抜粋)

  • ViewはDOM(Document Object Model)のことで、JavaScriptで扱うHTMLの要素です。
  • ModelはJavaScriptオブジェクトのこと。
  • ViewModelが重要で、ModelViewを同期するオブジェクト、Vueインスタンスです。ここで双方向データバインディングを実現しています。

双方向データバインディングとは、データと描写(View)を同期させる仕組みのことで、上記の場合View側・Model側どちらからでもデータを変更すれば同期されるようになっています。
Vue.jsは少ないコード量でこの仕組を実現しています。

設計パターンの難しい説明は割愛しますが、画面上に表示されるViewとJavaScriptを簡単につなげてくれるのがVueです。
主にここをゴニョゴニョ書いていきます。

3. SPA(シングルページアプリケーション)の作成に向いている

SPAは、単一のWebページからなるWebアプリケーションです。
画面遷移がほぼなく、動作が速い使い勝手の良いWEBアプリで、例えばWeb版のSlackやFacebookメッセンジャー、GoogleMapなどですね。

Vue.jsプロジェクトで、SPAは比較的簡単に作ることができます。

実際にVue.jsでToDoアプリを作ろう

下記サイトを参考にしたので、試してみてください。
- Vue.jsではじめるMVVM入門 | DeNA DESIGN BLOG
- ToDoリストを作りながら学習しよう! | 基礎から学ぶ Vue.js

今回はVue CLIのような開発ツールやVue Routerは使わず、シンプルにindex.htmlstyle.cssmain.jsの3ファイルで作成します。

まず完成形から共有

Image from Gyazo

1. HTML、CSSをざっくり組み立てます

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Vue.js TODO APP</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>
  <div id="to-do"> <!-- Vue.jsで扱うDOM要素を指定するためidを付与 -->
    <p>
      NewTask:
      <input type="text">
      <button>Add</button>
    </p>
    <hr>
    <ul>
      <li>
        <input type="checkbox">
        <span>Rubyの勉強をする</span>
        <button>Delete</button>
      </li>
      <li>
        <input type="checkbox">
        <span>Vue.jsのアプリを作る</span>
        <button>Delete</button>
      </li>
      <li>
        <input type="checkbox">
        <span>Youtubeをみる</span>
        <button>Delete</button>
      </li>
    </ul>
  </div>
</body>
</html>
style.css
ul {
  margin: 0;
  padding: 0;
  list-style-type: none;
}

ul > li {
  margin: 5px;
  text-indent: 0;
}

#to-do {
  width: 800px;
  margin: 0 auto;
}

2. Vue.jsを導入していきます

今回は簡単にscriptタグでHTMLに直接埋め込むCDNを使用します。

index.html
  <!-- Vue.js -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
  <script src="./main.js"></script>

そしてjsファイルにまずVueインスタンスを作成し、Vue.jsで扱う要素を指定します。
今回はid="to-do"以下の要素を扱います。
elはviewと紐付ける要素を指定します。classの場合、.to-doのようになります。

main.js
new Vue({
  el: '#to-do'
});

3. 初期値を設定しよう

HTMLにタスクが3つありますが、それらを初期値としてVueで設定します。

main.js
new Vue({
  el: '#to-do',
  data: {
    todos: [
      { task: 'Rubyの勉強をする', isCompleted: false },
      { task: 'Vue.jsのアプリを作る', isCompleted: false },
      { task: 'Youtubeをみる', isCompleted: false }
    ]
  }
});

isCompleted: falseは後ほど説明します。
dataは使用するデータの初期値を定義するオプションです。オブジェクトは配列も登録できます。

今回はtodosにタスクを3つ配列で、中身は連想配列で定義しています。

4. 登録したTodoを表示する

ここで登場するのが「ディレクティブ」です。
ディレクティブはviewに動きをつけるための特別な属性で、v-〇〇といった形をしています。

今回は配列で指定したTodoを繰り返し処理で表示したいので、v-forを使用します。

index.html
<li v-for="todo in todos">
  <input type="checkbox">
  {{ todo.task }}
  <button>Delete</button>
</li>

v-for

v-forの値はtodo in todosですが、
形式は(個々の要素) in (繰り返したいオブジェクト)といった具合です。

vueの値は二重中括弧で表示できるので、
タスク名の取得は{{ todo.task }}となります。
(連想配列のキーから取得しています)

5. チェックするとタスク完了にする(取り消し線を入れる)

次にチェックするとタスク名に取り消し線が入るようにします。

index.html
<li v-for="todo in todos">
  <input type="checkbox" v-model="todo.isCompleted">
  <span :class="{ 'complete': todo.isCompleted }">{{ todo.task }}</span>
  <button>Delete</button>
</li>
style.css
ul > li > .complete {
  text-decoration: line-through;
  color: #ddd;
}

v-model

まずv-model="todo.isCompleted"ですが、
v-modelは「双方向データバインディング」を実現するディレクティブです。

値には同期させたいデータを指定するので、今回の場合checkboxvalueとなります。
checkboxの場合、v-modelの値はboolean値(true/false)をとります。
初期値(data)設定の際、各タスクにisCompleted: falseと定義していましたが、
これはチェックボックスにチェックを入れていない状態を指します。

チェックON・OFFでtrue/falseと変化するので、v-modelの値は
v-model="todo.isCompleted"とします。

v-bind

次に:class="{ 'complete': todo.isCompleted }"ですが、
これはv-bind:class="{ 'complete': todo.isCompleted }"の省略記法です。

v-bindはhtmlの属性値をバインドする(結びつける)ためのディレクティブです。
ここではtodo.isCompletedの値(true/false)を受け取り、
classにcompleteをつけるかつけないを、チェックボックスのON・OFFで変更できるようにしています。

つまり、
1. 初期値:チェックボックスOFF、isCompleted: false
2. チェックボックスにチェックを入れる
3. isCompleted: trueとなる
4. v-bindで値を受け取り、completeクラスが付与されcssに定義したデザインが適用される

このような流れになります。

6. タスクを追加できるようにする

まず、入力フォームからタスクを追加するために、受け皿としてdataを追加します。

index.html
<p>
  NewTask:
  <input type="text" v-model="newTask">
  <button>Add</button>
</p>
main.js
new Vue({
  el: '#to-do',
  data: {
    newTask: '',

次にボタンを押したらタスクが追加できるように、クリックイベントを作成します。

index.html
<button v-on:click="addTodo()">Add</button>
main.js
methods: {
  addTodo: function() {
    if (this.newTask == '') return;
    this.todos.push(
      { task: this.newTask, isCompleted: false }
    );
    this.newTask = '';
  }
}

v-on

v-onはDOMイベントの際に使用するディレクティブで、クリックイベントの場合v-on:clickとなります。

そしてvue側でクリックイベントに対応するメソッドを定義します。

まず最初のif (this.newTask == '') return;ですが、入力したタスクはthis.newTaskで取得できます。
未入力の場合にこれ以降の処理を行わない(returnする)ということです。

そしてTodoリストであるthis.todosにタスクを追加し、newTaskは初期化しておきます。

7. タスクを削除できるようにする

index.html
<button v-on:click="deleteTodo(todo)">Delete</button>
main.js
methods: {
  addTask: function() {
    if (this.newTask == '') return;
    this.todos.push(
      { task: this.newTask, isCompleted: false }
    );
    this.newTask = '';
  },
  deleteTodo: function(todo) {
    var index = this.todos.indexOf(todo)
    this.todos.splice(index, 1)
  }
}

最後にdeleteボタンでタスクを削除します。

deleteTodo(todo)では、引数で該当する要素を取得し、
methodにてtodosの配列がらindexを取得、タスクを削除します。

v-on:click@clickと省略できます。

完成!

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Vue.js TODO APP</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>
  <div id="to-do">
    <p>
      NewTask:
      <input type="text" v-model="newTask">
      <button @click="addTodo()">Add</button>
    </p>
    <hr>
    <ul>
      <li v-for="todo in todos">
        <input type="checkbox" v-model="todo.isCompleted">
        <span :class="{ 'complete': todo.isCompleted }">{{ todo.task }}</span>
        <button @click="deleteTodo(todo)">Delete</button>
      </li>
    </ul>
  </div>

  <!-- Vue.js -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
  <script src="./main.js"></script>
</body>
</html>
main.js
new Vue({
  el: '#to-do',
  data: {
    newTask: '',
    todos: [
      { task: 'Rubyの勉強をする', isCompleted: false },
      { task: 'Vue.jsのアプリを作る', isCompleted: false },
      { task: 'Youtubeをみる', isCompleted: false }
    ]
  },
  methods: {
    addTodo: function() {
      if (this.newTask == '') return;
      this.todos.push(
        { task: this.newTask, isCompleted: false }
      );
      this.newTask = '';
    },
    deleteTodo: function (todo) {
      var index = this.todos.indexOf(todo)
      this.todos.splice(index, 1)
    }
  }
});
style.css
ul {
  margin: 0;
  padding: 0;
  list-style-type: none;
}

ul > li {
  margin: 5px;
  text-indent: 0;
}

#to-do {
  width: 800px;
  margin: 0 auto;
}

ul > li > .complete {
  text-decoration: line-through;
  color: #ddd;
}

※参考サイト再掲
下記サイトも参考にしたので、試してみてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[未完]在宅勤務中の家族との距離をうまくとるサイネージ

はじめに

こちらはProtoOutStudioというイケイケなスクールの「LINE Bot+IoTで表現してアウトプット」という課題で製作したものです。
obnizとLINEBotを組み合わせて何か作ろう!というものです。

概要と作れなかったところ

新型コロナウィルスの生で在宅になったものの、仕事中の家族との距離が難しい。
特にうちは猫が3匹もいるので、ニャンコちゃん達が騒ぎ始めたら、どちらの部屋に引き取る?とか、ご飯何時ににする?などなど、いちいちメッセージを送らずに部屋越しに会話することがよくあるのですが、
実は今オンラインミーティング中!!!!みたいなことがあって気まずい思いをしたりして、何かトイレの空き情報みたいなサイネージが欲しくて作りました。

obniz Starter Kit| obnizを買ったので、LEDマトリックスで◯×を出そうと思ったのですが、よくわからない文字しか出せず、挙げ句の果てに壊してしまいました。。。テスト画面ですら動かなくなってしまった。

作りたいもの

部屋にいながら部屋の扉にある
1.LINEBotで操作する
2.マトリックスが「今は部屋を開けないで的なマークを出す」
3.何かにデプロイしていつでも動くLINEBot
4.ディスプレイにメッセージを出す
5.扉につける

作れなかったところ

部屋にいながら部屋の扉にある
1.○ → obnizをLINEBotで操作する
2.× → マトリックスが「今は部屋を開けないで的なマークを出す」
3.× → ngrokではなく何かにデプロイしていつでも動くLINEBot
4.○ → ディスプレイにメッセージを出す
5.○ → 扉につける


環境
Node.js v13.7.0
MacBook Pro macOS Mojave
Visual Studio Code 1.44.0


できたもの

マトリックスを壊してしまったのでobnizのディスプレイを使って
LINEBotからオンラインミーティングが始まったら
「開けないで」 と送ると、
大きめの × を表示させる
「終わったよ」と送ると表示が消える
(マトリックスの残骸付き)
cavia_01.jpg
扉への設置は、画鋲を打とうとしてマジで注意を受けたので透明のバックに入れて、ドアノブに引っ掛けるという荒技です。

コード

node.js
'use strict';

// obniz呼び出し
const Obniz = require('obniz');
var obniz = new Obniz("***");  // Obniz_ID に自分のIDを入れます


const express = require('express');
const line = require('@line/bot-sdk');
const axios = require('axios');
const PORT = process.env.PORT || 3000;

let matrix;
const { createCanvas } = require('canvas')
const canvas = createCanvas(128, 64);
const ctx = canvas.getContext('2d');

const config = {
  channelAccessToken: '***',
  channelSecret: '***'
};
const app = express();
app.post('/webhook', line.middleware(config), (req, res) => {
  console.log(req.body.events);
  Promise
    .all(req.body.events.map(handleEvent))
    .then((result) => res.json(result));
});
const client = new line.Client(config);
// obniz接続
obniz.onconnect = async function () {
  obniz.display.clear();
  obniz.display.print("obniz meets LINE Bot!");
  //matrix = obniz.wired("MatrixLED_HT16K33", { vcc: 1, gnd: 0, sda: 2, scl: 3 });
  //matrix.init(8);
  //matrix.brightness(7);

  ctx.fillStyle = "white";
  ctx.font = "60px sans-serif";
  ctx.strokeStyle = 'rgba(255,255,255,1)'

}

function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  let mes = event.message.text;
  if (event.message.text === '開けないで') {
    mes = '表示を出すよ'; //待ってねってメッセージだけ先に処理
    getAskObnizTemp(event.source.userId); //スクレイピング処理が終わったらプッシュメッセージ
  }
  else if (event.message.text === '終わったよ') {
    obniz.display.clear();

  } else {
    mes = event.message.text;
  }

  return client.replyMessage(event.replyToken, {
    type: 'text',
    text: mes
  });
}

const getAskObnizTemp = async (userId, mes) => {
  //let ctx = obniz.util.createCanvasContext(matrix.width, matrix.height);
  // ctx.fillStyle = "black";
  // ctx.fillRect(0, 0, matrix.width, matrix.height);
  // ctx.fillStyle = "white";
  // ctx.font = "9px sans-serif";
  // ctx.fillText('X', 0, 7);
  let linemes = encodeURIComponent(mes);
  ctx.strokeStyle = 'rgba(255,255,255,1)'
  ctx.beginPath()
  ctx.lineTo(0, 0)
  ctx.lineTo(127, 63)
  ctx.stroke()

  ctx.beginPath()
  ctx.lineTo(127, 0)
  ctx.lineTo(0, 63)
  ctx.stroke()
  //ctx.fillText('  ×', 0, 60);
  await obniz.display.draw(ctx);


  await client.pushMessage(userId, {
    type: 'text',
    text: "変えました",
  });
}

app.listen(PORT);
console.log(`Server running at ${PORT}`);

感想

マトリックスが動いていた時も、このコードだとエラーだらけだったので、なんとも言えない気持ちで着地できずでした。
node-canvasにしてもエラーだったのできっと根本的なところが違うんだろうなと思いました。

 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[未完]在宅勤務同士の家族との距離をうまくとるサイネージ

はじめに

こちらはProtoOutStudioというイケイケなスクールの「LINE Bot+IoTで表現してアウトプット」という課題で製作したものです。
obnizとLINEBotを組み合わせて何か作ろう!というものです。

概要と作れなかったところ

新型コロナウィルスの生で在宅になったものの、仕事中の家族との距離が難しい。
特にうちは猫が3匹もいるので、ニャンコちゃん達が騒ぎ始めたらどちらの部屋に引き取る?とか、
ご飯何時にする?
などなど、いちいちメッセージを送らずに部屋越しに会話することがよくあるのですが、実は「今オンラインミーティング中!!!!ノックしないでー、開けないでー」みたいなことがあって気まずい思いをしたりして、何かトイレの空き情報みたいなサイネージが欲しくて作りました。

obniz Starter Kit| obnizを買ったので、LEDマトリックス(MatrixLED_HT16K33)で◯×を出そうと思ったのですが、よくわからない文字しか出せず、挙げ句の果てに壊してしまいました。。。テスト画面ですら動かなくなってしまった。

作りたいもの

部屋にいながら部屋の外にある
1.LINEBotで操作する
2.マトリックスが「今は部屋を開けないで的なマークを出す」
3.何かにデプロイしていつでも動くLINEBot
4.ディスプレイにメッセージを出す
5.扉にとりつける

作れなかったところ

部屋にいながら部屋の外にある
1.○ → obnizをLINEBotで操作する
2.× → マトリックスが「今は部屋を開けないで的なマークを出す」
3.× → ngrokではなく何かにデプロイしていつでも動くLINEBot
4.○ → ディスプレイにメッセージを出す
5.○ → 扉にとりつける


環境
Node.js v13.7.0
MacBook Pro macOS Mojave
Visual Studio Code 1.44.0


できたもの

マトリックスを壊してしまったのでobnizのディスプレイを使って
LINEBotからオンラインミーティングが始まったら
「開けないで」 と送ると、
大きめの × を表示させる
「終わったよ」と送ると表示が消える
(マトリックスの残骸付き)

扉への設置は、画鋲を打とうとしてマジで注意を受けたので透明のバックに入れて、ドアノブに引っ掛けるという荒技です。

コード

node.js
'use strict';

// obniz呼び出し
const Obniz = require('obniz');
var obniz = new Obniz("***");  // Obniz_ID に自分のIDを入れます


const express = require('express');
const line = require('@line/bot-sdk');
const axios = require('axios');
const PORT = process.env.PORT || 3000;

let matrix;
const { createCanvas } = require('canvas')
const canvas = createCanvas(128, 64);
const ctx = canvas.getContext('2d');

const config = {
  channelAccessToken: '***',
  channelSecret: '***'
};
const app = express();
app.post('/webhook', line.middleware(config), (req, res) => {
  console.log(req.body.events);
  Promise
    .all(req.body.events.map(handleEvent))
    .then((result) => res.json(result));
});
const client = new line.Client(config);
// obniz接続
obniz.onconnect = async function () {
  obniz.display.clear();
  obniz.display.print("obniz meets LINE Bot!");
  //matrix = obniz.wired("MatrixLED_HT16K33", { vcc: 1, gnd: 0, sda: 2, scl: 3 });
  //matrix.init(8);
  //matrix.brightness(7);

  ctx.fillStyle = "white";
  ctx.font = "60px sans-serif";
  ctx.strokeStyle = 'rgba(255,255,255,1)'

}

function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  let mes = event.message.text;
  if (event.message.text === '開けないで') {
    mes = '表示を出すよ'; //待ってねってメッセージだけ先に処理
    getAskObnizTemp(event.source.userId); //スクレイピング処理が終わったらプッシュメッセージ
  }
  else if (event.message.text === '終わったよ') {
    obniz.display.clear();

  } else {
    mes = event.message.text;
  }

  return client.replyMessage(event.replyToken, {
    type: 'text',
    text: mes
  });
}

const getAskObnizTemp = async (userId, mes) => {
  //let ctx = obniz.util.createCanvasContext(matrix.width, matrix.height);
  // ctx.fillStyle = "black";
  // ctx.fillRect(0, 0, matrix.width, matrix.height);
  // ctx.fillStyle = "white";
  // ctx.font = "9px sans-serif";
  // ctx.fillText('X', 0, 7);
  let linemes = encodeURIComponent(mes);
  ctx.strokeStyle = 'rgba(255,255,255,1)'
  ctx.beginPath()
  ctx.lineTo(0, 0)
  ctx.lineTo(127, 63)
  ctx.stroke()

  ctx.beginPath()
  ctx.lineTo(127, 0)
  ctx.lineTo(0, 63)
  ctx.stroke()
  //ctx.fillText('  ×', 0, 60);
  await obniz.display.draw(ctx);


  await client.pushMessage(userId, {
    type: 'text',
    text: "変えました",
  });
}

app.listen(PORT);
console.log(`Server running at ${PORT}`);

参考サイト

MatrixLED_HT16K33 | JS Parts Library | obniz
node.js+canvasでobnizのLCDに文字などを表示する: xshige's beta notes

感想

マトリックスが動いていた時も、このコードだとエラーだらけだったので、なんとも言えない気持ちで着地できずでした。
node-canvasにしてもエラーだったのできっと根本的なところが違うんだろうなと思いました。
がんばるっ

 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

在宅ワーク中家族に部屋に入ってこないでサインをobnizとLINEBotで作る

はじめに

こちらはProtoOutStudioというイケイケなスクールの「LINE Bot+IoTで表現してアウトプット」という課題で製作したものです。
obnizとLINEBotを組み合わせて何か作ろう!というものです。

概要と作れなかったところ

新型コロナウィルスの生で在宅になったものの、仕事中の家族との距離が難しい。
特にうちは猫が3匹もいるので、ニャンコちゃん達が騒ぎ始めたらどちらの部屋に引き取る?とか、
ご飯何時にする?
などなど、いちいちメッセージを送らずに部屋越しに会話することがよくあるのですが、実は「今オンラインミーティング中!!!!ノックしないでー、開けないでー」みたいなことがあって気まずい思いをしたりして、何かトイレの空き情報みたいなサイネージが欲しくて作りました。

obniz Starter Kit| obnizを買ったので、LEDマトリックス(MatrixLED_HT16K33)で◯×を出そうと思ったのですが、よくわからない文字しか出せず、挙げ句の果てに壊してしまいました。。。テスト画面ですら動かなくなってしまった。

作りたいもの

部屋にいながら部屋の外にある
1.LINEBotで操作する
2.マトリックスが「今は部屋を開けないで的なマークを出す」
3.何かにデプロイしていつでも動くLINEBot
4.ディスプレイにメッセージを出す
5.扉にとりつける

作れなかったところ

部屋にいながら部屋の外にある
1.○ → obnizをLINEBotで操作する
2.× → マトリックスが「今は部屋を開けないで的なマークを出す」
3.× → ngrokではなく何かにデプロイしていつでも動くLINEBot
4.○ → ディスプレイにメッセージを出す
5.○ → 扉にとりつける


環境
Node.js v13.7.0
MacBook Pro macOS Mojave
Visual Studio Code 1.44.0


できたもの

マトリックスを壊してしまったのでobnizのディスプレイを使って
LINEBotからオンラインミーティングが始まったら
「開けないで」 と送ると、
大きめの × を表示させる
「終わったよ」と送ると表示が消える
(マトリックスの残骸付き)

扉への設置は、画鋲を打とうとしてマジで注意を受けたので透明のバックに入れて、ドアノブに引っ掛けるという荒技です。

コード

node.js
'use strict';

// obniz呼び出し
const Obniz = require('obniz');
var obniz = new Obniz("***");  // Obniz_ID に自分のIDを入れます


const express = require('express');
const line = require('@line/bot-sdk');
const axios = require('axios');
const PORT = process.env.PORT || 3000;

let matrix;
const { createCanvas } = require('canvas')
const canvas = createCanvas(128, 64);
const ctx = canvas.getContext('2d');

const config = {
  channelAccessToken: '***',
  channelSecret: '***'
};
const app = express();
app.post('/webhook', line.middleware(config), (req, res) => {
  console.log(req.body.events);
  Promise
    .all(req.body.events.map(handleEvent))
    .then((result) => res.json(result));
});
const client = new line.Client(config);
// obniz接続
obniz.onconnect = async function () {
  obniz.display.clear();
  obniz.display.print("obniz meets LINE Bot!");
  //matrix = obniz.wired("MatrixLED_HT16K33", { vcc: 1, gnd: 0, sda: 2, scl: 3 });
  //matrix.init(8);
  //matrix.brightness(7);

  ctx.fillStyle = "white";
  ctx.font = "60px sans-serif";
  ctx.strokeStyle = 'rgba(255,255,255,1)'

}

function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  let mes = event.message.text;
  if (event.message.text === '開けないで') {
    mes = '表示を出すよ'; //待ってねってメッセージだけ先に処理
    getAskObnizTemp(event.source.userId); //スクレイピング処理が終わったらプッシュメッセージ
  }
  else if (event.message.text === '終わったよ') {
    obniz.display.clear();

  } else {
    mes = event.message.text;
  }

  return client.replyMessage(event.replyToken, {
    type: 'text',
    text: mes
  });
}

const getAskObnizTemp = async (userId, mes) => {
  //let ctx = obniz.util.createCanvasContext(matrix.width, matrix.height);
  // ctx.fillStyle = "black";
  // ctx.fillRect(0, 0, matrix.width, matrix.height);
  // ctx.fillStyle = "white";
  // ctx.font = "9px sans-serif";
  // ctx.fillText('X', 0, 7);
  let linemes = encodeURIComponent(mes);
  ctx.strokeStyle = 'rgba(255,255,255,1)'
  ctx.beginPath()
  ctx.lineTo(0, 0)
  ctx.lineTo(127, 63)
  ctx.stroke()

  ctx.beginPath()
  ctx.lineTo(127, 0)
  ctx.lineTo(0, 63)
  ctx.stroke()
  //ctx.fillText('  ×', 0, 60);
  await obniz.display.draw(ctx);


  await client.pushMessage(userId, {
    type: 'text',
    text: "変えました",
  });
}

app.listen(PORT);
console.log(`Server running at ${PORT}`);

参考サイト

MatrixLED_HT16K33 | JS Parts Library | obniz
node.js+canvasでobnizのLCDに文字などを表示する: xshige's beta notes

感想

マトリックスが動いていた時も、このコードだとエラーだらけだったので、なんとも言えない気持ちで着地できずでした。
node-canvasにしてもエラーだったのできっと根本的なところが違うんだろうなと思いました。
がんばるっ

 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

在宅ワーク中に会議中だよサインをobnizとLINEBotで作ってみた

はじめに

こちらはProtoOutStudiooの課題でobnizとLINEBotを組み合わせて何か作ろう!というものです。

概要

新型コロナウィルスのせいで在宅ワークになったため、仕事中の家族との距離が難しい。
特にうちは猫が3匹もいるので、ニャンコちゃん達が騒ぎ始めたらどちらの部屋に引き取る?とか、
ご飯何時にする?
などなど、いちいちメッセージを送らずに部屋越しに会話することがよくあるのですが、実は「今オンラインミーティング中!!!!ノックしないでー、開けないでー」みたいなことがあって気まずい思いをしたりして、何かトイレの空き情報みたいなサイネージが欲しくて作りました。

できたもの

部屋に入らないで欲しい時に入らないでマークをLINEから、obnizで表示させるサイネージ的なもの


作りたいもの

部屋にいながら部屋の外にある
1.LINEBotで操作する
2.マトリックスが「今は部屋を開けないで的なマークを出す」
3.何かにデプロイしていつでも動くLINEBot
4.ディスプレイにメッセージを出す
5.扉にとりつける

作れなかったところ

部屋にいながら部屋の外にある
1.○ → obnizをLINEBotで操作する
2.× → マトリックスが「今は部屋を開けないで的なマークを出す」
3.× → ngrokではなく何かにデプロイしていつでも動くLINEBot
4.○ → ディスプレイにメッセージを出す
5.○ → 扉にとりつける

obniz Starter Kit| obnizを買ったので、LEDマトリックス(MatrixLED_HT16K33)で◯×を出そうと思ったのですが、よくわからない文字しか出せず、挙げ句の果てに壊してしまいました。。。テスト画面ですら動かなくなってしまった。


環境
Node.js v13.7.0
MacBook Pro macOS Mojave
Visual Studio Code 1.44.0
使用部材
obniz


扉への設置は、画鋲を打とうとしてマジで注意を受けたので透明のバックに入れて、ドアノブに引っ掛けるという荒技です。

マトリックスを壊してしまったのでobnizのディスプレイを使って
LINEBotからオンラインミーティングが始まったら
「開けないで」 と送ると、
大きめの × を表示させる
「終わったよ」と送ると表示が消える
(マトリックスの残骸付き)

コード

node.js
'use strict';

// obniz呼び出し
const Obniz = require('obniz');
var obniz = new Obniz("***");  // Obniz_ID に自分のIDを入れます


const express = require('express');
const line = require('@line/bot-sdk');
const axios = require('axios');
const PORT = process.env.PORT || 3000;

let matrix;
const { createCanvas } = require('canvas')
const canvas = createCanvas(128, 64);
const ctx = canvas.getContext('2d');

const config = {
  channelAccessToken: '***',
  channelSecret: '***'
};
const app = express();
app.post('/webhook', line.middleware(config), (req, res) => {
  console.log(req.body.events);
  Promise
    .all(req.body.events.map(handleEvent))
    .then((result) => res.json(result));
});
const client = new line.Client(config);
// obniz接続
obniz.onconnect = async function () {
  obniz.display.clear();
  obniz.display.print("obniz meets LINE Bot!");
  //matrix = obniz.wired("MatrixLED_HT16K33", { vcc: 1, gnd: 0, sda: 2, scl: 3 });
  //matrix.init(8);
  //matrix.brightness(7);

  ctx.fillStyle = "white";
  ctx.font = "60px sans-serif";
  ctx.strokeStyle = 'rgba(255,255,255,1)'

}

function handleEvent(event) {
  if (event.type !== 'message' || event.message.type !== 'text') {
    return Promise.resolve(null);
  }

  let mes = event.message.text;
  if (event.message.text === '開けないで') {
    mes = '表示を出すよ'; //待ってねってメッセージだけ先に処理
    getAskObnizTemp(event.source.userId); //スクレイピング処理が終わったらプッシュメッセージ
  }
  else if (event.message.text === '終わったよ') {
    obniz.display.clear();

  } else {
    mes = event.message.text;
  }

  return client.replyMessage(event.replyToken, {
    type: 'text',
    text: mes
  });
}

const getAskObnizTemp = async (userId) => {
  //let ctx = obniz.util.createCanvasContext(matrix.width, matrix.height);
  // ctx.fillStyle = "black";
  // ctx.fillRect(0, 0, matrix.width, matrix.height);
  // ctx.fillStyle = "white";
  // ctx.font = "9px sans-serif";
  // ctx.fillText('X', 0, 7);
  ctx.strokeStyle = 'rgba(255,255,255,1)'
  ctx.beginPath()
  ctx.lineTo(0, 0)
  ctx.lineTo(127, 63)
  ctx.stroke()

  ctx.beginPath()
  ctx.lineTo(127, 0)
  ctx.lineTo(0, 63)
  ctx.stroke()
  //ctx.fillText('  ×', 0, 60);
  await obniz.display.draw(ctx);


  await client.pushMessage(userId, {
    type: 'text',
    text: "変えました",
  });
}

app.listen(PORT);
console.log(`Server running at ${PORT}`);

参考サイト

MatrixLED_HT16K33 | JS Parts Library | obniz
node.js+canvasでobnizのLCDに文字などを表示する: xshige's beta notes

感想

マトリックスが動いていた時も、このコードだとエラーだらけだったので、なんとも言えない気持ちで着地できずでした。
node-canvasにしてもエラーだったのできっと根本的なところが違うんだろうなと思いました。
がんばるっ

 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VSCode でF5でホーム画面を起動して JaveScript をステップ実行する

TypeScript 編は、
VSCode でF5でホーム画面を起動して TypeScript をステップ実行する を見てください。

ゴール

  • F5でホーム画面を起動 (CakePHP以外でもステップ実行までの手順は基本同じ?はずです) debugjs.gif

書かないこと

  • CakePHP のインストール手順

前提

  • CakePHP ビルトイン の簡易ウェブサーバ (port:8765) を起動していること
C:\src\cake-app\bin>cake server

Welcome to CakePHP v3.8.11 Console

Version

- version
VScode 1.43.2
Debugger for Chrome 4.12.6
Debugger for Firefox 2.7.1

準備

  1. デバッグ構成ファイルを作る

  2. (お好きな方の) 拡張をインストールしてデバッグ構成ファイルを編集

    ⅰ. Debugger for Chrome

    /.vscode/launch.json
    {
        "version": "0.2.0",
        "configurations": [
            {
                "type": "chrome",
                "request": "launch",
                "name": "Launch Chrome JavaScript",
                "url": "http://localhost:8765",
                "webRoot": "${workspaceFolder}/webroot"
            }
        ]
    }
    

    ⅱ. Debugger for Firefox

    /.vscode/launch.json
    {
        "version": "0.2.0",
        "configurations": [
            {
                "type": "firefox",
                "request": "launch",
                "name": "Launch Firefox JavaScript",
                "url": "http://localhost:8765",
                "webRoot": "${workspaceFolder}/webroot",
            }
        ]
    }
    
  3. ステップ実行対象を作成

    src/webroot/js/debug.js
    document.addEventListener("DOMContentLoaded", function() {
        console.log("on loaded");
    });
    
  4. ホーム画面 home.ctp でステップ実行対象を読み込む (<head></head> 内に追記)

--- a/src/Template/Pages/home.ctp
+++ b/src/Template/Pages/home.ctp
@@ -43,6 +43,8 @@ $cakeDescription = 'CakePHP: the rapid development PHP framework';
     <?= $this->Html->css('style.css') ?>
     <?= $this->Html->css('home.css') ?>
     <link href="https://fonts.googleapis.com/css?family=Raleway:500i|Roboto:300,400,700|Roboto+Mono" rel="stylesheet">
+    <?= $this->Html->script('debug.js') ?>
+    <!-- ↑同じ <script src="/js/debug.js"></script> -->
 </head>

ステップ実行

  1. F5でホーム画面を起動して「ブレークモード」になることを確認
    ※ firefoxの場合、ページのリロードが必要な場合あり Troubleshooting
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

100日後にエンジニアになるキミ - 20日目 - Javascript - JavaScriptの基礎3

今日もJavaScriptの続きをやっていきましょう。

前回はこちら

100日後にエンジニアになるキミ - 19日目 - Javascript - JavaScriptの基礎2

定数

昨日は「変数」についてやりました。

変数はその名の通り「変わる数」なので
中身を上書きして変更したりできました。

定数は変数と同様に宣言して、値を代入まではできますが
その後、中身を変更できないものになります。

書き方は
constを頭につけて宣言します。
const SAMPLE_CONST;

代入まで行うと
const SAMPLE_CONST = 'abc';

const MY_FAV = 7;
MY_FAV = 20;

Uncaught TypeError:

const MY_FAV = 20;

// ここから下は全てエラー
const MY_FAV = 20;
var MY_FAV = 20;
let MY_FAV = 20;

Uncaught SyntaxError:

定数は再度代入をしようとしたり
変数として同じ名前を使用しようとするとエラーが発生します。

データ型

プログラミングにおいて「データの型」というものはすごく重要なポイントです。

プログラムのコード上では必ずデータを取り扱います。

変数や定数の中身もデータですが
プログラムのデータには「型」というものが存在します。

例えば
数値としてコードを打ち込んだ場合
123
これは数値の型になります。

文字として打ち込んだ場合
"ABC"
これは文字列の型になります。

JavaScriptでは様々なデータの型がありますが
基本のデータ型(プリムティブ型)で扱えるデータ型は以下の通りです。

データ型 意味
Boolean 論理型 true , false の2つの値のみ
Null 値はnullのみ 型も値も無い型
Undefined 値を代入していないとこの型になる
Number 整数もしくは小数点の数値型 最大値はNumber.MAX_VALUE
String 文字列型 テキストデータを現す型
Symbol Symbol() で作成できる特殊な値の型
Object Objectとして定義された型

自分がどのようなデータの型を扱っているかを確認するには
typeof データ値と書くと、そのデータ型を知ることができます。

console.log(値)を用いて、コンソールに表示させてみましょう。

console.log(typeof true);
console.log(typeof null);
console.log(typeof undefined);
console.log(typeof 123);
console.log(typeof 'abc');
console.log(typeof Symbol());
console.log(typeof {});

boolean
object // なぜかnullはobject扱い
undefined
number
string
symbol
object

文字列型(string型)

データの形を見極めることはプログラミングでは重要です。
まず最初は文字列型から学んでいきましょう。

文字列型(string型)は文字として打ち込んだら文字列型になります。
文字列として打ち込むには文字を"'で囲みます。

"ABC"
"123"

変数に格納する際も同様に定義します。

var aaa = 'abc';
let bbb = '123';

文字列型では「足し算」のみ行うことができ、他のデータ型を足した際には
文字として連結されます。

console.log('a' + null);
console.log('a' + 123);

anull
a123

プログラム上で他のデータ型を文字列型として定義し直したい場合は
「String関数」を用います。

console.log(typeof String(null));
console.log(typeof String(123));

string
string

HTMLで画面に文字を反映させたりする際は
文字列として出力するので最も多用されるデータ型です。

数値型(Number型)

数値型は「整数」もしくは「小数点」の値を取り扱うデータ型です。
他のプログラム言語では整数型と小数点型で別れていたりします。

数字をそのまま用いると基本的には数値型になります。

var a = 123;
let n2 = 123.45;

プログラム上では「進数」を取り扱うことができるため
書き方で取り扱われる進数を定義することができ
何も指定しない場合は10進数になります。

他にどんな進数が扱えるかというと

進数 書き方(前に付ける文字) 10進数の値
2進数 0b , 0B 0b11 3
8進数 0o , 0O 0o70 56
16進数 0x , 0X 0xff 255
console.log(0b11);
console.log(0o70);
console.log(0xff);

3
56
255

文字列から数値型に「Number関数」で変換することができます。

Number("123") ;
Number("123.45") ;

関数というのはそのデータ型の中で使うことのできる機能のことで
文字列型や、数値型などの、データ型によって
使える関数が決まっています。

数値型の関数として
数値型から3ケタ区切りで文字列へ変換する「toLocaleString関数」
というものがあります。

var num = 12500.45;
console.log(num.toLocaleString());

12,500.45

このように数値を文字列に直したり、表記を変更したりするのは
関数を用いていきますので、データ型と関数の概念を押さえておきましょう。

演算子

算術演算子の項でも取り扱いましたが
演算子というのはたくさんの種類があり
その中でも四則計算を取り扱うのが「算術演算子」になります。

算術演算子

加算 +
減算 -
乗算 *
除算 /
剰余 %
べき乗 **
三項演算子 ?
インクリメント ++
デクリメント --

などがあります。

前回の補足として「三項演算子」は
条件によって結果が変わってくる値を定義できます。

条件 ? 真の値 : 偽の値

2==2 ? 'a' : 'b'

a

2==3 ? 'a' : 'b'

b

「インクリメント」と「デクリメント」は書き方によって挙動が変化します。

var xがあったとして

//前置記法
var x = 3;
y = ++x; // x = 4, y = 4
console.log(x+' : '+y)

// 後置記法
var x = 3;
y = x++; // x = 4, y = 3
console.log(x+' : '+y)

4 : 4
4 : 3

x が 3 の場合:
++x は x に 4 を設定して 4 を返す
x++ は 3 を返したあと x に 4 を設定する
--x は x に 2 を設定して2を返す
x-- は 3を返したあとに x に 2 を設定する

という結果になります。

「インクリメント」は左側の変数に対して1加算した値を設定します。
「デクリメント」は左側の変数に対して1減算した値を設定します。

代入演算子

代入を行うための演算子で , 左辺に右辺を代入します。

代入演算子 意味
= 左辺に右辺を代入
*= 左辺の値に右辺の値をかけたものを左辺に代入
/= 左辺の値に右辺の値を割ったものを左辺に代入
%= 左辺の値に右辺の値の剰余を左辺に代入
+= 左辺の値に右辺の値を足したものを左辺に代入
-= 左辺の値に右辺の値を引いたものを左辺に代入
<<= 左シフトした値を代入
>>= 右シフトした値を代入
>>>= 符号なしの右シフトした値を代入
&= ビット論理積(AND)の値を代入
^= ビット排他的論理和(XOR)の値を代入
|= ビット論理和(OR)の値を代入

中でもよく使うのは
+=などで、左辺の値に右辺の値を足したものを左辺に代入し直します。

var a = 3;
a += 2;
a;

5

変数aには3が代入されていますが、それに2を足した値で上書きされ
最終的な値は5になります。

他の代入演算子もどのような挙動になるかは
コードを書いて試してみましょう。

比較演算子

比較やその結果の結合、関係を現す演算子です。

これは代入をするのではなく
値同士がどうなのかを比較するための演算子です。

等値(比較)演算子 意味
== 等値の比較の結果を返す
!= 不等値の比較の結果を返す
=== 同値の比較の結果を返す
!== 非同値の比較の結果を返す
2==2

true

結果はBoolean型というデータ型となりtruefalseという値になります。
この場合、左右が等しいのでtrueになります。

2==3

false

この場合、左右は等しくないのでfalseになります。
比較した結果を返すものになるので、値が等しいか、等しくないかを判定できます。

関係演算子

関係演算子 意味
in 左辺の値を右辺が持っているかを判定した値を返す
instanceof objectが別のobjectのインスタンスかどうかを判別
< 左辺が右辺より小さいかどうかを判定した結果を返す
> 左辺が右辺より大きいかどうかを判定した結果を返す
<= 左辺が右辺以したかどうかを判定した結果を返す
>= 左辺が右辺以上かどうかを判定した結果を返す

関係演算子は右と左の値の関係性をみます。
例えば

2>=3;

false

この場合左側は右側よりも小さいので成り立ちません。
なので結果はfalseとなります。
成り立つ場合はtrueになります。

論理演算子

論理演算子は関係演算子などの結果を組み合わせた結果を返します。

論理演算子 意味
&& 論理積(AND) 左辺と右辺の結果を論理積を返す
|| 論理和(OR) 左辺と右辺の結果を論理和を返す

2つの結果があるとします。

その場合結果の組み合わせは

結果1 結果2
true true
true false
false true
false false

と4通りあります。

この組み合わせた結果を返すのが
論理演算子です。

結果としては

論理演算 結果
true && true true
true && false false
false && true false
false && false false
true || true true
true || false true
false || true true
false || false false

というようになります。

2==2 && 3==3

true

2==3 || 3==3

true

まとめ

プログラム上には様々なデータがあり
コードの書き方でデータの取り扱い方が変わります。

演算子でデータ同士の比較をしたり、代入をしたりするのが
多用されるので、どう書いたら
どうなるのか、を把握しておきましょう。

特に論理演算の部分は明日のところでも
重要なポイントとなります。

君がエンジニアになるまであと80日

作者の情報

乙pyのHP:
http://www.otupy.net/

Youtube:
https://www.youtube.com/channel/UCaT7xpeq8n1G_HcJKKSOXMw

Twitter:
https://twitter.com/otupython

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

どん兵衛を生け贄に青眼の白龍を召喚!

はじめに

React Native で ARKit を使ってみたかったのやってみました。
使用するライブラリは react-native-arkit です。

完成形は以下のようになります。

IMG_0232.jpg

では、実装していきます。

インストール

正直、ここが一番面倒でした。
公式が、 React Native >= 60.0 から追加された Autolink に対応していない…!

まだ取り込まれていない修正プルリクエストがあったので、そちらに従いました。

yarn add github:code-matt/react-native-arkit react-native-svg

Pod.file に下記の二行を追加します。

  pod 'RCTARKit', :path => "../node_modules/react-native-arkit/ios/RCTARKit.podspec"
  pod 'PocketSVG

これで pod install が動くはずです。

下準備

ARKit がどん兵衛を認識できるように、画像を取り込みます。
Asset Catalog のImages.xcassetsの中にAR Resource Groupを作成して、写真を設定します。

adding-arresources.png

次に 3D オブジェクトを用意します。
最後に紹介する参考ページで紹介されていたサイトから青眼の白龍のオブジェクトをダウンロードして、.scnファイルを作成します。

XCode でSceneKit Catalogを作成し、名前をart.scnassetsとしました。
作成された.scnファイルをその中に移動させます。

私の環境では、これだけだと色のない真っ白なオブジェクトになりました。
PNG ファイルを見て色を当てているようだったので、一緒に入っていた PNG ファイルも格納したところ、ちゃんと色ありで表示されるようになりました。

実装

const ARKitContainer: React.FunctionComponent = () => {
  const [position, setPosition] = useState(null);
  return (
    <ARKit
      style={{ flex: 1 }}
      debug
      // enable plane detection (defaults to Horizontal)
      planeDetection={ARKit.ARPlaneDetection.Horizontal}
      // enable light estimation (defaults to true)
      lightEstimationEnabled
      onAnchorDetected={(anchor) => {
        setPosition(anchor?.position);
      }}
      onAnchorUpdated={(anchor) => {
        setPosition(anchor?.position);
      }}
      onAnchorRemoved={(anchor) => {
        setPosition(null);
      }}
      // you can detect images and will get an anchor for these images
      detectionImages={[{ resourceGroupName: 'AR Resources' }]}
    >
      {position && (
        <ARKit.Model
          position={{ ...position, z: position.z * 1.25, frame: 'local' }}
          scale={0.01}
          model={{
            scale: 1,
            file: 'art.scnassets/BlueEyes.scn',
          }}
        />
      )}
    </ARKit>
  );
};

AR Resources のリソースグループを検知した時に、その座標をpositionに設定しています。
positionが存在する場合、その位置にart.scnassets/BlueEyes.scnを表示しています。

ARKit の検出精度が良いので、特に変わったことをしなくても、どん兵衛を見つけると青眼の白龍を召喚できるようになりました。

おわりに

いかがでしたでしょうか。
Swift x ARKit の例はたくさん出てきたのですが、React Native で AR をやっていた記事はあまり見つけられませんでした。
ライブラリがアップデート(Autolink対応)されておらず、インストールでつまづいたのが一番難点でした。

3D モデルさえ手に入れれば色々と可能性を感じたので、もうちょっと ARKit で遊んでみたいと思います。

参考

休日2日を生け贄に青眼の白龍を召喚!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptを学ぼう!

JavaScriptを学ぼう!

【目次】

  • JavaScriptとは?
  • JavaScriptで計算しよう
  • JavaScriptで論理を扱う
  • ループで繰り返し処理
  • 配列を利用しよう
  • 関数を作ろう
  • オブジェクトを活用しよう

①JavaScriptとは?

JavaScriptとは、webページの情報に変更を加えられるプログラミング言語です。JSと略されて呼ばれています。(以下、基本JSと書きます) 

例えば、以下のようにJSをHTMLの<body>タグ内に記述すると、
“1+2”の計算式の説明と計算結果をHTMLに表示することが出来ます。

<script> //・・・(1)
  document.write('1足す2は')//・・・(2)
  document.write(1+2)//・・・(3)
  document.write('です')//・・・(4)
</script>
  1. <script>とは、HTML内でJSを書く際に必要なタグです。
    他のHTMLのタグと同様、最後には( / )(バックスラッシュ)を使って</script>と記述します。

  2. document.write( )とは、webページ上に情報を書き出す際に用いる関数です。
    ( )の中に書き出したい内容を記述しますが、注意点が1つあります。
    文字列を書く場合は( ' )(シングルクォテーション)で文章を囲む必要があります。(数字を書く場合は必要ありません)
    文字列とは、要するに文章のことだと思っていただいてかまいません。
    今回は、「1足す2は」と出力されます。

  3. JSでは、( )の中に計算式を入力した場合、( )内の計算が実行されます。
    (計算に使う記号については、「2.JavaScrptで計算しよう」で解説します)
    ここでは、(1+2)の計算を処理し、計算結果を出力します。

  4. 「です」という文字列を出力します。

上記のコードをHTMLのbodyタグ内に記述することで
webページ上には以下のように出力されます。

JSって何?の例.png

また、HTMLファイルが格納されているフォルダ内にJSのファイルを作成し、HTMLファイルに呼び出すことが出来ます。こうすることで、HTMLファイルには文章の構造、JSファイル内には処理と、それぞれの内容を読みやすくなります。
下記は、HTMLに「test.js」という名前のJSのファイルを呼び出す方法です。
(※JSのファイルを作成する場合は、末尾に「.js」をつけます)

<script src="test.js"></script>

ファイルを呼び出す場合は、<script src = " " >となります。
( " " )(ダブルクォテーション)内に呼び出したいファイルのファイル名を記述します。

②JavaScriptで計算しよう

JSでは、様々な計算を処理することができます。
ですので、下記のような数学の問題を解かせることが出来ます。

円の半径を r 、円の面積を Ar とする。
r = 3.88の時の Ar を求めよ。

②では、JSで計算するのに必要な3つの道具について紹介します。
3つの道具とは以下の3つです。

  • 算術演算子(数学記号)
  • 変数

それぞれ見ていきましょう。



とは、プログラムにおいて処理の対象となる単一のデータのことです。
値には以下の3種類があります。

〇数値

10, -45, 2.55などの数字

〇文字列

「こんにちは。」などの文章。( ' )で囲む必要がある。
文字列に記号を記述する場合は、記号の前に( / )を入れなければ、記号として認識されてしまうため注意。

〇真偽値

true / false : 「真」と「偽」を表す。
日本語の「はい」「いいえ」の選択や、「オン」「オフ」の設定の値として使える。

算術演算子(数学記号)

算術演算子とは、( + )や( - )など計算に必要な記号のことです。
JSでは以下の演算子を用いて計算を行います。

計算   演算子
足し算 +
引き算 -
掛け算(×) *
割り算(÷) /
割り算のあまり %

変数

変数とは、値を入れておく箱のようなものです。固有の名前をつけられます。変数を設定することを「宣言」といいます。宣言の仕方は以下の通りです。

var [変数名] = [値]

変数名は好きに決めてもらってかまいませんが、
英語で書くほうが好ましいです。
例えば、年齢を宣言する場合は 

var age = 22

となり、好きな食べ物を宣言する場合は

var favoriteFood = 'そうめん'

となります。


③JavaScriptで論理を扱う

論理とは、言葉のあいまいさを無くす道具です。あいまいな論理をプログラムは処理できないので、プログラムを書く上ではあいまいさを無くす必要があります。
下の文章を見てください。

「そうめんをご注文の方は、ねぎまたは大根おろしがつけられます。」

この場合、そうめんを注文したら、ねぎと大根おろしの両方をつけることはできるのでしょうか?はたまた、両方はつけられないのでしょうか?つけられないというのなら、なぜそう言い切れるのでしょうか?一緒に食べたほうがおいしいですよね?

日常生活であれば質問すれば解決する問題ですが、プログラムとなると、開発者に逐一質問するわけにもいきません。
そのため、比較演算子を用いてものごとの真偽( true / false )をはっきりさせる必要があります。

比較演算子とは、値と値を比較して真偽値にできる演算子のことです。比較演算子の使い方は以下のようになります。

表現 入力例 結果
1は2以下 1 <= 2 true
1は2以上 1 >= 2 false
1は2より小さい 1 < 2 true
1は2より大きい 1 > 2 false
1は2と等しい 1 === 2 false
1は2と異なる 1 ! == 2 true

比較演算子には、以下のように変数も用いることができます。

var x = 1
var y = 2

x > y false
x ! == y true

また、場合分けをしたい時は if文を使います。

if文とは、「もし~だったら」と条件を指定し、その条件の真偽で処理を実行するかどうか、また処理をするかしないかを決める仕組みです。
if文の書き方は以下のようになります。

if ( a ) {
  //論理式aの値が真の場合実行する処理
} else if ( b ) {
  //論理式bの値が真の場合実行する処理
} else {
  //論理式の値が偽の場合実行する処理
}

else, else if ( )はなくても問題ありません。複数の条件で場合分けをしたい場合などに使用してください。
 
上記のように、if文では( )内に論理式を代入します。
最後に、論理式を利用する場合に役に立つ演算子を紹介します。

表現 名称 演算子 表現
Aではない 否定 ! ! A
AまたはB 論理和 | | A | | B
AかつB 論理積 && A && B

④ループで繰り返し処理

ループとは、プログラムにおいて繰り返しを行う処理のことです。
for文というものを使います。

 例えば、100回繰り返すfor文の書き方は以下のようになります。

for ( var i = 0; i < 100`; i++ ){
 //実行する処理
}

赤文字初期化式青文字条件式緑文字変化式といいます。

初期化式

ループ開始時1番初めに行う。
ここでは、変数 i を宣言し、0を代入。

条件式

繰り返しを行うかどうかの条件を決める。
ここでは、変数 i が100未満の場合繰り返しを行う。

変化式

次のループに入る前にする処理。
ここでは、変数 i を1増やす。
※ i ++とは、i = i + 1 を省略したもの。

for文では、下記の順番で処理を行います。
  1. 初期化式の計算
  2. 条件式の計算。ループするかしないかの決定
  3. { }内のプログラムの処理(上記の例の「実行する処理」
  4. 変化式の計算 ・ ・ ・

以後、2~4を繰り返し、条件式の条件に適さなくなった時( ex. 条件式が i < 5 の時に、 i = 5となった場合など)ループをやめる。

 
1から100000までの数字を書き出す場合、以下のようなfor文にすることで実行することが出来ます。

for (var i = 0; i <= 100000; i++){
    document.write(i + ' ')
}

⑤配列を利用しよう

プログラミングには、コレクションという値などの要素を集めたものがあります。JSにおける基本的なコレクションに配列があります。

配列では、複数の並んだ要素に対し、「添字」という整数の番号を使い中身を取り出すことが出来ます。
配列は以下のように記述します。

var materialsOfSomen = ['そうめん', 'めんつゆ', '水', 'ねぎ']

consoleで materialsOfSomen[ ]の[ ]内に整数を入力することで配列内の要素(ここではそうめんの材料)が取り出せますが、注意点としては、配列において始まりの数字は[0]になります。

よって、consoleで

materialsOfSomen[1]

と入力すると、

" めんつゆ "

という結果がでてきます。

この他にも、consoleでは下記のようなことが可能です。

・配列に含まれている要素を調べる
console.log( materialsOfSomen )

・配列に要素がいくつあるかを調べる
console.log( materialsOfSomen.length )

・配列に要素を追加する
materialsOfSomen.push( 'ごま’ )

⑥関数を作ろう

関数とは、ひとかたまりの処理に名前をつけて再利用できるようにしたものです。関数を作るにはfunctionを利用します。
関数の書き方は以下の通りです。

function A() {
  //実行する処理
}

Aには関数の名前を入れます。実行する処理は複数あっても大丈夫です。

関数を使ってそうめんのレシピを作ると、以下のようになります。

function howToMakeSomen() {
    そうめんをゆでる;
    ねぎを切る;
    器にめんつゆをいれる;
    めんつゆを水で薄める;
    そうめんを氷で冷やす;
    器にねぎを入れる;
    冷えたそうめんを器に入れる;
}

関数を作ることで、手順を実行する処理を1つ1つ入力しなくても、実行したい処理のセットをいつでも再利用することができます。
(いつでもそうめんが食べられますね!)

また、関数の( )内には、引数という処理に利用する値を代入することができます。
ですので、半径rの円の面積Arを求める関数を、以下のように作成することが出来ます。

function Ar (r){
    return r*r*3.14
}

これにより、consoleで

console.log( Ar( 3) )

と入力すると、半径3の円の面積導くことが出来ます。

⑦オブジェクトを活用しよう

オブジェクトとは、JSにおける値の1つで、プロパティという、名前と値のセットを複数持つことが出来ます。
例えば、今年会社に入社したこうた君の自己紹介をオブジェクトで宣言すると、以下のようになります。

var Kota = {
  name : "Kota",
  age : 22,
  favoriteFood : "Somen"
}

このようにオブジェクトを宣言しておくと、後々こうた君の好きな食べ物が知りたくなった時に、

Kota.favoriteFood

と入力すると、こうた君の好きな食べ物を呼び出すことが出来ます。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Express Validatorを使ってみる(API変更後)

はじめに

完全自粛中なのでNode.js超入門第2版でnode.jsの勉強をしていました。
express-validatorがAPI変更に伴って、参考書通りのソースコードだとバリデーションが実行されなかったので、変更点も交えながら記事投稿します。

環境

・node 12.7.0
・express 4.16.1
・express-validator 5.3.1

Node.js超入門第2版 P334~335

app.js
var validator = require('express-validator');
app.use(validator());
hello.js
router.get('/add',(req,res,next) => {
  var data = {
    title: 'Hello/Add',
    content: '新しいレコードを入力:',
    form: {
      name: '',
      mail: '',
      age: 0
    }
  }
  res.render('hello/add',data);
});

router.post('/add',(req,res,next) => {
  req.check('name','名前は必ずご入力してください。').notEmpty();
  req.check('mail','メールアドレスをご入力してください。').isEmail();
  req.check('age','年齢は整数をご入力してください。').isInt();

  req.getValidationResult().then((result) => {
    if(!result.isEmpty()) {
    var re = '<ul class="error">';
    var result_arr = result.array();
    for(var n in result_arr){
      re += '<li>' + result_arr[n].msg + '</li>';
    }
    re += '</ul>';
    var data = {
      title: 'Hello/add',
      content: re,
      form: req.body
    }
    res.render('hello/add',data);
    }
    else{
      var nm = req.body.name;
      var ml = req.body.mail;
      var ag = req.body.age;
      var data  = {
        'name': nm,
        'mail': ml,
        'age': ag
      };
      var connection = mysql.createConnection(mysql_setting);

      connection.connect();

      connection.query('insert into mydata set ?',data,
        function(error,results,fields){
          res.redirect('/hello');
        });
      connection.end();
    }
  });
});

API変更後

hello.js
// Express-validatorを読み込む
const { check, validationResult } = require('express-validator/check');

router.get('/add',(req,res,next) => {
  var data = {
    title: 'Hello/Add',
    content: '新しいレコードを入力:',
    form: {
      name: '',
      mail: '',
      age: 0
    }
  }
  res.render('hello/add',data);
});

router.post('/add', [
  check('name','名前は必ずご入力してください。').not().isEmpty(),
  check('mail','メールアドレスをご入力してください。').isEmail(),
  check('age','年齢は整数をご入力してください。').isInt()
],(req,res,next) => {
// getValidationResult()ではなく、validationResult(req)でバリデーションの実行結果を受け取る。
  var result = validationResult(req);
// チェック項目があった場合
  if(!result.isEmpty()) {
    var re = '<ul class="error">';
    var result_arr = result.array();
    for(var n in result_arr){
      re += '<li>' + result_arr[n].msg + '</li>';
    }
    re += '</ul>';
    var data = {
      title: 'Hello/add',
      content: re,
      form: req.body
    }
    res.render('hello/add',data);
  }
// チェックを通過した場合
  else{
    var nm = req.body.name;
    var ml = req.body.mail;
    var ag = req.body.age;
    var data  = {
      'name': nm,
      'mail': ml,
      'age': ag
    };
    var connection = mysql.createConnection(mysql_setting);

    connection.connect();

    connection.query('insert into datamy set ?',data,function(error,results,fields){
      res.redirect('/hello');
    });
    connection.end();
  }
});

モジュールのロードの仕方とValidatorの追加の仕方

app.use(validator());が使えなくなっているため。

req.check()ではなくcheck()でExpressValidator.Validatorのオブジェクトを返す

app.post('/add',[],(req,res,next)=>{});

第1引数にPOST処理後のルーティング処理、第2引数にチェック項目を配列で、第3引数にコールバック関数を入れる

参考

https://express-validator.github.io/docs/index.html
https://www.shuwasystem.co.jp/book/9784798055220.html

おわりに

参考書で勉強していて困っていた人の助けになれれば光栄です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScriptでクラスのGetterを含めたオブジェクトの値を列挙する

マルチウィンドウ間でデータのやり取りをする際に、クラスに定義されたgetterの値(返り値)も含めてやり取りしたかったので、クラスに定義されたgetterを含めたオブジェクトの値を列挙する方法を調べた

基本的には以下を参考にした
https://qiita.com/yu0819ki/items/2bc8ba7e46d6ded63f4e

実装は以下のような感じ、
今回は、対象のクラスが他のクラスを継承している場合にそのクラスのgetterも含めて出力したかったので、ちょっと改造している

const listupGetter = (obj) => {
    if (obj.constructor.name === 'Object') return [];
    let parentKeys = [];
    // 親クラスを持っている場合には、そのクラスのgetterのキーも取得する
    if (Object.getPrototypeOf(obj).constructor.name !== 'Object') {
      parentKeys =listupGetter(Object.getPrototypeOf(obj));
    }
    // プロトタイプ(クラス)取得
    const proto = Object.getPrototypeOf(obj);

    // Object.getOwnPropertyNames: プロトタイプ(クラス)で定義されているプロパティを全部取得
    const ownKeys = Object.getOwnPropertyNames(proto).filter((v) => {
      // そのうち、デスクリプタとして get があるものだけフィルタリング
      const descriptor = Object.getOwnPropertyDescriptor(proto, v);
      return descriptor.get instanceof Function && v !== '__proto__';
    });
    return [...parentKeys, ...ownKeys];
  }

  /**
   * クラスのGetterを含めた、オブジェクトのすべての値のキーの一覧を取得する
   * functionは無視される
   * @param obj 対象のobject
   * @return {(string)[]} オブジェクトのすべての値のキーの一覧
   */
const getPropertyList = (obj) => {
  return [...Object.keys(obj), ...listupGetter(obj)];
}

// 以下サンプルコード

class Polygon {
  name = "Polygon";
  get test (){
    return 1
  }
}

class Poly2 extends Polygon {
  get test2(){
    return 5
  }
}

const poly1 = new Polygon();
const poly2 = new Poly2();

const obj = {
  get test(){
    return aaa
  },
  ccc: 1
}


console.log(getPropertyList(obj));   // => Array ["test", "ccc"]
console.log(getPropertyList(poly1)); // => Array ["name", "test"]
console.log(getPropertyList(poly2)); // => Array ["name", "test", "test2"]

以上

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaScript で正規表現を使って HTML の属性を取得する。

parser 等を使わずにサクッと取得したいと思いました。

// attribute(対象となるDOMの文字列, 要素名)
function attribute(str, key) {
  const regExp = new RegExp(`${key}="([^"]*)"`)
  return str.match(regExp)[1]
}

attribute('<p id="hello" style="color: red;">Hello, world</p>', 'style')
> attribute('<p id="hello" style="color: red;">Hello, world</p>', 'style')
'color: red;'
> 

[^"]* ... " を含まない任意の文字列

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

IE11では、チェックボックスのindeterminateプロパティが変化したときにonchangeイベントは発動しない

JavaScriptを使うと、チェックボックスの中間状態を表すのに、indeteminateプロパティを使えます。
以下のHTMLをブラウザで表示すると、チェックボックスとボタンが表示されます。ボタンを押すと、チェックボックスが中間状態になります。

<meta charset="utf-8">
<input type="checkbox" id="test" onchange="showAlert()">
<input type="button" onclick="toIndeterminate()" value="チェックを中間状態にする">
<script>
  let checkbox = document.getElementById("test");

  let showAlert = function(){window.alert("alert")};
  let toIndeterminate = function(){checkbox.indeterminate = true};
</script>

ところがどうやらIEでは、チェックを中間状態にしてから、チェックボックスをクリックしても、onchangeイベントは発生しないようです。

仕方ないので、onclickにすることで対応しました。

<meta charset="utf-8">
<input type="checkbox" id="test" onclick="showAlert()">
<input type="button" onclick="toIndeterminate()" value="チェックを中間状態にする">
<script>
  let checkbox = document.getElementById("test");

  let showAlert = function(){window.alert("alert")};
  let toIndeterminate = function(){checkbox.indeterminate = true};
</script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】Ajaxを用いた非同期フォロー機能の実装

目標

ezgif.com-video-to-gif.gif

開発環境

・Ruby: 2.5.7
・Rails: 5.2.4
・Vagrant: 2.2.7
・VirtualBox: 6.1
・OS: macOS Catalina

前提

ログイン機能を実装済み。

ログイン機能 ➡︎ https://qiita.com/matsubishi5/items/5bd8fdd45af955cf137d

フォロー機能を実装

1.モデル

ターミナル
$ rails g model Relationship follower_id:integer followed_id:integer
ターミナル
$ rails db:migrate
schema.rb
ActiveRecord::Schema.define(version: 2020_04_05_115005) do
    create_table "relationships", force: :cascade do |t|
        t.integer "follower_id"
        t.integer "followed_id"
        t.datetime "created_at", null: false
        t.datetime "updated_at", null: false
    end  

    create_table "users", force: :cascade do |t|
        t.string "email", default: "", null: false
        t.string "encrypted_password", default: "", null: false
        t.string "reset_password_token"
        t.datetime "reset_password_sent_at"
        t.datetime "remember_created_at"
        t.integer "sign_in_count", default: 0, null: false
        t.datetime "current_sign_in_at"
        t.datetime "last_sign_in_at"
        t.string "current_sign_in_ip"
        t.string "last_sign_in_ip"
        t.string "name"
        t.datetime "created_at", null: false
        t.datetime "updated_at", null: false

        t.index ["email"], name: "index_users_on_email", unique: true
        t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
    end
end
user.rb
class User < ApplicationRecord
    devise :database_authenticatable, :registerable,
           :recoverable, :rememberable, :trackable,:validatable

    has_many :books, dependent: :destroy
    has_many :follower, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy
    has_many :followed, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy
    has_many :following_user, through: :follower, source: :followed
    has_many :follower_user, through: :followed, source: :follower

#ユーザーをフォローする
    def follow(user_id)
        follower.create(followed_id: user_id)
    end

#ユーザーをアンフォローする
    def unfollow(user_id)
        follower.find_by(followed_id: user_id).destroy
    end

#フォローしているかを確認する
    def following?(user)
        following_user.include?(user)
    end
end

:one: has_many :follower, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy
➡︎ Relationshipモデルのfollower_idにuser_idを格納

:two: has_many :followed, class_name: "Relationship", foreign_key: "followed_id", dependent: :destroy
➡︎ Relationshipモデルのfollower_idにuser_idを格納

:three: has_many :following_user, through: :follower, source: :followed
➡︎ 自分がフォローしているユーザー

:four: has_many :follower_user, through: :followed, source: :follower
➡︎ 自分をフォローしているユーザー

relationship.rb
class Relationship < ApplicationRecord
    belongs_to :follower, class_name: "User"
    belongs_to :followed, class_name: "User"
end

2.コントローラー

ターミナル
$ rails g controller relationships
relationships_controller.rb
class RelationshipsController < ApplicationController
    def follow
        current_user.follow(params[:id])
        redirect_to root_path
    end

    def unfollow
        current_user.unfollow(params[:id])
        redirect_to root_path
    end
end
users_controller.rb
class UsersController < ApplicationController
    def index
        @users = User.all
    end

#自分がフォローしているユーザー一覧
    def following
        @user = User.find(params[:user_id])
    end

#自分をフォローしているユーザー一覧
    def follower
        @user = User.find(params[:user_id])
    end
end

3.ルーティング

routes.rb
Rails.application.routes.draw do
    root 'homes#top'
    devise_for :users
    resources :users
    post 'follow/:id' => 'relationships#follow', as: 'follow'
    post 'unfollow/:id' => 'relationships#unfollow', as: 'unfollow'
    get 'users/following/:user_id' => 'users#following', as:'users_following'
    get 'users/follower/:user_id' => 'users#follower', as:'users_follower'
end

4.ビュー

users/index.html.erb
<table class="table">
    <thead>
        <tr>
            <th>Image</th>
            <th>Name</th>
            <th></th>
            <th></th>
            <th></th>
            <th></th>
        </tr>
    </thead>

    <tbody>
        <% @users.each do |user| %>
            <tr>
                <td><%= attachment_image_tag(user, :profile_image, :fill, 50, 50, fallback: "no-image-mini.jpg") %></td>
                <td><%= user.name%></td>
                <td><%= link_to "Show", user %></td>
                <td><%= link_to "フォロー数:#{user.follower.count}", users_following_path(user) %></td>
                <td><%= link_to "フォロワー数:#{user.followed.count}", users_follower_path(user) %></td>
                <td>
                    <% unless user == current_user %>
                        <% if current_user.following?(user) %>
                            <%= link_to 'フォロー解除', unfollow_path(user.id), method: :POST,class:"btn btn-info", remote: true %>
                        <% else %>
                            <%= link_to 'フォローする', follow_path(user.id), method: :POST,class:"btn btn-default" %>
                        <% end %>
                    <% end %>
                </td>
            </tr>
        <% end %>
    </tbody>
</table>
users/following.html.erb
<h3>フォロー中</h3>
<table class="table">
    <thead>
        <tr>
            <th>Image</th>
            <th>Name</th>
            <th>Introduction</th>
            <th></th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        <% @user.following_user.each do |user| %>
            <tr>
                <td><%= attachment_image_tag user, :profile_image, :size =>'30x30', class: "img-circle pull-left profile-thumb", fallback: "no_image.jpg" %></td>
                <td><%= user.name %></td>
                <td><%= user.introduction %></td>
                <td><%= link_to "Show", user %></td>
                <td>
                    <% if current_user.following?(user) %>
                        <% unless user == current_user %>
                            <%= link_to 'フォロー解除', unfollow_path(user.id), method: :POST,class:"btn btn-info", remote: true %>
                        <% else %>
                            <%= link_to 'フォローする', follow_path(user.id), method: :POST,class:"btn btn-default" %>
                        <% end %>
                    <% end %>
                </td>
            </tr>
        <% end %>
    </tbody>
</table>
users/follower.html.erb
<table class="table">
    <thead>
        <tr>
            <th>Image</th>
            <th>Name</th>
            <th>Introduction</th>
            <th></th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        <% @user.follower_user.each do |user| %>
            <tr>
                <td><%= attachment_image_tag user, :profile_image, :size =>'30x30', class: "img-circle pull-left profile-thumb", fallback: "no_image.jpg" %></td>
                <td><%= user.name %></td>
                <td><%= user.introduction %></td>
                <td><%= link_to "Show", user %></td>
                <td>
                    <% unless user == current_user %>
                        <% if current_user.following?(user) %>
                            <%= link_to 'フォロー解除', unfollow_path(user.id), method: :POST,class:"btn btn-info", remote: true %>
                        <% else %>
                            <%= link_to 'フォローする', follow_path(user.id), method: :POST,class:"btn btn-default" %>
                        <% end %>
                    <% end %>
                </td>
            </tr>
        <% end %>
    </tbody>
</table>

非同期機能を実装

1.jQueryを導入

Gemfile
    gem 'jquery-rails'
ターミナル
    $ bundle
application.js
    //= require rails-ujs
    //= require activestorage
    //= require turbolinks
    //= require jquery
    //= require_tree .

2.各ビューのフォローボタンを編集

users/~.html.erb
<td class="followbutton_<%= user.id %>"> <!-- 各ボタンにidをつける -->
    <% unless user == current_user %>
        <%= render "followbutton", user: user %> <!-- ボタン部分をパーシャル化 -->
    <% end %>
</td>
users/_followbutton.html.erb
<!-- link_toの引数に「remote: true」を追記 -->
<% if current_user.following?(user) %>
        <%= link_to 'フォロー解除', unfollow_path(user.id), method: :POST,class:"btn btn-info", remote: true %>
<% else %>
        <%= link_to 'フォローする', follow_path(user.id), method: :POST,class:"btn btn-default", remote: true %>
<% end %>

2.JavaScriptファイルを作成

users/follow.js.erb
$("#followbutton_<%= @user.id %>").html("<%= j(render 'users/followbutton', user: @user) %>");
users/unfollow.js.erb
$("#followbutton_<%= @user.id %>").html("<%= j(render 'users/followbutton', user: @user) %>");

$("#followbutton_<%= @user.id %>")
➡︎ 「2」で付けたクラスを指定

.html("<%= j(render 'users/followbutton', user: @user) %>");
➡︎ フォローボタンのパーシャルをrenderしている

参考サイト

https://freecamp.life/rails-follow-follower/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js の Composition API における親子コンポーネント間のデータ受け渡し

皆様いかがお過ごしでしょうか。
いろいろと大変な時期ですが頑張っていきましょう。

Vue 3.0 のリリースを目前に控え、今後メインの記法となっていくであろう Composition API における親子コンポーネント間のデータのやりとりにフォーカスについてまとめてみました。
※ 2020 年 4 月現在、 vue@2.6.11 + @vue/composition-api@0.5.0 での挙動を元に書いています。

デモサイトを用意してあります。記事の該当する部分と交互に見ていただくと理解しやすいかもしれません。
https://vue-props-samples.netlify.app/
※ 記事内のコードは、装飾用のクラスなどを省略しています。
※ ブラウザ拡張機能 Vue.js devtools を使うと、コンポーネント内の様子などを確認できるようにビルドしてあります。
※ ソースはこちら。 https://github.com/jay-es/vue-props-samples

TL;DR

従来の記法 Options API に精通している方向けのまとめ。
親のテンプレートと子の props オプションは今までと同じ。子の setup 関数のひとつめの引数にも props が入っている(ただし分割代入は NG)

では、順を追って詳しく解説していきます。

1. props: 親から子にデータを渡す

まずは一番基本の、子コンポーネントへのデータの渡し方を説明していきます。

親コンポーネントの書き方

親コンポーネントのテンプレート内で、子コンポーネントのカスタム属性に変数を渡します。
:title="foo" は子コンポーネントの title というプロパティに foo 変数の中身(下の例の場合は abc)を渡す、という意味です。
v-bind:title="foo" とも書けますが、本記事では省略記法を使用していきます。
※ テンプレートの書き方は従来(Options API)と変わりません。
※ 親の変数名と子のプロパティ名が同じだと分かりづらいため、あえて別々にしています。

親コンポーネント
<template>
  <div>
    <Child :title="foo" :count="bar" />
  </div>
</template>

<script>
import { ref } from '@vue/composition-api'
import Child from './child.vue'

export default {
  components: { Child },
  setup () {
    const foo = ref('abc')
    const bar = ref(123)

    return { foo, bar }
  }
}
</script>

子コンポーネントで親からのデータを受け取る方法

一番シンプルなのは、コンポーネントオプションの props にプロパティ名の配列を指定する方法です。

子コンポーネント
<template>
  <div>
    title: {{ title }}<br />
    count: {{ count }}<br />
  </div>
</template>

<script>
export default {
  props: ['title', 'count'] // プロパティ名の配列
}
</script>

この方法は簡単ではあるものの、他の開発者(や半年後の自分)が見たときにどのような値が渡ってくるのか分かりづらいため、避けたほうがよいです。

かわりに、オブジェクト形式にして「キーにプロパティ名、値に変数の型(コンストラクタ)」のように指定したり、

export default {
  props: {
    title: String,  // プロパティ名: 型
    count: Number
  }
}

もう 1 階層ネストしたオブジェクトにして type で型を指定し、required(必須かどうかの真偽値)や、default(省略された場合の値)などを指定するとよいでしょう。

export default {
  props: {
    title: {         // プロパティ名
      type: String,  // 型
      required: true // 必須かどうか
    },
    count: {
      type: Number,
      default: 0     // 親から値が渡されなければ 0 になる
    }
  }
}

※ デフォルト値を関数で生成したり、値のバリデーション関数を定義することもできます。詳細は Vue 公式サイト: プロパティのバリデーション を参照してください。

setup 関数で props を使う

Composition API の setup 関数では、ひとつめの引数で props を取得できます。
親コンポーネントで値が変わったら props にも反映される、というリアクティブな性質をもつオブジェクトです。関数内で使用する場合は computedwatch で監視する必要があります。

子コンポーネント
import { computed } from '@vue/composition-api'

export default {
  props: {
    title: {
      type: String,
      required: true
    },
    count: {
      type: Number,
      default: 0
    }
  },
  setup (props) {
    const doubleCount = computed(() => props.count * 2)

    return { doubleCount }
  }
}

ただし、引数を分割代入で取得してしまうと、リアクティブではなくなってしまうので注意が必要です。
(自分はたまに忘れてやってしまいます。最初はちゃんと表示されるので気づきにくいんですよね……)

propsを分割代入
  setup ({ count }) {
    // 親コンポーネントで値が変わっても、子コンポーネントは初期値のまま変わらない
    const doubleCount = computed(() => count * 2)

TypeScript で型の情報をつける

TypeScript の場合、props にプリミティブな型(String など)を指定した場合はちゃんと型がつきますが、ArrayObject を指定した場合は中身の情報がないので補完されません。

子コンポーネント
import { defineComponent } from '@vue/composition-api'

export default defineComponent({
  props: {
    names: {
      type: Array, // 本当は文字列の配列
      required: true
    },
    staff: {
      type: Object, // 本当はユーザー定義型
      required: true
    }
  },
  setup (props) {
    type Names = typeof props['names'] // -> unknown[] になってしまう
    type Staff = typeof props['staff'] // -> { [key: string]: any } になってしまう
  }
})

PropType という型関数が用意されていますので、それを使ってキャストすることで型の補完が効くようになります。

子コンポーネント
import { defineComponent, computed, PropType } from '@vue/composition-api'
import { Person } from './Person'

export default defineComponent({
  props: {
    names: {
      type: Array as PropType<string[]>, // PropType で型の情報を付与
      required: true
    },
    staff: {
      type: Object as PropType<Person>, // PropType で型の情報を付与
      required: true
    }
  },
  setup (props) {
    type Names = typeof props['names'] // -> string[] と認識される
    type Staff = typeof props['staff'] // -> Person 型と認識される
  }
})

vue パッケージも PropType を export しているので、 Options API でも使えます。
※ 今回の例では PropType を使わず、 Array as () => string[] のように書いても同様の効果を得られます。
※ ちなみに、コンポーネントを作る関数名は以前 createComponent でしたが、@vue/composition-api@0.4 から defineComponent に変更されています。古いバージョンからアップデートした場合、 createComponent のままでも動きますが、コンソールに下記のエラーが表示されます。

`createComponent` has been renamed to `defineComponent`.

2. emit: 子から親にイベントを発生させる

次は子から親へデータを渡す方法です。
props のように直接データを渡す方法は用意されていないので、イベントを通じてデータを送ります。

子コンポーネントでイベントを発生させる方法

Composition API の setup 関数のふたつめの引数に context というオブジェクトが渡されてきます。これは従来(Options API)の this に入っていたプロパティやメソッドの一部が格納されています。
context.emit(eventName) を実行することで、カスタムイベントを発生させることができます。

子コンポーネント
<template>
  <div>
    <button @click="handleClick">Click me!</button>
  </div>
</template>

<script>
export default {
  setup (props, context) {
    const handleClick = () => {
      context.emit('my-event')
    }

    return { handleClick }
  }
}
</script>

props と違い、context は分割代入しても悪影響はありません。

export default {
  setup (props, { emit }) {
    const handleClick = () => {
      emit('my-event')
    }

    return { handleClick }
  }
}

また、 emit には任意の数の引数を渡すことができます。

子コンポーネント
export default {
  setup (props, { emit }) {
    const handleClick = () => {
      emit('my-event', 123, 'abc', false)
    }

    return { handleClick }
  }
}

親コンポーネントで子のイベントを受け取る方法

通常のクリックイベントなどと同じように、テンプレート内で @ もしくは v-on ディレクティブを使います。

親コンポーネント
<template>
  <div>
    <Child @my-event="handleEvent" />
  </div>
</template>

<script lang="ts">
import Child from './child.vue'

export default {
  components: { Child },
  setup () {
    const handleEvent = () => {
      alert('イベント発生!')
    }

    return { handleEvent }
  }
}
</script>

子コンポーネントの emit で 2 つ以上の引数を指定した場合は、イベントハンドラの引数として受け取ることができます。

子コンポーネント(前後略)
emit('my-event', 123, 'abc', false)
親コンポーネント(テンプレートは前と同じ)
export default {
  components: { Child },
  setup () {
    const handleEvent = (...args) => {
      alert(args) // -> 123, 'abc', false
    }

    return { handleEvent }
  }
}

※ ちなみに親のテンプレートで @my-event="handleEvent($event)" とすると、イベントハンドラには emit の第 2 引数のみ(上記の場合は 123)が入ってきます。

3. 双方向バインディング

さて、子に渡した親の変数を書き換えたい場合はどうしたらよいでしょう。
子コンポーネントの中で props の中身を直接変更しようとすると、以下のようにエラーになってしまいます。

子コンポーネント(テンプレート略)
export default {
  props: {
    count: Number
  },
  setup (props) {
    // ボタンのイベントハンドラ
    const handleClick = () => {
      props.count += 1
      /*
       * 以下のエラーが発生(改行は筆者が追加)
       * Avoid mutating a prop directly since the value will be overwritten
       * whenever the parent component re-renders.
       */
    }

    return { handleClick }
  }

そこでイベントを使います。
子コンポーネントでイベントを発生させ、その引数を(親コンポーネントの)イベントハンドラ内で代入する、という手順を踏むことで親コンポーネントの変数の値を変更できます。

子コンポーネント
export default {
  props: {
    count: Number
  },
  setup (props, { emit }) {
    // ボタンのイベントハンドラ
    const handleClick = () => {
      emit('my-event', props.count + 1)
    }

    return { handleClick }
  }
}
親コンポーネント
<template>
  <div>
    <!-- 上と下 どちらの書き方でもよい -->
    <Child :count="num" @my-event="num = $event" />
    <Child :count="num" @my-event="newVal => num = newVal" />

    <!-- setup 内で作った関数を渡して、その中で更新するのもあり -->
    <Child :count="num" @my-event="handleEvent" />
  </div>
</template>

<script>
import { ref } from '@vue/composition-api'
import Child from './child.vue'

export default {
  components: { Child },
  setup () {
    const num = ref(0)

    // イベントハンドラ内で num の値を更新
    const handleEvent = (newVal) => {
      num.value = newVal
    }

    return { num, handleEvent }
  }
}
</script>

ただ、これだといちいち代入の処理を書かないといけないので少し大変です。
短くかけるシンタックスシュガーが 2 種類用意されています。

3-1. v-model

まずは古参の v-model から。
親コンポーネント側は v-model というディレクティブに変数を入れるだけで準備完了です(input タグなどと同じ)。
先程に比べると、相当シンプルですね。

親コンポーネント
<Child v-model="num" />

子コンポーネントでは value という名前でプロパティが渡されてきます。更新の際は input イベントを発生させます。

子コンポーネント
export default {
  props: {
    value: Number
  },
  setup (props, { emit }) {
    // ボタンのイベントハンドラ
    const handleClick = () => {
      emit('input', props.value + 1)
    }

    return { handleClick }
  }
}

つまり、v-model は以下と同等です。

親コンポーネント
<Child :value="num" @input="num = $event" />

※ 当記事と直接関係ないですが、 v-model について深く知りたい場合は、先日公開された Vue.jsの双方向バインディング再入門 という記事がとても参考になります。

3-2. .sync

さて、もうひとつは Vue 2.3 で加わった .sync 修飾子です。
親コンポーネント側はプロパティを渡す際、後ろに .sync を付け足します。
これも属性がひとつだけなので、すっきりしてますね。

親コンポーネント
<Child :count.sync="num" />

子コンポーネントでのプロパティの受け取り方は通常どおりです。更新の際は update:プロパティ名 のイベントを発生させます。

子コンポーネント
export default {
  props: {
    count: Number
  },
  setup (props, { emit }) {
    // ボタンのイベントハンドラ
    const handleClick = () => {
      emit('update:count', props.count + 1)
    }

    return { handleClick }
  }
}

したがって、 .sync 修飾子は、以下の省略形ということになります。

親コンポーネント
<Child :count="num" @update:count="num = $event" />

3-3. Vue 3.0 での変更点

来たる Vue 3.0 では、上記の .sync 修飾子は廃止され、 v-model が引数を取れるようになるそうです。

引用元: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0005-replace-v-bind-sync-with-v-model-argument.md

Instead of:
<MyComponent v-bind:title.sync="title" />

the syntax would be:
<MyComponent v-model:title="title" />

<MyComponent v-model="xxx" />

<!-- would be shorthand for: -->

<MyComponent
  :model-value="xxx"
  @update:model-value="newValue => { xxx = newValue }"
/>
<MyComponent v-model:aaa="xxx"/>

<!-- would be shorthand for: -->

<MyComponent
  :aaa="xxx"
  @update:aaa="newValue => { xxx = newValue }"
/>

これを見ると、子コンポーネント側の受け取り方と更新方法は今までの .sync 修飾子のやり方に統一されていますね。
v-model に引数がない場合は model-value というプロパティが渡されるので、update:model-value イベントで更新します。

3-4. アンチパターン

プリミティブでない値(配列やオブジェクトなど)を渡した場合は、子コンポーネントから直接中身を変更できてしまいます。

親コンポーネント
<template>
  <div>
    <Child :obj="foo" :arr="bar" :dt="baz" />
  </div>
</template>

<script>
import { ref } from '@vue/composition-api'
import Child from './child.vue'

export default {
  components: { Child },
  setup () {
    const foo = ref({
      num: 0
    })
    const bar = ref([])
    const baz = ref(new Date())

    return { foo, bar, baz }
  }
}
</script>
子コンポーネント
export default {
  props: {
    obj: Object,
    arr: Array,
    dt: Date
  },
  setup (props) {
    // ボタンのイベントハンドラ
    const handleClick = () => {
      props.obj.num += 1 // 親コンポーネントの foo.num に反映される
      props.arr.push(0)  // 親コンポーネントの bar の要素が増加する
      props.arr[0] += 1  // 配列内の値を変更することもできる
      props.dt.setDate(Math.random()) // 日付が変わる
    }

    return { handleClick }
  }
}

イベントの処理を書く手間が省けるので便利に感じるかもしれませんが、どこで値を変えているのかが追いづらく、メンテナンス性が著しく下がってしまうので避けたほうがよいです(経験談)。
子はイベントを発生させるだけにして、親の変数の更新は親の中のみで行ないましょう。

まとめ

Vue.js の新しい記法、 Composition API での親子コンポーネント間で直接データを受け渡しする方法について紹介しました。
冒頭にもまとめましたが、従来の書き方を知っている場合はそれほど大きな変更点はありませんね。setup 関数内での使い方も一度知ってしまえば難しくはないです。ただ分割代入には気をつけましょう。

以前 Vue.js 公式サイトで、親子のコンポーネントの関係は props down, events up という言葉で説明されていました。1 このページで説明してきた内容を端的に表している言葉ですね。

今回の主旨から外れますが、親子間以外でもデータを共有したい場合は store パターン を試してみたり、公式の状態管理ライブラリ Vuex の導入を検討するとよいでしょう。

Vue 3.0 は Composition API 以外にも新機能がたくさんあるので待ち遠しいですね。
それでは。


  1. GitHub をさかのぼってみたら、2 年前の大きな改訂 で消えていました。つい最近見た気がするけど、そんなに前だったとは……。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

?【CakePHP2】js.mapファイルが存在しないエラーの解消法

環境

PHP 7.2.21
CakePHP 2.10.18

やりたいこと

pnotifyを追加したところ、下記の様なJsControllerが無いエラーが多数吐かれていたが動作に問題はなかった
が、このまま放置は良くないので出ないように修正したい

error.log
2020-03-04 10:10:58 Error: [MissingControllerException] Controller class JsController could not be found.
Exception Attributes: array (
  'class' => 'JsController',
  'plugin' => NULL,
)
Request URL: /js/vendor/jquery-pnotify/pnotify.js.map
Stack Trace:
#0 /***/***/***/webroot/index.php(100): Dispatcher->dispatch(Object(CakeRequest), Object(CakeResponse))
#1 {main}

やったこと

pnotify.custom.min.js
/*
PNotify 3.2.0 sciactive.com/pnotify/
(C) 2015 Hunter Perrin; Google, Inc.
license Apache-2.0
*/
!function(t,i) *******省略******* {"feturn s(i)});
//# sourceMappingURL=pnotify.js.map ←これを削除もしくはmapファイルを追加で対応可能

ここからmapファイルをダウンロードし同階層に格納
https://cdnjs.com/libraries/pnotify

もしくは最下部にあるmapファイルのリンクを削除

pnotify.custom.min.js
//# sourceMappingURL=pnotify.js.map
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RailsでjQueryを使う

学習中に作っていたrailsアプリでjQueryを使いたかったのですが、
設定方法があいまいだったのでざっと調べました。
参考にしていただけると幸いです。

jQueryを使いたい。

作業内容

1.  'jquery-rails'の導入
2.  turbolinksの停止。
3.  読み込みの確認。

1.  'jquery-rails'を導入する

最下行にjquery-railsを記載します。

Gemfile
gem 'jquery-rails'

2.  turbolinksを停止させる

Gemfileからturbolinksをコメントアウトする。

Gemfile
 ~変更前~
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
gem 'turbolinks', '~> 5' 

 ~変更後~
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
# gem 'turbolinks', '~> 5' # この行をコメントアウトする

( turbolinksはRails 4以降デフォルトでGemfileに導入されています。)

!ここでGemfileを変更したので、bundle installをします。


次に、application.html.haml から turbolinks の部分を削除します。
(application.html.erbから、hamlに変換しています。)

application.html.haml
 ~変更前~
!!!
%html
  %head
    %meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
    %title giga-app
    = csrf_meta_tags
    = stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload'  このオプションを消す
    = javascript_include_tag 'application', 'data-turbolinks-track': 'reload'  このオプションを消す
  %body
    = render "layouts/notifications"
    = yield

 ~変更後~
 省略
    = stylesheet_link_tag    'application', media: 'all'
    = javascript_include_tag 'application'
 省略

最後にapplication.js から turbolinks の部分を削除します。

application.js
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file. JavaScript code in this file should be added after the last require_* statement.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery      
//= require jquery_ujs
//= require turbolinks  ⬅️ ここを削除する
//= require_tree .


3.  Jsファイルの読み込みを確認。

実際の処理を記述するjsファイル( 今回は item.jsとします )に、
以下の記述をし、ブラウザで読み込みを確認します。

item.js
$(function(){
});

ここで、コンソールに何もエラーが出なければ問題なく読み込めています。

うまく読み込めていないと、何かしらエラーが出ています。
Image from Gyazo


ここまで問題なく進むと、RailsにてjQuery を使う準備が完了です。
イベントの発火などの記事も書きたいと思います...

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TOEIC400点台の僕がどうしてもやりたい英語js教材がありました

…のでやってみました。(※回し者ではありません。)

1. やった教材

Wes Bos氏の「javascript30」という教材です。
メールアドレスを登録するとすぐ閲覧できました。

https://javascript30.com/
https://github.com/wesbos/JavaScript30

2. Wes Bosと私の出会い

このQiita記事です。(めっちゃ人気!)
https://qiita.com/baby-degu/items/a06bddd1a61b7d907a55

この記事の

このコースを修了する頃には、JavaScript、DOM、そしてブラウザーAPIを使いこなせるようになっているはずです。

この部分を読んで、「マジか!!!すごっ!!!」となったのがきっかけです。

3. この教材の愛すべきところ

なんといっても無料!

海外のサイトだと、お金払えって言われると少し怖いですもん。

「文法理解」から「アプリ開発」へ移行する際のみぞを埋めてくれる!

Wes「基本文法を学んだあとは、たくさんのアプリを作るべきだ!でも何を作るかわからない?ならこれやって!」
僕「これだ!!!!」

初学者の妨げとなるものを排除!

よくわからないフレームワークや、「このアプリ以外でどこで使うの?」といったAPIは一切出てきません。
html・css・javascript これだけ!

サンプルが非常に魅力的!

https://javascript30.com/
このリンクの下のほうにサムネイルが載っています。
これが作れます。わくわくするでしょ??

時間がいい感じ

長くても30分程度。一日1つ。とても気軽にできます!

4.この教材の問題点

英語?

5.何としてもやりたかったので

とりあえす、やってみました。

6.意外といけた!

意外といけた理由を考えてみました。

さすがWes!簡単な単語で説明してくれる!

単語が意外と簡単!どっかで見たことあるような単語ばかりを選んで使ってくれている。
さすが超人気講師Wes!

私たちはプログラマー! 共通言語を持っているではないか!

簡単な単語を使ってくれてはいるけど、私も負けてません。
まったく聞き取れないところだってありました!

でも私たちはプログラマー。共通言語を持っているではないか!
そう。javascript !!

わからないキーワードは調べれば日本語で出てきます。そこで理解すればいいのです。
英語がわからなくても、javascriptで私たちは会話ができます!

tips これが意外とつかえた!

google翻訳の拡張機能!
https://chrome.google.com/webstore/detail/google-translate/aapbdbdomjkkjkaonfhkkikfgjllcleb?hl=ja

これの設定を「すぐにポップアップを表示する」にすると…
1.PNG
字幕を入れてわからない単語を選択するとすぐ検索できるよ!

7.やって身についた(と思う)こと

  • アプリケーション作るのって難しいんだろ?という固定概念の脱却(これが一番かも)
  • Javascriptってこういうことができるんだ。という知識
  • アプリケーションってこうやって作っていけばいいんだ。という知識
  • ものづくりの楽しさ!
  • 英語が理解できればもっっと広い世界が見えるかも!英語がんばろ!というモチベ向上

やってよかった!ありがとう!Wes!

8.恐れてはいけない!

わからないことにおそれちゃいけない。大体何とかなる!

Never fear, Wes Bos is here… (恐れるな。Wes Bosがここにいる…)
https://medium.com/employbl/30-things-i-learned-from-javascript-30-fa37d554bfca

9.実は…

  • 私はまだ23日目までしか完了してません(2020/04/09現在)。一日一個づつこなしていきます…
  • youtubeでも同じ動画を公開しています。だから自動翻訳機能を使えば(少し違和感のある)日本語でも学習できます。でも英語で勉強することを薦めるよ!

10. あとがき

興奮して急いで書いたため、誤字脱字があるかもしれません。
読みづらい文章になっています。まことにごめんなさい。後で修正します(予定は未定)。

この教材はJavascriptの基本中の基本が理解できてないと少し難しいかもしれません。
まだ身についてないよ!そんな方はこれがいいですよ!
https://jsprimer.net/
全部理解する必要はないかもしれませんが、全部目を通してからやるといいかもです!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む