20190227のJavaScriptに関する記事は19件です。

payjp.jsの導入方法<Rails>

Payjp.jsを導入するにあたって、理解するのに時間がかかってしまったので(なにせ記事が少ない!)今後導入される方の時間短縮のために、ここに残しておきます。

Pay.jpとは

Pay.jpのサービスを利用することでクレジットカード決済機能を簡単に導入することができます。
gem'payjp'をインストールすることで使用可能に。
利用方法については大きく2通りあります。

「チェックアウト」

公式で用意されたスクリプトタグをHTMLに記述すれば、デザインされた決済フォーム、カード情報のバリデーション、カード情報のトークン化を行うフォームを生成することができます。こちらを使用すれば、ささっとすぐに実装可能です。
ビューについてこだわりがないという方であればこちらで十分だと思います。

「カスタムフォーム(payjp.js)」

payjp.jsを使うことで、好きなデザインや挙動でトークン化を行うフォームを組み込めます。
ここで注意をしたいのが、payjp.jsはトークン化に特化したライブラリであり、これを使用したからといって決済ができる訳ではありません。
クレジットカード情報入力ページは自分の好みのビューにしたい!という方はこちらです。
今回はこのpayjp.jsで実装していくための説明となります。

トークンとは

クレジットカード情報を自身のデータベース上で管理するのはセキュリティの観点で好ましくないので、顧客が入力したカード情報をトークン化しなくてはいけません。
顧客情報を持った情報の塊のことをトークンを呼びます。IDを使用することで顧客情報を呼び出すことができます。

トークン化については公式を参考にしてください。
https://pay.jp/docs/cardtoken

またトークンは1度のみの使用しか出来ないので、2度以上使いたい場合は顧客カードを作成してカード情報をそこに紐付けることが必要です。顧客カードへの紐付けというのは後ほど説明します。

いざ導入!

ここからは実際のコードを交えて説明します。
まずは、顧客がカード情報を入力するためのフォームを作成します。

qiita.html.haml
%h2 クレジットカード情報入力
  = form_tag("/users/purchase", method: "PATCH", id: "charge-form") do

  %label カード番号
    %input{maxlength: "16", type: "text", class: "number", id: "payment_card_no" name: "number", placeholder: "カード番号"}
  %label CVC
    %input{type: "text", class: "cvc", id: "payment_card_security_code", name: "cvc", maxlength: "3", placeholder: "CVC"}
  %label 有効期限
    %input{type: "text", class: "exp_month", id: "payment_card_expire_mm",  name: "exp_month", maxlength: "2", placeholder: "月"}
    %input{type: "text", class: "exp_year", name: "exp_year", maxlength: "4",  placeholder: "年"}

Pay.jpにトークンを作成してもらうには、公式で用意されているフォームを活用しましょう。
(本記事では所々書き換えています。)

qiita.js
$(document).on('turbolinks:load', function() {
  var form = $("#charge-form");
  Payjp.setPublicKey('pk_test_0383a1b8f91e8a6e3ea0e2a9');

  $("#charge-form").on("click", "#submit-button", function(e) {
    e.preventDefault();
    form.find("input[type=submit]").prop("disabled", true);
    var card = {
        number: parseInt($("#payment_card_no").val()),
        cvc: parseInt($("#payment_card_security_code").val()),
        exp_month: parseInt($("#payment_card_expire_mm").val()),
        exp_year: parseInt($("#payment_card_expire_yy").val())
    };
    Payjp.createToken(card, function(s, response) {
      if (response.error) {
        alert("error")
        form.find('button').prop('disabled', false);
      }
      else {
        $(".number").removeAttr("name");
        $(".cvc").removeAttr("name");
        $(".exp_month").removeAttr("name");
        $(".exp_year").removeAttr("name");

        var token = response.id;
        $("#charge-form").append($('<input type="hidden" name="payjp_token" class="payjp-token" />').val(token));
        $("#charge-form").get(0).submit();
      }
    });
  });
});

顧客がsubmitボタンを押した瞬間にこのjsファイルが反応するようにしてあげます。
このフォームの中のPayjp.createTokenで作成されたトークンIDがresponse.idに返ってきます。
後はif文で分岐をさせてあげて、最後にe.preventDefault();によって停止させていたsubmitを実行させます。

setPublicKeyにはpay.jpにログインした際の管理画面で確認できるAPIキーを入れてください。
APIキーはconfig/initializers/setting.rbを作成してその中に変数を定義して入れてあげるとrbファイル間で共有できるので、利便性・セキュア面で良いですね!
(記載しているAPIキーは公式のものなのでご心配なく)

qiita.html.haml
 %input.payjp-token{ type: "hidden", name: "payjpToken", value: "" }

そしてHTML上で上記の記述をして、jsファイルで作成したトークンIDが空のvalueに入ることで、paramsとしてコントローラーへ投げることができます。
inputタグをhiddenにすることでビュー上には反映されていません。
以上でトークン作成と、トークンIDをparamsとしてコントローラーへ渡せるようになりました。

顧客カードとの紐付け

カード情報非通過対応により2018年6月頃からサーバーサイドからのカード情報のPOSTリクエストができなくなりました。
そのためトークン化はビュー上で行いましたが、顧客作成はコントローラーで行います。

users.controller.rb
Payjp.api_key = PAYJP_SECRET_KEY
customer = Payjp::Customer.create(card: params[:payjp_token])

Payjp::Customer.createで顧客カードが作成されたので、その中のcardキーのバリューとしてトークンIDを指定してあげます。
カード情報を取り出す際にもこのIDは使用するので、私はUserテーブルのカラムに入れてあげました。

他にも支払い等の詳しいやり方は公式のリファレンスを参照ください。
https://pay.jp/docs/api/#introduction

終わりに

私自身まだエンジニアとしてプログラミングを学んで3ヶ月満たないので、ツッコミどころがあるかもしれません。
こういったやり方もあるんだな〜という、一つの実装方法として参考程度に見てください。
他に効率の良いやり方があるよ!この記述無駄じゃない?と気づいた方がいらしたらご指摘ください(_ _)

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

Node.jsとExpressでローカルサーバーを構築する(3) ―JSONを返すスタブAPI―

※ 本記事は、Node.jsとExpressでローカルサーバーを構築する(2) ―Expressでルーティング―の続きです。

最近は、Jsonデータを送りつけたり受け取ったりしてなんやかやするケースが増えてきました。私の極狭い認識の範囲では、フロント側の製作期間中にAPIが完成していないことが多いです。
その為、ローカルサーバーにJSONを返すスタブAPIの機能を実装したいと思います。

スタブとは - IT用語辞典

概要

この記事の概要

  • 目的
    • Node.jsとExpressを利用して、PC上にローカルサーバーを立ち上げる
  • 本記事のゴール
    • GETでリクエストを送ると、JSONデータが返ってくる
    • POSTでJSONデータを送ると、200 OK のレスポンスコードが返ってくる
  • 対象者
    • WEBフロント担当者
    • HTML,CSS,JavaScript(es2015含む)の基本的な構文を理解している人
    • HTTP通信、GET、POSTなどについて、ある程度理解している人(ざっくりで良いです)
    • 黒い画面にコマンドを打ち込むことに抵抗がない方
  • 環境・バージョン
    • Windows10
    • Node.js(推奨版) 10.15.01
    • npm 6.4.1
    • Express 4.16.4
    • Advanced REST client 10.0.12

Chrome拡張ツール:Advanced REST client

GETやPOSTの動作確認を簡単に行うために、Chrome拡張ツールの「Advanced REST client」を利用します。
Advanced REST client
導入方法や利用方法は、下記サイトが詳しいです。分かりやすいUIですので、本記事内でも都度説明します。
「Advanced REST client」の使い方まとめ ~GoogleChromeの拡張ツール

expressでのルーティングの実装

前回のおさらい
Expressを使ったサーバーで、静的コンテンツのルーティングのためにuseメソッドやexpress.staticメソッドを利用しました。

const express = require('express');
const app = express();

app.listen(8080);

app.use(express.static(path.join(__dirname, 'public')));

useは、全てのタイプのリクエストで実行されます。

ルーティングの基本

GETリクエストを受け取る

GETリクエストを受け取りたい場合は、getメソッドを利用します。

// サイトルートへのGETリクエストの場合
app.get('/', (req, res, next)=>{
  res.send('ルートだよ!');
});

// /hoge/fugaへのGETリクエストを受け取りたい場合
app.get('/hoge/fuga', (req, res, next)=>{
  res.send('ほげふが');
});

POSTリクエストを受け取る

POSTリクエストを受け取りたい場合は、postメソッドを利用します。

// サイトルートへのPOSTリクエストの場合
app.post('/', (req, res, next)=>{
  res.send('ルートだよ!全員集合!');
});

// /hoge/fugaへのPOSTリクエストを受け取りたい場合
app.post('/hoge/fuga', (req, res, next)=>{
  res.send('ほげふが');
});

パスの指定

getpostいずれの場合も、第一引数にはピックアップしたいサイト上のパスを指定します。この指定は、ワイルドカードや正規表現などを組み合わせることも可能です。

app.get(/^book/, ()=>{});

上記の例では、/book/booksにはマッチしますが、/boo/ibookとはマッチしません。
公式ガイド:Expressでのルーティング

ミドルウェアの基本

getpostいずれの場合も、第二引数はリクエストを受け取った際のコールバック関数を渡します。このコールバック関数は、ミドルウェア(中間処理を行う関数)と呼ばれます。
ミドルウェアは、リクエストreq・レスポンスresを表すオブジェクトと、後続のミドルウェアへのバトンの役割を果たす関数nextを受け取ります。
このミドルウェア内でクライアントにレスポンスをするか、明示的にプロセスを終了させるかnext()を実行して他へ制御を渡すかしないと、応答なしの状態に陥ってしまいます。

app.get(/^book/, (req, res, next)=>{

  if(req.query.id){
    res.send('OK.');
  }else{
    next();
  }
});

res.send
クライアントへHTTPレスポンスを返すためのメソッドです。
文字列だけでなく、配列やオブジェクトも送信できます。事前に指定しなかった場合、ヘッダーのContentTypeは送信データに応じて自動的に割り当てられます。

res.end
データ送信をせずにレスポンスのプロセスを終了させるためのメソッドです。
実体はNode.jsのコアモジュールhttpresponse.endです。引数にデータを渡せば送信されますが、res.sendなどの専用メソッドを利用する方が望ましいです。

公式ガイド:アプリケーションで使用するミドルウェアの作成

送信されたデータを受け取る

GETで送信されたURLパラメータ

GETで送信されたURLパラメータを受け取るには、ミドルウェアに渡されたreqオブジェクトのqueryプロパティにアクセスします。

/**
 * /search?color=red&size=small へのリクエスト
 */ 
app.get('/search', (req, res, next)=>{

  console.log(req.query.color); // "red"
  console.log(req.query.size);  // "small"

});

リファレンス:req.query(英語)

POSTで送信されたJOSNデータ

POSTでJSONデータを受け取るには、予めパース処理を行うミドルウェアを設定しておく必要があります。このミドルウェアは、Expressに予め用意されています。

const express = require('express');
const app = express();

app.use(express.json()); //パース用ミドルウェアを設定

app.post((req, res)=>{
  console.log(req.body); //パースされたデータを参照
});

Expressの静的メソッドjsonの実行結果をuseメソッドに渡しています。渡すのはあくまでjsonの実行結果として返されるミドルウェアで、jsonメソッドそのものではありません。パースされたデータは、requestオブジェクトのbodyプロパティに格納されます。

リファレンス:express.json(英語)

JSONデータを送る

オブジェクトをパースして送る

オブジェクトをJSONにパースした上で送信したい場合は、res.jsonメソッドを使います。

app.get('/', (req, res, next)=>{
  res.json({ message: `This is a pen.`});
});

リファレンス:res.json(英語)

 JSONファイルを送る

予め用意したJSONファイルを送信したい場合、res.sendFileメソッドが便利です。このメソッドは、Expressのバージョン4.8.0以降からサポートされています。

app.get('/', (req, res, next)=>{
  res.sendFile(__dirname + '/data.json', (err) => {
    if (err) {
      res.sendStatus(400);
    } else {
      console.log('sending completed');
    }
  });
});

第一引数に送信したいファイルの絶対パスを指定します。
第二引数には、送信完了もしくはエラー発生時に呼び出されるコールバック関数を指定します。
エラー発生時はエラー情報を引数で受け取ります。成功時はundefinedです。

リファレンス:res.sendFile(英語)

route()でミドルウェアをまとめる

routeメソッドを利用すると、同一パスに対するタイプ別のリクエストに対し、ミドルウェアを見通し良くまとめられます。

app.route('/moimoi')
  .get((req, res)=>{
    // GET
  })
  .post((req, res)=>{
    // POST
  })
  .put((req, res)=>{
    // PUT
  })
  .delete((req, res)=>{
    // DELETE
  });

リファレンス:app.route(英語)

 Routerでルーティング処理を外部モジュール化する

ルーティング処理を他ファイルで定義して、メインの処理にミドルウェアとして組み込むことが出来ます。

ミドルウェアを定義して公開する

Expressの静的メソッドexpress.Routerを実行してルーティング用のミドルウェアの骨格を生成します。
この骨格に対して、「このパスへのリクエストはこれ返せ」といったルーティング処理を肉付けしていきます。肉付けの方法は、通常のexpress()で取得したオブジェクトと同様に、use、get、post等を利用します。
そして、完成したミドルウェアをエクスポートして公開します。

/**
 *  /api/index.js
 */
const express = require('express');
const router = express.Router(); //ミドルウェアの準備

router.get('/items', (req, res) => {
  res.json({ items: [] });
});

router.post('/registration', (req, res) => {
  res.end('Completed');
});

module.exports = router; //エクスポートして公開

公開されたミドルウェアを利用する

公開されたミドルウェアをインポートしてルーティングに適用します。

/**
 * /app.js
 */
const express = require('express');
const app = express();
const api = require('./api/'); //ミドルウェア読み込み

app.use('/api', api);

/apiへのリクエストの対応を、/api/index.jsで定義したミドルウェアへ渡しています。
この際、ミドルウェア内のルートパスは/apiです。ミドルウェア内で指定した/itemsは、実際は/app/itemsへのリクエストです。ミドルウェア内で/app/itemsとしてしまうと、実際には/app/app/itemsを指定したことになってしまいます。

ガイド:express.Router(日本語)
リファレンス:express.Router(英語)

API機能の実装

上記を踏まえて、API機能を実装していきます。

 ファイルとフォルダの準備

API関連のファイルを格納するフォルダapiを追加します。
このフォルダの配下に、クライアントに送りつけるjsonデータと、ルーティング処理を記述するjsファイルを作成します。

sample/
  ├ public/
  │    ├ css/
  │    │   └ sample.css 
  │    ├ js/
  │    │   └ sample.js 
  │    ├ img/
  │    │   └ sample.png 
  │    └ index.html
  ├ api/
  │    ├ index.js
  │    └ data.json
  └ app.js
/**
 * /app.js
 */
const express = require('express');
const app = express();
const path = require('path');
// /api/index.js で定義されたミドルウェア
const api = require('./api/');

app.listen(8080, () => {
  console.log('Running at Port 8080...');
});

// APIルーティング用ミドルウェアを/apiに設定
app.use('/api', api);

app.use(express.static(path.join(__dirname, 'public')));

app.use((req, res) => {
  res.sendStatus(404);
});


/**
 * /api/index.js
 */
const express = require('express');
const router = express.Router();

// JSONパース
router.use(express.json());

// /api/foo へのGETリクエスト
router.get('/foo', (req, res) => {
  // ファイルを転送
  res.sendFile(__dirname + '/data.json', (err) => {
    if (err) {
      res.sendStatus(400);
    } else {
      console.log('sending completed');
    }
  });
});

// /api/bar へのGET・POSTリクエスト
router.route('/bar')
  .get((req, res) => {

    // 受け取ったパラメータをそのままJSONにして送り返している
    res.json(req.query);
  })
  .post((req, res) => {

    // 必須のデータ項目を、id,name,address として、受信データをチェックしている
    const nameAry = ['id', 'name', 'address'],
      failed = nameAry.some(v => !req.body[v]);

    if (failed) {
      res.sendStatus(400);
    } else {
      res.sendStatus(200);
    }
  });

module.exports = router;

/api/data.jsonに適当な内容を記述します。

{
  "id": "W0001",
  "title": "I Love Cats and Dogs",
  "price": 3000000000000
}

準備が出来たら、サーバーを起動します。

$ node app.js

「Advanced REST client(ARC)」で送受信テスト

送受信のテストを、Chromeの拡張ツールの「Advanced REST client」で行いたいと思います。

こちらから取得できます。
ホーム > アプリ > Advanced REST client

ARCを起動して、まずはTOPページにアクセスしてみます。

  1. Methodを「GET」
  2. RequestURLに「http://localhost:8080/
  3. 「SEND」をクリック

arc.png

「200 OK」とHTMLデータが取得できました。

では、続けてAPIのデータを取得します。
GETで「http://localhost:8080/api/foo」へリクエストを送ると、/api/data.jsonが取得できます。

次は、GETで「http://localhost:8080/api/bar?name=pee&address=poo&age=17」へリクエストを送ります。
URLパラメータの値をJSONで取得で取得できます。

次は、POSTで「http://localhost:8080/api/bar」へリクエストを送ります。

  • 「Parameters>Body>Body content type」を「application/jdon」
  • 「Parameters>Body>Editor view」を「JSON visual editer」
  • id, name, address の三つのプロパティを含むJSONデータを定義

arc02.png

「200 OK」が返ってきました。
次は、項目を一つ減らして送信してみると、「400 Bad Request」が返ってきます。

これで、APIの開発が間に合っていなくても何とかなりそうですね!

初心者の拙い記事を読んでいただき、ありがとうございました。
お役に立てていたら幸いです。

JSON Server

この記事を書いている途中で、JSONを返すAPIを簡単に実現する「JSON Server」なるものの存在を知りました。。。
JSON Server使いこなし - モックサーバーの起動とリソース処理 | CodeGrid
公式:GitHub - typicode/json-server

リンク一覧

  1. Node.jsとExpressでローカルサーバーを構築する(1) ―Node.jsとnpmの導入―
  2. Node.jsとExpressでローカルサーバーを構築する(2) ―Expressでルーティング―
  3. Node.jsとExpressでローカルサーバーを構築する(3) ―JSONを返すスタブAPI―(本記事)
  4. Node.jsとExpressでローカルサーバーを構築する(4) ―他サーバーから画像を取得する―(準備中)

参考情報

Express公式サイト(英語)
Node.js + ExpressでREST API開発を体験しよう[作成編]

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

JQueryでhtmlをincludeする

いまさらJQuery。

概要

html内でフッターやヘッダーなどの共通部分のhtmlをincludeするコード。
htmlを記述する際に同じ部分をたくさん書かずに済む。メンテも楽!

コード

共通部分を呼び出したいhtmlファイルの一部。共通部分は<div class="target"></div>の中に展開されます。

index.htmlの一部
<script type="text/javascript" src="includeTemplateHTML.js">
IncludeTemplateHTML("div.target","/Path/Of/templateHTML.html");
</script>
<div class="target"></div>

共通部分を呼び出すためのJS関数。

includeTemplateHTML.js
function IncludeTemplateHTML(selector,filepath){
    $(function (){
        $.ajaxSetup({cache:false});
        $(selector).load(filepath);
    });
}

補足

template自体が共通部分なのでさらにその中の一部分を呼びたい、ということはほとんどないと思いますが、
引数のfilepathを

filepath="/Path/Of/templateHTML.html div#hogehoge"

にかえてやればtemplateHTML.htmlのdivタグのid="hogehoge"の内容だけをselectorにインクルードさせることができます。

参考

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

JavaScriptについての個人的まとめ

この記事について

自分の備忘録として、JavaScript全般についての自分の考えや調べたことを記載していきます。
ほぼリンク集として使用。
経験が浅いので、間違っていることもあると思いますがご容赦ください。

小技

事項 説明 参照
イベント処理 イベント、イベントハンドラについて イベント処理/JavaScript
イベントリスナー一覧 イベントリスナーについて イベントリスナー一覧/JavaScript
JQuery-入力チェック jQuery Validation Engine
を使った入力チェック
jQueryで簡単!フォームをリアルタイムで入力チェック

サンプルコード
ブレークポイントによるデバッグ - ChromeのデベロッパーツールでJSをデバッグする方法(2018年版)
要素に紐づくイベント確認 ボタンを押したらどのイベントが動くかを確認 JavaScript,jQueryの爆速コーディング、デバッグ方法論の勧め

エラー対応

事象 説明 参照
資源が読み込めない Failed to load resource: the server responded with a status of 404 (Not Found) Jqueryがテストサーバーで動いていたけど、本番環境で動かない場合
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

TWAの挙動をPWAに寄せていく(起動編)

先日、TWAサポートライブラリを使用してPWAをAndroidアプリ化してPlayストアから配信する方法を紹介しました。

【実践】Google Play Store でPWA配信 (TWA)

PWAをPlayストアから配信するという目的は達成しましたが、起動時の見栄えがあまり良くないなぁ・・・という状態だったので、少しのコード追加で起動時挙動をPWAに寄せる方法を紹介したいと思います。

PWAとTWAの起動時挙動はどう違うか

PWAとTWAの起動時挙動を比較してみます。

PWAの挙動

pwa.gif

TWAの挙動

before.gif

差分

  • 起動時のスプラッシュページがない
  • 起動時の画面でActionBarが表示されている(QRとだけ表示されているヘッダー)
  • Webページがフェードインじゃなくスライドインで表示される
  • Webページ表示中のステータスバーが白くて浮いている

PWAに寄せていく

GitHubのcommitログと共に変更箇所を説明します。

ActionBarを消す

アプリのテーマをDarkActionBarからNoActionBarに変更することで、ActivityのActionBarを消しています。

https://github.com/zprodev/QR-TWA/commit/6d8732fb94840b89792f3b33836fc3f17cad4601

TWAのステータスバーにテーマカラーを反映

元はTWAサポートライブラリで提供されているLauncherActivityをそのまま使用していましたが、LauncherActivityを継承した自前のActivityを使用するよう変更し、ChromeCustomTabのIntentを生成しているメソッドをオーバーライドします。
https://github.com/zprodev/QR-TWA/commit/fb93ee7b87827627f03e7258904333003f3cec7b

その上で、リソースとして定義済みのcolorPrimaryDarkをsetToolbarColorでTWAに設定しています。
https://github.com/zprodev/QR-TWA/commit/3944385ce235c405788a0879ef23b13e23dfce3c

TWA起動時のトランジションを変更

ステータスバーの色変更と同様に起動時アニメーションを設定することができるので、Alpha変更アニメーションを定義してsetStartAnimationsでTWAに設定しています。
https://github.com/zprodev/QR-TWA/commit/b0ba980f52a8a028ed23ecc8f814662cd0404579

結果

変更後の起動時挙動は以下ようになります

after.gif

スプラッシュページに関しては若干面倒なので省きましたが、デフォルトの状態よりかなりPWAに近づき違和感がなくなったかなと思います。

実機で見てみたい方は↓から試してみてください :thumbsup:
PWA: https://qr.zpro.app
アプリ: https://play.google.com/store/apps/details?id=app.zpro.qr

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

jsオフでも変にならないように年を自動更新

hogehoge.html
<span id="copyright-year">2019</span>
hogehoge.js
var date = new Date();
var y = String(date.getFullYear());
var getCopy = $('#copyright-year');
var tx = getCopy.text();
var t = tx + ' - ' + y;

if (tx !== y) {
  getCopy.text(t);
}

htmlには公開年、もといjs無効の時に見せたい年を書いておく。
jsが動けば、公開から現在までを2018 - 20XXといったように表示。

兼投稿テストでした。

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

Google Apps Scriptで使えるJSのバージョンについて調査

はじめに

未来になると拡張されてると思うので 2019/02/27 時点での話です。

対応しているバージョン

  • https://developers.google.com/apps-script/guides/services/#basic_javascript_features
  • 公式にはJavaScript 1.6をベースにしていて1.7と1.8のfeatureも少し入ってるよ、というアナウンス
    • 何が入ってて何が入ってないは書いていないので読者の想像に任せられている... :poop:
  • :arrow_up: の記事から各バージョンへのリンクはMDNに向けて貼られているのでMDNでのスペックで調べてます

1.6

feature 対応状況
E4X :o:
Arrayの拡張 :o:
Array および String の汎用化 :o:

1.7

feature 対応状況
ジェネレータ (yield) :x:
イテレータ :o:
配列内包 :o:
let :x:
分割代入 :o:

1.8

1.8.11.8.5のfeatureも含む

feature 対応状況
式クロージャ :x:
ジェネレータ式 :x:
Array.reduce :o:
Array.reduceLight :o:
Object.getPrototypeOf :o:
Using Native JSON :o:
Object.create :o:
Object.defineProperty :o:
Object.defineProperties] :o:
Object.getOwnPropertyDescriptor :o:
Object.keys :o:
Object.getOwnPropertyNames :o:
Object.preventExtensions :o:
Object.isExtensible :o:
Object.seal :o:
Object.isSealed :o:
Object.freeze :o:
Object.isFrozen :o:
Array.isArray :o:
Date.toJSON :o:
getter/setter :o:
strictモードのサポート :question:
Proxy :x:

まとめ

  • 意外と対応していたのでGASがんばってる
  • polyfillが使えないんでES7で入った仕様とかはGASだと現状は動きません :cry:
    • たとえば Array.prototype.includes
  • strictモードのサポートが :question: になっているのはちゃんとエラーになるケースならないケースがあったので部分的に対応してるようだったからです
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ml5jsで特徴抽出を用いた回帰を試す

Daniel Shiffmanさんのcodingtrainのチュートリアルを参考に
特徴抽出を用いた回帰(Regression with Feature Extractor)を試してみました。
featureExtractor()メソッドを用いて実装できます。

ファイルは以下にアップしています。
https://www.dropbox.com/s/3cgr99lwuocugwo/03_2_featureExtractorRegression-demo.zip?dl=0

コードは以下です。
試す場合は、プロジェクトフォルダに「images」フォルダを設置し、配下に「happy.png」「sad.png」を置いてください。
ライブラリはCDNを読み込んでますのでローカルサーバー上で実行してください。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Feature Extractor Regression</title>
  <style>
    body {
      width: 100%;
      height: 100%;
      margin: 0;
      padding: 0;
      overflow: hidden;
    }
    #inputContainer {
      position: absolute;
      top: 10px;
      left: 10px;
    }
    input, button {
      display: block;
      margin: 0 10px 10px 0;
    }
  </style>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/addons/p5.dom.min.js"></script>
  <script src="https://unpkg.com/ml5@0.1.1/dist/ml5.min.js"></script>
  <script src="sketch.js"></script>
</head>
<body>
  <div id="inputContainer"></div>
</body>
</html>
let mobilenet;
let predictor;
let video;
let happyIco;
let sadIco;
let value = 0;
let slider;
let addButton;
let trainButton;
// MobileNetモデルの準備が完了したときコールバック関数として呼ばれる
function modelReady() {
  console.log('モデルが準備できた!');
}
// ビデオ入力したクラス分類機の準備が完了したときコールバック関数として呼ばれる
function videoReady() {
  console.log('ビデオが準備できた!!!');
}
// トレーニング完了時に1度だけ実行される関数
function whileTraining(loss) {
  if (loss == null) {
    console.log('トレーニングが完了した!');
    // 画像の予測を取得する
    predictor.predict(gotResults);
  } else {
    console.log(loss);
  }
}
// トレーニング完了時に再帰的に実行される関数
function gotResults(error, result) {
  if (error) {
    console.error(error);
  } else {
    // 画像の予測の値を代入する
    value = result;
    // 画像の予測を取得する
    // 再帰的に実行
    predictor.predict(gotResults);
  }
}
function setup() {
  // キャンバスを生成する
  createCanvas(windowWidth, windowHeight);
  // ウェブカメラから映像をキャプチャし、Video要素を生成
  video = createCapture(VIDEO);
  // Video要素を非表示にする
  video.hide();
  happyIco = loadImage('images/happy.png');
  sadIco = loadImage('images/sad.png');
  // 背景を黒で塗りつぶす
  background(0);
  // MobileNetで事前学習された特徴を展開
  mobilenet = ml5.featureExtractor('MobileNet', modelReady);
  // ビデオを入力にして、回帰のカスタム予測子を生成する
  predictor = mobilenet.regression(video, videoReady);
  // スライダーを生成(0 ~ 1)
  slider = createSlider(0, 1, 0.5, 0.01);
  slider.parent('inputContainer');
  // 「サンプル画像追加」ボタンンを生成する
  addButton = createButton('サンプル画像を追加する');
  addButton.parent('inputContainer');
  addButton.mousePressed(function() {
    // 0 ~ 1の値に画像を紐づける
    predictor.addImage(slider.value());
  });
  // 「トレーニング開始」ボタンを生成する
  trainButton = createButton('トレーニング開始');
  trainButton.parent('inputContainer');
  // 「トレーニング開始」ボタンが押された時の処理
  trainButton.mousePressed(function() {
    // クラス分類機を再トレーニングする(Transfer Learning 転移学習)
    predictor.train(whileTraining);
  });
}
function draw() {
  // 背景を黒で塗りつぶし
  background(0);
  // ウェブカメラからの映像をキャンバスに描画
  // ミラー表示
  imageMode(CORNER)
  push(); 
    translate(width,0); 
    scale(-1.0,1.0); 
    image(video, 0, 0, width, height);
  pop();
  // 画像の直径は横幅の1/10
  let diameter = width / 10;
  // 円を描画
  imageMode(CENTER);
  // 上下左右に画像を配置
  for ( let x = 0; x < width; x += diameter) {
    for ( let y = 0; y < height; y += diameter) {
      if (value == 0) {
        break; // value = 0(初期値)のときは表示しない
      } else if (value < 0.5) {
        // 楽しい顔のアイコンを表示
        image(happyIco, x + diameter/2, y + diameter/2, diameter*Math.cos(value * Math.PI), diameter); 
      } else {
        // 悲しい顔のアイコンを表示
        image(sadIco, x + diameter/2, y + diameter/2, diameter*Math.cos(value * Math.PI), diameter);
      }
    }
  }
  // 線を描画しない
  noStroke();
  // 塗りを黒へ
  fill(0);
  // 矩形の描画モードを左上へ
  rectMode(CORNER);
  rect(0, height - 50, width, 50);

  // 矩形の描画モードを中央へ
  rectMode(CENTER);
  // 塗りつぶしを赤へ
  fill(255, 0, 0);
  rect(value * width, height - 25, 50, 50);
  // 塗りつぶしを白に設定
  fill(255);
  // テキストのサイズを設定
  textSize(25);
  // テキストを描画 
  text(value, 10, height - 10);
}
function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
}

ローカルサーバーを立ち上げ、ブラウザでindex.htmlを開いたら、
以下の手順でテストしてみてください。

  1. スライダーを左端までドラッグし、ウェブカメラの前で前でポーズをとって、[楽しい]ボタンをクリックします。約15枚の画像を追加してみてください。
  2. スライダーを右端までドラッグし、ウェブカメラの前でポーズを変えて、[悲しい]ボタンをクリックします。約15枚の画像を追加してみてください。
  3. [トレーニング開始]をクリックして、トレーニングプロセスが完了するまで待ちます。 (コンソールログにプロセスが表示されます。)
  4. トレーニングが完了したら、モデルをトレーニングした2つのポーズを切り替えます。

※カメラのいろんな位置で、ポーズ画像を追加した方が精度が上がるかと思います。

  • 楽しい
    happy.png

  • 悲しい
    sad.png

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

ml5jsで特徴抽出を用いた分類器を試す

Daniel Shiffmanさんのcodingtrainのチュートリアルを参考に
特徴抽出を用いた分類器(Classifier with Feature Extractor)を試してみました。
featureExtractor()メソッドを用いて実装できます。

ファイルは以下にアップしています。
https://www.dropbox.com/s/i7f8fs6wiznzxs6/02_2_featureExtractorClassification-demo.zip?dl=0

コードは以下です。
試す場合は、プロジェクトフォルダに「images」フォルダを設置し、配下に「happy.png」「sad.png」を置いてください。
ライブラリはCDNを読み込んでますのでローカルサーバー上で実行してください。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Feature Extractor Classification</title>
    <style>
        body {
            width: 100%;
            height: 100%;
            margin: 0;
            padding: 0;
            overflow: hidden;
        }
        #inputContainer {
            position: absolute;
            top: 10px;
            left: 10px;
        }
        input, button {
            display: block;
            margin: 0 10px 10px 0;
        }
    </style>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/p5.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.6.1/addons/p5.dom.min.js"></script>
    <script src="https://unpkg.com/ml5@0.1.1/dist/ml5.min.js"></script>
    <script src="sketch.js"></script>
</head>
<body>
    <div id="inputContainer"></div>
</body>
</html>
let mobilenet;
let classifier;
let video;
let happyIco;
let sadIco;
let label = '';
let preLabel = '';
let happyButton;
let sadButton;
let trainButton;
let count = 0;

// MobileNetモデルの準備が完了したときコールバック関数として呼ばれる
function modelReady() {
  console.log('モデルが準備できた!!!');
}

// ビデオ入力したクラス分類機の準備が完了したときコールバック関数として呼ばれる
function videoReady() {
  console.log('ビデオが準備できた!!!');
}

// トレーニング完了時に1度だけ実行される関数
function whileTraining(loss) {
  if (loss == null) {
    console.log('トレーニングが完了した!');
    // 画像の予測を取得する
    classifier.classify(gotResults);
  } else {
    console.log(loss);
  }
}

// トレーニング完了時に再帰的に実行される関数
function gotResults(error, result) {
  if (error) {
    console.error(error);
  } else {
    // ラベルに結果を代入
    label = result;
    // 画像の予測を取得する
    // 再帰的に実行
    classifier.classify(gotResults);
  }
}

function setup() {
  // キャンバスを生成する
  createCanvas(windowWidth, windowHeight);

  // ウェブカメラから映像をキャプチャし、Video要素を生成
  video = createCapture(VIDEO);
  // Video要素を非表示にする
  video.hide();

  happyIco = loadImage('images/happy.png');
  sadIco = loadImage('images/sad.png');

  // 背景を黒で塗りつぶす
  background(0);

  // MobileNetで事前学習された特徴を展開
  mobilenet = ml5.featureExtractor('MobileNet', modelReady);
  // ビデオを入力にして、既に展開させた特徴抽出でクラス分類機を生成する
  classifier = mobilenet.classification(video, videoReady);

  // 「楽しい」ボタンを生成する
  happyButton = createButton('楽しい');
  happyButton.parent('inputContainer');
  // 「楽しい」ボタンが押された時の処理
  happyButton.mousePressed(function() {
    // クラス分類機に「楽しい」ラベルを付与して画像を追加
    classifier.addImage('happy');
  });

  // 「悲しい」ボタンを生成する
  sadButton = createButton('悲しい');
  sadButton.parent('inputContainer');
  // 「悲しい」ボタンが押された時の処理
  sadButton.mousePressed(function() {
    // クラス分類機に「悲しい」ラベルを付与して画像を追加
    classifier.addImage('sad');
  });

  // 「トレーニング開始」ボタンを生成する
  trainButton = createButton('トレーニング開始');
  trainButton.parent('inputContainer');
  // 「トレーニング開始」ボタンが押された時の処理
  trainButton.mousePressed(function() {
    // クラス分類機を再トレーニングする(Transfer Learning 転移学習)
    classifier.train(whileTraining);
  });
}

function draw() {
  // 背景を黒で塗りつぶし
  background(0);

  // ウェブカメラからの映像をキャンバスに描画
  // ミラー表示
  imageMode(CORNER)
  push(); 
    translate(width,0); 
    scale(-1.0,1.0); 
    image(video, 0, 0, width, height);
  pop();

  // 画像の直径は横幅の1/10
  let diameter = width / 10;

  // 円を描画
  imageMode(CENTER);

  // 状態が変更(happy <-> sad)されたらカウンターをリセット
  if (label !== preLabel) count = 0;

  // 上下左右に画像を配置
  for ( let x = 0; x < width; x += diameter) {
    for ( let y = 0; y < height; y += diameter) {
      // 回転のタイミングをずらす
      let delay = (x / diameter + y / diameter) * 0.2;
      // 画像のサイズ
      let scale = Math.min(1, count/90);

      if (label == 'happy') {
        // 楽しい顔のアイコンを表示
        image(happyIco, x + diameter/2, y + diameter/2, diameter*Math.sin(count * Math.PI / 180 - delay) * scale, diameter * scale);
      } else if (label == 'sad') {
        // 悲しい顔のアイコンを表示
        image(sadIco, x + diameter/2, y + diameter/2, diameter*Math.sin(count * Math.PI / 180 - delay) * scale, diameter * scale);
      }
    }
  }

  count+=4;
  // 前フレームのlabelをキャッシュしておく
  preLabel = label;
}

function windowResized() {
  resizeCanvas(windowWidth, windowHeight);
}

ローカルサーバーを立ち上げ、ブラウザでindex.htmlを開いたら、
以下の手順でテストしてみてください。

  1. ウェブカメラの前で前でポーズをとって、[楽しい]ボタンをクリックします。約15枚の画像を追加してみてください。
  2. ウェブカメラの前でポーズを変えて、[悲しい]ボタンをクリックします。約15枚の画像を追加してみてください。
  3. [トレーニング開始]をクリックして、トレーニングプロセスが完了するまで待ちます。 (コンソールログにプロセスが表示されます。)
  4. トレーニングが完了したら、モデルをトレーニングした2つのポーズを切り替えます。

※カメラのいろんな位置で、ポーズ画像を追加した方が精度が上がるかと思います。

  • 楽しい
    happy.png

  • 悲しい
    sad.png

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

JavaScriptでカウントダウンを作る

業務でカウントダウン機能を作る機会があったので備忘録的に。

完成物

ダウンロード.gif

html側の設定

index.html
<!DOCTYPE html>
<html>
    <head>

    </head>
    <body>
        <p>東京オリンピックまであと</p>
        <div id="result"></div>
        <script src="countdown.js"></script>
    </body>
</html>

これだけ。
bodyの最後でjsを読み込んでいるのは、後述するが、getElementByIdでresultを読み込むため。

JavaScript側の設定

countdown.js
var endDate = new Date('2020/07/24 00:00:00');
var interval = 1000;


function countdownTimer(){
  var nowDate = new Date();
  var period = endDate - nowDate ;
  var addZero = function(n){return('0'+n).slice(-2);}
  var addZeroDay = function(n){return('0'+n).slice(-3);}
  if(period >= 0) {
  var day = Math.floor(period / (1000 * 60 * 60 * 24));
  period -=  (day *(1000 * 60 * 60 * 24));
  var hour = Math.floor(period / (1000 * 60 * 60));
  period -= (hour *(1000 * 60 * 60));
  var minutes =  Math.floor(period / (1000 * 60));
  period -= (minutes * (1000 * 60));
  var second = Math.floor(period / 1000);
  var insert = "";
  insert += '<span class="h">' + addZeroDay(day) +'日' + '</span>';
  insert += '<span class="h">' + addZero(hour) + '時'+'</span>';
  insert +=  '<span class="m">' + addZero(minutes) +'分' + '</span>';
  insert += '<span class="s">' + addZero(second)+ '秒'+ '</span>';
  document.getElementById('result').innerHTML = insert;
  setTimeout(countdownTimer,10);
  }
  else{
    var insert = "";
    var number = 0;
    insert += '<span class="h">' + number + number + '</span>';
    insert +=  '<span class="m">' + number + number + '</span>';
    insert += '<span class="s">' + number + number + '</span>';
    document.getElementById('result').innerHTML = insert;
  }
}

countdownTimer();

少し解説。

endDateに目標となる日付を与え、それをnowDateと引き算してあげることで目標の日付までの差分を出す。しかしこれだけじゃうまく日や時間、分などに別れていないため、それぞれ分けてあげる。

countdown.js
  var day = Math.floor(period / (1000 * 60 * 60 * 24));
  period -=  (day *(1000 * 60 * 60 * 24));
  var hour = Math.floor(period / (1000 * 60 * 60));
  period -= (hour *(1000 * 60 * 60));
  var minutes =  Math.floor(period / (1000 * 60));
  period -= (minutes * (1000 * 60));
  var second = Math.floor(period / 1000);

ここらへんは、periodをそれぞれの単位に合わせている。

countdown.js
var insert = "";
  insert += '<span class="h">' + addZeroDay(day) +'日' + '</span>';
  insert += '<span class="h">' + addZero(hour) + '時'+'</span>';
  insert +=  '<span class="m">' + addZero(minutes) +'分' + '</span>';
  insert += '<span class="s">' + addZero(second)+ '秒'+ '</span>';
  document.getElementById('result').innerHTML = insert;

変数にそれぞれの単位を格納した後は、実際に表示させるためにinsertに入れていく。
spanにそれぞれの値を挟むことで、数字の装飾が出来るようになっている。

しかし、そのままの変数で表示しようとすると、1桁の数字の時にサイズが変わって、レイアウトが崩れてしまう。そこでaddZeroを使っている。

countdown.js
  var addZero = function(n){return('0'+n).slice(-2);}
  var addZeroDay = function(n){return('0'+n).slice(-3);}

addZeroDayは、addZeroだけだと3桁の表示の時にレイアウトが崩れてしまうと思ったので、3桁を表示することがあるdayだけに適用している。

書きなぐっただけなので、適宜わかりやすく書き直します....

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

グローバルインストールなしで nodemon を使用する

はじめに

Node.jsで開発中、修正が終わるたびに再起動するはなかなかの手間になり、
ストレスがたまります。そこでソース修正後に自動再起動してくれるnodemonの登場です。

nodemonはグローバルにインストールして使われることが多いようですが、
今回はnodemonをローカルインストールで動かす方法を調べてみました。

インストール

$ npm install --save-dev nodemon

この例では開発依存としてインストールしました。
--save-dev の代わりに --save を使用して、通常の依存としてインストールしても問題ありません。

package.jsonを編集

その後、package.jsonを以下のようにを編集します。

package.json
"scripts": {
  "start": "nodemon index.js"
}, 

index.jsは適宜実行したいファイル名に置き換えてください。

サーバーを起動

$ npm start

以上で、ファイル修正後にnodemonが自動で再起動してくれるようになりました。

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

[Vue.js] IME入力時にEnterキーイベントを発火しない様にする

Vue.js ^2.5.16 においてIME-ON時にも keydown.enter が発火する様になったらしいので
それを制御するお話
備忘録程度に

対策

  • compositionstart compositionend を使いましょう

Source
https://forum.vuejs.org/t/how-does-vue-handle-real-enter-event/2169/4

See the Pen YgPBye by ?あんじぇあーと? (@shinomiya_ag) on CodePen.

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

JavaScriptのコーディング面接した際の出題問題(Sort)

JavaScriptのコーディング面談で下記のような問題がでました。
緊張すると頭が真っ白になって、実装できなかったです。
帰りの電車で考えたらあっさりできました。。

問題

時間は5〜10分ぐらいです。

const data = `pineapple,160
apple,80
watermelon,160
grape,160
melon,80
mango,170
banana,80
strawberry,170
peach,160
orange,170
kiwi,80
`

// printSortedDataに処理を記載して、shoud print以下の内容が表示されるようにする
// 要件はdataの金額でソートする。同一金額はdataの記載順で表示する。
// dataは改行区切りの文字列である
const printSortedData = (items) => {
}

printSortedData(data)

// should print
// apple,80
// melon,80
// banana,80
// kiwi,80
// pineapple,160
// watermelon,160
// grape,160
// peach,160
// mango,170
// strawberry,170
// orange,170

実装例

もっと良い方法や書き方があれば、コメントや編集リクエストを頂けると幸いです。
paizaさんのオンラインエディタとかですぐ確認できて便利でした。
https://paiza.io/ja/projects/new

実装例
const printSortedData = items => {
  const array_data = items.split("\n").slice(0, -1);
  const sort_data = array_data.slice().sort(function(a, b) {
    const a_price = Number(a.split(",")[1]);
    const b_price = Number(b.split(",")[1]);
    if (a_price < b_price) return -1;
    if (a_price > b_price) return 1;
    return 0;
  });
  // console.log(array_data)
  // console.log(sort_data)
  result = sort_data.join("\n");
  console.log(result);
};

所感

初めてのコーディング面談でしたが、日頃から手を動かしてプログラミングしているかが重要であると感じました。
悔しいので、アルゴリズム問題をちょくちょく解いていこうと思います。

追記

つとむ( @TsutomuNakamura )さんがbashで書いてくれました!!
bashだと一行で書けるらしいです。スゴイです。
なおとさんの「コーディング面接した際の出題問題(Sort)」をbash で解いてみた

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

最近知った JavaScript のメソッドや記法の話

概要

こんにちは TOMMY です。
タイトルのとおりですが今日は JavaScript の話です。ぶっちゃけ別に大した内容じゃないんですが、最近こんなメソッドや記法を知って地味に便利で感動しているっていう話を書きたいと思います。

String.prototype.padStart()

String.prototype.padStart() - JavaScript | MDN

基本形
"1".padStart(2, 0); //=> "01"
"12".padStart(2, 0); //=> "12"

↓パッと思いつく使い方としては、日付などの 0 埋めなどでしょうか。

月を0埋めしてみる例
const date = new Date();
console.log(
  (date.getMonth() + 1).toString().padStart(2, 0)
); //=> "02"

ちなみに 0 埋めだけなら slice を使う手もあります。わりとこっちも好きです。
String.prototype.slice() - JavaScript | MDN

sliceを使った例
const date = new Date();
console.log(
  ("0" + (date.getMonth() + 1)).slice(-2)
); //=> "02"

ただ、前者のほうが英単語的に何をやっているかが直感的でわかりやすいかと思います。ん?直感的か?と思った方は、詰め物の pad (パッド) って考えればピンと来るんじゃないでしょうか。肩パッドとか、胸パッドとか(ぁ

Template literals (Template strings)

Template literals (Template strings) - JavaScript | MDN

こんなかんじでバッククオートを使うと文字列に式を埋め込むことができます。

使用例1-メソッド呼び出し
console.log(`現在は${new Date().getFullYear()}年です。`);
//=> 現在は2019年です。
使用例2-変数
const nowYear = new Date().getFullYear();
console.log(`現在は${nowYear}年です。`);
//=> 現在は2019年です。
使用例3-演算など
console.log(`現在は${2017 + 1 + 1}年です。`);
//=> 現在は2019年です。

下記はES5以前でよくやっていた文字列を連結する方法です。

ES6以前からある文字列連結
const nowYear = new Date().getFullYear();
console.log("現在は" + nowYear + "年です。");
//=> 現在は2019年です。

数が少なければこれでもいいんですが数が増えたり、区切りが細かくなってくると差は歴然としてくるとおもいます。

百聞は一見に如かずということでこちらをご覧ください。

テンプレートリテラルと従来の文字列連結の比較
//テンプレートリテラル
const template = `現在は${nowYear}${month}${date}日(${day})です。`
//従来の文字列連結
const concat   = "現在は" + nowYear + "年" + month + "月" + date + "日" + day + "です。"

明らかにテンプレートリテラルのほうが読みやすいかと思います。ついでに後者は書くのも面倒ですし、油断すると文字列の閉じクオート忘れたりもして余計面倒です(僕だけか

Support

今日紹介したものはどちらもIEはダメです。IEでどうしても使いたい場合は Babel 等によるトランスパイル必須です。IEはほんとWEB開発者泣かせですね:scream:

それにしても ES6 にさわりはじめたころは Babel が手放せなかったんですが、近頃は最新版の Chrome や Firefox なら軒並み素で動くようになってきましたね。

もしご存知なかったという方がいらっしゃいましたら、けっこう便利なので機会があればぜひ使ってみてください。

それでは。

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

Googleスプレッドシート - ボタンを押すだけでメールを送信する機能

今回はタイトル通り、
GASの記述ルールやお作法、変数、定数、型、配列や演算子など様々な基本を複数回に分けて書いていきます!

以下様々なGASの記事を紹介してます!
https://bzbot.work/

紹介記事

今回紹介している記事は以下です!
https://bzbot.work/2019/02/01/spreadsheet-email-1/

GAS
function goGoGmail() {
  var sheet = SpreadsheetApp.openById('****************');
  var add = sheet.getSheetByName('メールテンプレ').getRange(1,2).getValue();
  var popUp = Browser.msgBox("送信確認!", add + "へ送信しますか?", Browser.Buttons.OK_CANCEL);
  if (popUp == 'ok') {
    var mailSubject = sheet.getSheetByName('メールテンプレ').getRange(2,2).getValue();
    var mailBody = sheet.getSheetByName('メールテンプレ').getRange(3,2).getValue();
    GmailApp.sendEmail(add, mailSubject, mailBody);
    Browser.msgBox("送信しました");
  } else {
    Browser.msgBox("キャンセルしました");
  }
}

var sheet = SpreadsheetApp.openById('****************');
にはスプレッドシートのID部分を指定することができます。
それ以外にもActiveのシートとして別の記述をする方法もあります!

Toには送信先アドレスをセットします。
Subjectには送りたい件名をセットします。
Bodyには送信したい内容をセットします。

送信用シート

image.png

送信確認は誤送信を防ぐ意味でもポップアップで通知をしています。

送信確認

image.png

最後に送信の完了確認が取れました。

送信完了

image.png

どういった場面でManualで送信をする方法でやりたいか?ですが、
これは結構現場によりけりですが、メールフォーマットでスクレイピングをする様なビジネスだったり、メールの文面から判断する様なシステムがあったときに、一度セットしておけばフォーマット変更がない限りずっと使えます。

私も仕事でそういった場面があったときに都度送ったりしてましたがどこかに向き先とか指定できるツールがあればなってことでつくってみました。

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

ワイプスライダーに機能を追加し、プラグイン化してみた

こちらの記事で作ったスライダーに機能を追加し、プラグインとして使えるように拡張しました。
サンプル

使い方

ファイルはGithubから一式ダウンロードできます。
https://github.com/Kackie/wipeSlider
※test.htmlは旧バージョン。

HTML

外部ファイル読み込み

<link rel="stylesheet" href="css/wipeSlider.css">
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="script/jquery.wipeSlider.js"></script>

スライド用のCSSとjQuery、プラグインのJSを読み込みます。
CSSは開いてみればわかりますがすごくシンプルです。必須なのはこれだけです。
ページャー等の見た目の調整はサンプルのHTMLに直書きしています。参考にしてもらっても、自由にスタイルを書くのもよいと思います。

スライド部分

<div class="slidesWrap js_wiper">
    <ul class="slides">
        <li class="slide">
            <img src="https://dummyimage.com/980x500/ccc/fff&text=1" alt="">
        </li>
        <li class="slide">
            <img src="https://dummyimage.com/980x500/ccc/fff&text=2" alt="">
        </li>
        <li class="slide">
            <img src="https://dummyimage.com/980x500/ccc/fff&text=3" alt="">
        </li>
    </ul><!-- / .slides -->
</div><!-- / .js_wiper -->

一番シンプルな形です。
階層が守られていればulタグ以外でも構いません。

jQuery

$('.js_wiper').wipeSlider();

最低限これだけ書けば動きます。

$('.js_wiper').wipeSlider({
    transition : 1000,
    auto : true
});

オプションを設定するとこんな感じです。

オプション設定

ラベル 機能
transition スライドが切り替わるときの速度を設定します。
初期値は500。
auto 自動再生を設定します。
初期値はfalse。
duration 自動再生時、スライドの表示時間を設定します。
初期値は4000。
pager ページャーを生成します。
初期値はtrue。
controls 前後のスライドに切り替えるコントローラーを生成します。
初期値はtrue。

その他の仕様

表示中のスライドにはactiveクラスが付きます。
これを使用して、スライド内のインタラクション作成に役立ててもらえればと思います。

前回のバージョンでは、別タブでページを閲覧してから戻ってくると、アニメーションがおかしくなってしまうことがありました(setIntervalの仕様ですが)。
タブが非アクティブ時にはclearIntervalがかかり、戻ってくるとsetIntervalが再発動するようになっています。

感想と反省

プラグイン自作初挑戦です。意外と簡単ですね。
前回作った元のスライダーがシンプル過ぎたので、最低限スライダーのプラグインに必要になるであろう機能を追加しておきました。
表示中のものより前のスライドに移動したときは逆方向にアニメーションさせたりとか、ユーザー設定でアニメーション方向を自由に帰らるようにしたかったのですが、アニメーションをCSSのtransitionでやっているがためにclipの再設定ができないという問題に直面し断念。。
backFlagという変数をつくり、戻る動作のときはbackFlag = true;となるようにしたもののそのままうまく扱えていないです。
keyframesを動的生成はできなさそうだし、やるとしたらanimateメソッドのstepを使うことになるのだろうか・・?
時間がかかりそうだったのでひとまず公開してしまいましたが、これはなんとか作ってみたいのでじっくり考えてみることにします。

せっかくなのでクレジットを入れようと思ったのですが書き方がわかりませんでした(恥)。

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

ANGULAR.JS公式チュートリアルのPhoneCat Tutorial Appはじめました。

ANGULAR.JS公式チュートリアルのPhoneCat Tutorial Appはじめました。

なぜ始めたのか?

業務でJavascriptのwindowオブジェクトの操作とJasmineのUTをやる機会が
あったので、これを機会にJavaScriptの知識を深めたいと思い、テーマを探していました。

目に留まったのがGoogleなどが開発を手掛けたAngularJSが有名!などという情報が入ってきたので、公式ページにあったチュートリアルを試してみることにしました。
電話のカタログページを使ったチュートリアルみたいですね。

参考:https://docs.angularjs.org/tutorial

ひとまず、今回は環境構築から始めたいと思いまっす。

前提

・Gitがインストールされていることを前提として進めます。

環境構築

繰り返しになりますが、今回は環境構築がメインです。

まずはangular-phoncatのリポジトリをクローンして
クローンしたフォルダをカレントディレクトリにします。

git clone --depth=16 https://github.com/angular/angular-phonecat.git
cd angular-phonecat

--depth=16のオプションは最新16件分のコミットのみ抽出するらしいです。
ダウンロード速度を考慮して設定してるみたいですね。

次に依存関係を解決するためにNode.jsをインストールします。
これは事前設定されているローカルWEBサーバの実施に使うみたいです。

インストーラはOS毎に分かれているので公式ページから適切なものをダウンロードしてインストールしてください。

参考:https://nodejs.org/en/download/

Node.JSについてよくわからなかったので調べてみたところ、サーバでJavaScriptを実行できるようにするための
プラットフォームらしいです。
まずは使ってみて雰囲気つかんでみて、気になるとこがあったら詳細に調べてみたいと思います。

インストールが完了したらコマンドラインで次のコマンドを叩いて正常にインストールされた事を確認します。
ちなみに、2019年2月26日現在ではv10.15.1が推奨される最新のNode.jsです。
11.10.0も公開されていましたが、安定している方を選びます。

node --version

Node.jsをインストールしたらnpmも取得されます。
npmはパッケージ管理ツールでNode.jsのパッケージを管理するのに使用します。
まずは依存関係をインストールしてみます。

npm install

完了したら実際にWEBサーバを起動してみましょう!
デフォルトの設定ではポート8000をリスナーとするlocalhostが作成されるはずです。

npm start

起動に成功したらhttp://localhost:8000/index.htmlにアクセスしてみましょう。
こ、これがAngular.jsを使ったページか!めっちゃカッコいい画面やんけ!って画面が表示されれば成功です。

さて、まだ環境構築は終わりではありません。
次は単体テストの機能が正常に動作するか確認します。

angular.jsはKarmaを使って単体テストするように構築されています。
さっそくKarmaを起動してみます。
起動はnpmの以下コマンドでできます。

npm test

Chromeのブラウザが起動してKarmaの画面が表示されます。
今回はKarumaの勉強に焦点を当てるわけではないので、いったん此処まで。

次にテストツールのE2E(エンドツーエンド)の確認です。
このプロダクトはユーザとブラウザの対話をシュミレートすることでテストします。

WEBサーバが起動した状態で、別のコマンドラインを起動して、次のコマンドを実行します。

npm run protractor

すると、ブラウザが起動してアプリケーションを自動でテストし始めます。
検索用テキストボックスに勝手に文字が入力されて検索されたりと少し驚きますが、これによりE2Eのテストが自動化できる仕組みっぽいです。
たぶん別途specを用意する必要があるんだと思いますが、これは別の機会に勉強します。

今回はここまで!

参考:https://docs.angularjs.org/tutorial/step_01

マークダウンは後程なおします。
おやすみなさい。

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

ランダムな文脈表示

100分de名著の太宰治「斜陽」(高橋源一郎著)に感銘を受けた。
そして、そのノリのまま、webアプリを一つ作成した。

http://world-l-word.com/dazai/index.html

特徴:小説「人間失格」から、文脈をランダムに表示するもの。
データ:青空文庫から適当に。

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

JavaScriptのvar、let、constの違いを恋愛に置き換えて説明してみる。

JavaScriptの変数宣言Var,let,constについて恋愛に置き換えて説明してみました。

主人公は花子ちゃんです。

varの場合

花子ちゃんは同じクラスの太郎くんが好きだと宣言するとします。

var my_crush = "太郎くん";
console.log(my_crush); // 太郎くん;

しかし、二郎くんを好きになってしまった場合、後から宣言し直すことができます。

var my_crush = "二郎くん"; 
console.log(my_crush); // 二郎くん

letの場合

同じく花子ちゃんは同じクラスの太郎くんが好きだと宣言するとします。

let my_crush = "太郎くん";
console.log(my_crush); // 太郎くん;

次に二郎くんを好きになってしまったとしても、宣言し直すことができません。

let my_crush = "二郎くん" ;
console.log(my_crush); // エラー

しかし、ひっそりと好きな人を二郎くんにすることは可能です。

my_crush = "二郎くん";
console.log(my_crush); // 二郎くん

constの場合

上記同様、花子ちゃんは同じクラスの太郎くんが好きだと宣言するとします。

const my_crush = "太郎くん";
console.log(my_crush); // 太郎くん;

次に二郎くんを好きになってしまったとしても、let同様宣言し直すことができません。

const my_crush = "二郎くん";
console.log(my_crush); // エラー

さらにひっそりと好きな人を変えることもできません。

my_crush = "二郎くん";
console.log(my_crush); // エラー

まとめ

constは最も一途

letは見かけ上一途

varは一途ではないが、いさぎが良い

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