20200227のvue.jsに関する記事は16件です。

文系ガチの未経験だった僕が自力4ヶ月弱で自社開発のweb系会社で新卒内定をもらうために行ったこと

誰に読んで欲しいか

・文系新卒未経験の方
・web系の会社で働きたい方
・フリーランスでアソビタィ!!

偉そうに書きますがご容赦ください><

なぜ書くのか

実際に自分がエンジニアになりたいと思った時に情報が全くなかったから。
何をしなければいけないかが全くわからなかった。

web系で就職した人や未経験理系卒の方の情報は多少あれど文系未経験新卒でweb系エンジニアになった人の情報は全くなかったため。しかし文系卒のエンジニア志望は増えて来ていてニーズはかなりあると考えた。

自己紹介

早稲田大学文系3年生。
長所は考えた上で実際に行動にすぐに移せること。
大学生時代は酒飲み、遊び、そしてオーストラリアとニュージーランドでワーキングホリデーを行い、ガチガチの現地の環境に身を置いて生活をした。
その時に初めてテクノロジーに触れて衝撃を受け、ITに興味を少し持つ。
ものを作ってびっくりさせることが好き。

自分が行ったこと

3年生の夏

コンサル目指してサマーインターンを応募しまくるも落選落選。
→自己分析を重ねてどう考えても仕事よりノマドになって海外で自由に働きたいと強い決意。

10月前半

・週3働いていたマーケのインターンを辞め、飲み会などを減らし、未来への投資として勉強する。
実際にprogateという初心者向けの学習サイトで月額1000円払ってHTML,CSSを2週ほどした。
 →一周2日あれば余裕
 →月額1000円はスクールと比較すればどう考えても格安
・その後実際に自分の作ってみたいサイトを5つほどアウトプットしてみる
・その後同じものでJavaScriptを学び、難しかったので5週ほどした。

10月後半

・インターンを視野にいれて重役のいる大手コンサルでの長期インターンの条件としてあげられた制作物の作成に励む。
 ・課題の内容としてはとあるチャットアプリのAPIを使ったアプリ内メッセージ検索webアプリ
 ・AWSを使ったデプロイまで行うよう指示される
 ・文系なのでエンジニアの友人もいなかったため小学校の時の友人、MatcherというOB訪問アプリで 出会った社会人の方、前職場のエンジニアの方に再コンタクト
 ・正直かなり大変でエラー1つに数日かけることもしばしば
・Vue.jsというjavascript(js)の指定が確かあったため、Udemyというオンライン学習用動画を販売するサイトを利用し、1000円で購入し、課題と同時並行で勉強した。

11月後半

課題完成し、OKが出たが人数の関係で引き伸ばされた。
→しょぼいですがもしお時間あれば見てみてください..まだまだ改良中なのでお手柔らかに..
slackのメッセージ検索アプリ

早く実務経験を積みたかったため、諦めて自分でインターンを探し始める。
この時使ったものはMatcher,キャリアバイト,Infraというサービスたち。
まずエンジニアとしての働き方などベースを知るためにMatcherで20人ほど文系未経験卒のエンジニアの方中心に話を聞いた。

12月

その後早速実務未経験でも働けそうなインターンに応募。面接させてくれた企業は3つ。
1つ目の企業には面接中に断られる。

ここでフィードバックをお願いして教えてもらったこと
・普通に企業の目線で考えるとエンジニアを雇う意味は2つ。
 ❶即戦力で企業に利益をもたらすため
 ❷就職するかもしれないため将来を見越した研修込みの投資として

今このページを読んでいる人はおそらく❷を狙うしかない。
だから自分はそれ以降就職してもいい会社しか応募せず、面接も本気で就活のレベルで受けた。

12月後半

ゲームメディアを運営する企業で採用された。フロントでの努力を評価されバックエンドのPHPを使ったエンジニアとして研修込みでの採用。
同時にレバテックという人材サービスを利用し就活を視野に入れて自己分析をもっと極める。

1月

長期インターン開始。
後半からは面接も平日夜に毎日受ける。
→フィードバックを最大限に生かしてPDCA回して面接の受けが良くなっていく。

2月

ついに複数の企業から内定をいただく。
現在まだ一応他の企業も見ている。

すいません。日記のような形になりました。

まとめ

就職までの時間を考えて何をすべきか逆算していくことが大事。
正直自分は実務経験ないと就職できないと思ってインターンを必死に探したが、しなくてももしかしたらよかったかもしれない。
しかしインターンをして就活しながらも現在進行形で技術に触れていることをアピールできたのは大きかった所感。

インターンを受けるにあたって大事なことは
ちゃんと就職してもいい企業で受けると互いに良くて面接ももちろん通る。

就活では
・しっかりエンジニアになりたい理由を固めることは大前提必要。

・その上でそのために実際にこんなもの作って、インターンもやっている(何かしら活動している)から私はこれからももちろんエンジニアでゴリゴリ開発していきますよ?だって書くの好きだもん!ということを伝えることが絶対に必要。

おそらく面接官が心配していることは、
❶エンジニアなりたいのはわかったけど続けれるのか。文系やめる人多いけど。
❷成長は早くて遅い経験有りのエンジニアを雇うよりも将来的に利益になるのか。

ということ。
あとは文系ならでは(?)のコミュニケーション能力を面接で抜群に発揮して内定をもぎ取りましょう!!

感想

初めてのqiita投稿でした。
ずっと前から自分が情報なくて困っていたのでこういったことを内定もらったらすぐにしたいと考えていて、それが実行できたので嬉しいです。

次回は初心者から見た簡単なIT業界やエンジニアの中の詳しい職種の特徴を発信したいと思います。
もちろんコーディングの知識もアウトプットしたいと考えてます。

お手柔らかに、意見やコメントもらえると嬉しいです!
ありがとうございました。

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

文系ガチの未経験エンジニアだった僕が自力4ヶ月弱で自社開発のweb系会社で新卒内定をもらうために行ったこと

誰に読んで欲しいか

・文系新卒未経験の方
・web系の会社で働きたい方
・フリーランスでアソビタィ!!

偉そうに書きますがご容赦ください><

なぜ書くのか

実際に自分がエンジニアになりたいと思った時に情報が全くなかったから。
何をしなければいけないかが全くわからなかった。

web系で就職した人や未経験理系卒の方の情報は多少あれど文系未経験新卒でweb系エンジニアになった人の情報は全くなかったため。しかし文系卒のエンジニア志望は増えて来ていてニーズはかなりあると考えた。

自己紹介

早稲田大学文系3年生。
長所は考えた上で実際に行動にすぐに移せること。
大学生時代は酒飲み、遊び、そしてオーストラリアとニュージーランドでワーキングホリデーを行い、ガチガチの現地の環境に身を置いて生活をした。
その時に初めてテクノロジーに触れて衝撃を受け、ITに興味を少し持つ。
ものを作ってびっくりさせることが好き。

自分が行ったこと

3年生の夏

コンサル目指してサマーインターンを応募しまくるも落選落選。
→自己分析を重ねてどう考えても仕事よりノマドになって海外で自由に働きたいと強い決意。

10月前半

・週3働いていたマーケのインターンを辞め、飲み会などを減らし、未来への投資として勉強する。
実際にprogateという初心者向けの学習サイトで月額1000円払ってHTML,CSSを2週ほどした。
 →一周2日あれば余裕
 →月額1000円はスクールと比較すればどう考えても格安
・その後実際に自分の作ってみたいサイトを5つほどアウトプットしてみる
・その後同じものでJavaScriptを学び、難しかったので5週ほどした。

10月後半

・インターンを視野にいれて重役のいる大手コンサルでの長期インターンの条件としてあげられた制作物の作成に励む。
 ・課題の内容としてはとあるチャットアプリのAPIを使ったアプリ内メッセージ検索webアプリ
 ・AWSを使ったデプロイまで行うよう指示される
 ・文系なのでエンジニアの友人もいなかったため小学校の時の友人、MatcherというOB訪問アプリで 出会った社会人の方、前職場のエンジニアの方に再コンタクト
 ・正直かなり大変でエラー1つに数日かけることもしばしば
・Vue.jsというjavascript(js)の指定が確かあったため、Udemyというオンライン学習用動画を販売するサイトを利用し、1000円で購入し、課題と同時並行で勉強した。

11月後半

課題完成し、OKが出たが人数の関係で引き伸ばされた。
→しょぼいですがもしお時間あれば見てみてください..まだまだ改良中なのでお手柔らかに..
slackのメッセージ検索アプリ

早く実務経験を積みたかったため、諦めて自分でインターンを探し始める。
この時使ったものはMatcher,キャリアバイト,Infraというサービスたち。
まずエンジニアとしての働き方などベースを知るためにMatcherで20人ほど文系未経験卒のエンジニアの方中心に話を聞いた。

12月

その後早速実務未経験でも働けそうなインターンに応募。面接させてくれた企業は3つ。
1つ目の企業には面接中に断られる。

ここでフィードバックをお願いして教えてもらったこと
・普通に企業の目線で考えるとエンジニアを雇う意味は2つ。
 ❶即戦力で企業に利益をもたらすため
 ❷就職するかもしれないため将来を見越した研修込みの投資として

今このページを読んでいる人はおそらく❷を狙うしかない。
だから自分はそれ以降就職してもいい会社しか応募せず、面接も本気で就活のレベルで受けた。

12月後半

ゲームメディアを運営する企業で採用された。フロントでの努力を評価されバックエンドのPHPを使ったエンジニアとして研修込みでの採用。
同時にレバテックという人材サービスを利用し就活を視野に入れて自己分析をもっと極める。

1月

長期インターン開始。
後半からは面接も平日夜に毎日受ける。
→フィードバックを最大限に生かしてPDCA回して面接の受けが良くなっていく。

2月

ついに複数の企業から内定をいただく。
現在まだ一応他の企業も見ている。

すいません。日記のような形になりました。

まとめ

就職までの時間を考えて何をすべきか逆算していくことが大事。
正直自分は実務経験ないと就職できないと思ってインターンを必死に探したが、しなくてももしかしたらよかったかもしれない。
しかしインターンをして就活しながらも現在進行形で技術に触れていることをアピールできたのは大きかった所感。

インターンを受けるにあたって大事なことは
ちゃんと就職してもいい企業で受けると互いに良くて面接ももちろん通る。

就活では
・しっかりエンジニアになりたい理由を固めることは大前提必要。

・その上でそのために実際にこんなもの作って、インターンもやっている(何かしら活動している)から私はこれからももちろんエンジニアでゴリゴリ開発していきますよ?だって書くの好きだもん!ということを伝えることが絶対に必要。

おそらく面接官が心配していることは、
❶エンジニアなりたいのはわかったけど続けれるのか。文系やめる人多いけど。
❷成長は早くて遅い経験有りのエンジニアを雇うよりも将来的に利益になるのか。

ということ。
あとは文系ならでは(?)のコミュニケーション能力を面接で抜群に発揮して内定をもぎ取りましょう!!

感想

初めてのqiita投稿でした。
ずっと前から自分が情報なくて困っていたのでこういったことを内定もらったらすぐにしたいと考えていて、それが実行できたので嬉しいです。

次回は初心者から見た簡単なIT業界やエンジニアの中の詳しい職種の特徴を発信したいと思います。
もちろんコーディングの知識もアウトプットしたいと考えてます。

お手柔らかに、意見やコメントもらえると嬉しいです!
ありがとうございました。

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

【RefreshToken】Vue.jsでAccessTokenのリフレッシュを行う共通処理

はじめに

APIリクエストの際にアクセストークンの有効期限が切れていた場合に、トークンのリフレッシュを行い再度本来のAPIリクエストを行う処理のサンプルです。

前提

トークンの保持はcookieで行っています。トークンの発行はflask_jwt_extendedというライブラリを使用しています。サーバーサイドの仕様によってリクエストは適宜変更してください。
axiosのバージョンは0.18.X系です。0.19.X系は共通処理に記述したwithCredentialsがうまく動作しないバグがあるみたいです。

App.vueで下記のaxios.js読み込んで使用します。

コード

axios.js
import Axios from 'axios'
import Vue from 'vue'

const http = Axios.create({
    // 共通定義
    baseURL: 'http://localhost:5000/api/v1',
    withCredentials: true,
    headers: {
        "Content-Type": "application/json",
        "X-Requested-With": "XMLHttpRequest"
    }
})

http.interceptors.response.use((response) => {
    // APIのリクエストに成功した場合はそのまま結果を返す
    return response;
}, error => {
    // エラーかつトークン切れ(401)の場合
    if (error.config && error.response && error.response.status === 401) {
        // トークンリフレッシュのエンドポイントにリクエスト
        http
            .post(
                "/token/refresh",
                {},
                {
                    withCredentials: true,
                    headers: {
                        // flask_jwt_extendedの仕様
                        // CSRF対策。サーバサイドの実装によっては不要
                        "X-CSRF-TOKEN": Vue.$cookies.get("csrf_refresh_token")
                    }
                }
            )
            .then(res => {
                if (res.status == 200) {
                    // トークンの更新に成功
                    console.log("success token refresh");
                    const config = error.config;
                    // flask_jwt_extendedの仕様
                    // CSRF対策。新しく取得したアクセストークンに対応するCSRF対策文字列を設定。
                    // Authorizationヘッダーにトークンをセットする仕様の場合はここでトークンセット。
                    // 例:config.headers["Authorization"] = "bearer" + 取得したトークン
                    config.headers["X-CSRF-TOKEN"] = Vue.$cookies.get("csrf_access_token");
                    // 本来行いたかったAPIリクエストを再実行
                    return Axios.request(error.config);
                } else {
                    // トークンの更新に失敗した場合
                    console.log("failure token refresh");
                    // 現在の画面のパスをqueryにbackuriとしてセット。
                    // ログイン時にbackuriが存在していたらその画面に戻るように実装している場合のみセット。
                    // ただログイン画面に戻るだけならqueryは不要。
                    Vue.$router.push({
                        path: "login",
                        query: { backuri: Vue.$route.path }
                    });
                }
            })
            .catch(error => {
                console.log("failure token refresh");
                // エラーの場合もログイン画面に戻る
                Vue.$router.push({
                    path: "login",
                    query: { backuri: Vue.$route.path }
                });
            });
    }
    // エラー終了時にPromiseを返す
    return Promise.reject(error);
});
export default http
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AIに歌を聞かせた。そした彼は笑顔?になった。(COTOHA-情分析API)

概要

歌を聞かせた。
彼はその歌を聴き、
Positiveな感情になると笑い?、
Negativeな感情になると怒り?、
Neutralな感情になると。。。?

COTOHA感情分析APIを使って、歌の歌詞からどのような感情をくみ取るのかを試した。COTOHA API for Developersに登録してすぐに、感情分析APIを試せるようWEBアプリも作った。)

結果

とりあえず歴代CDシングル売り上げ枚数ランキングを聞かせた。
「1位から順に!カウントアップ!?」

1.(457.7万枚)およげ!たいやきくん

?
"sentiment": "Negative"
"score": 0.5

Response
{
  "result": {
    "sentiment": "Negative",
    "score": 0.5,
    "emotional_phrase": [
      {
        "form": "こころがはずむ",
        "emotion": "喜ぶ"
      },
      {
        "form": "たのしい",
        "emotion": "P"
      },
      {
        "form": "おもい",
        "emotion": "N"
      },
      {
        "form": "ひろいぜ",
        "emotion": "PN"
      },
      {
        "form": "いじめられる",
        "emotion": "N"
      },
      {
        "form": "もがいて",
        "emotion": "N"
      },
      {
        "form": "こげある",
        "emotion": "N"
      },
      {
        "form": "うまそう",
        "emotion": "P"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

2.(325.6万枚)女のみち

?
"sentiment": "Negative"
"score": 0.2011977885862898

Response
{
  "result": {
    "sentiment": "Negative",
    "score": 0.2011977885862898,
    "emotional_phrase": [
      {
        "form": "みちならば暗い",
        "emotion": "N"
      },
      {
        "form": "恋",
        "emotion": "好ましい,切ない"
      },
      {
        "form": "幸せ",
        "emotion": "P"
      },
      {
        "form": "つらい",
        "emotion": "N"
      },
      {
        "form": "泣",
        "emotion": "喜ぶ,悲しい,切ない"
      },
      {
        "form": "すがって泣いたうぶな",
        "emotion": "PN"
      },
      {
        "form": "いけない",
        "emotion": "N"
      },
      {
        "form": "みちならば",
        "emotion": "PN"
      },
      {
        "form": "捨てた",
        "emotion": "N"
      },
      {
        "form": "いじめる",
        "emotion": "N"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

3.(313.2万枚)世界に一つだけの花

?
"sentiment": "Positive"
"score": 0.08995079776388203

Response
{
  "result": {
    "sentiment": "Positive",
    "score": 0.08995079776388203,
    "emotional_phrase": [
      {
        "form": "うれしそうな",
        "emotion": "P"
      },
      {
        "form": "笑顔",
        "emotion": "安心"
      },
      {
        "form": "好み",
        "emotion": "好ましい"
      },
      {
        "form": "困った",
        "emotion": "N"
      },
      {
        "form": "迷",
        "emotion": "不安"
      },
      {
        "form": "笑い",
        "emotion": "喜ぶ"
      },
      {
        "form": "もともと特別な",
        "emotion": "PN"
      },
      {
        "form": "ひとそれぞれ",
        "emotion": "PN"
      },
      {
        "form": "きれいだ",
        "emotion": "P"
      },
      {
        "form": "争う",
        "emotion": "N"
      },
      {
        "form": "誇らしげ",
        "emotion": "P"
      },
      {
        "form": "咲かせる",
        "emotion": "P"
      },
      {
        "form": "一生懸命",
        "emotion": "PN"
      },
      {
        "form": "頑張って",
        "emotion": "P"
      },
      {
        "form": "仕方ないね",
        "emotion": "N"
      },
      {
        "form": "色とりどり",
        "emotion": "P"
      },
      {
        "form": "気づかない",
        "emotion": "N"
      },
      {
        "form": "小さい",
        "emotion": "PN"
      },
      {
        "form": "同じものはない",
        "emotion": "PN"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

4.(293.6万枚)TSUNAMI

?
"sentiment": "Negative"
"score": 0.10121480950551423

Response
{
  "result": {
    "sentiment": "Negative",
    "score": 0.10121480950551423,
    "emotional_phrase": [
      {
        "form": "愛",
        "emotion": "安心,好ましい"
      },
      {
        "form": "愛しい",
        "emotion": "P"
      },
      {
        "form": "怯えてる",
        "emotion": "不安"
      },
      {
        "form": "悲しみ",
        "emotion": "悲しい"
      },
      {
        "form": "恋",
        "emotion": "好ましい,切ない"
      },
      {
        "form": "好きな",
        "emotion": "P"
      },
      {
        "form": "戸惑う",
        "emotion": "N"
      },
      {
        "form": "泣いた",
        "emotion": "N"
      },
      {
        "form": "涙",
        "emotion": "喜ぶ,不安,悲しい,切ない"
      },
      {
        "form": "微笑",
        "emotion": "安心"
      },
      {
        "form": "弱気な",
        "emotion": "N"
      },
      {
        "form": "すがる",
        "emotion": "N"
      },
      {
        "form": "涙もろい",
        "emotion": "PN"
      },
      {
        "form": "清か",
        "emotion": "P"
      },
      {
        "form": "魔性",
        "emotion": "N"
      },
      {
        "form": "彷徨う",
        "emotion": "N"
      },
      {
        "form": "枯れる",
        "emotion": "N"
      },
      {
        "form": "素直に",
        "emotion": "P"
      },
      {
        "form": "侘しさ",
        "emotion": "PN"
      },
      {
        "form": "鏡のような",
        "emotion": "P"
      },
      {
        "form": "深い",
        "emotion": "PN"
      },
      {
        "form": "気付いてる",
        "emotion": "P"
      },
      {
        "form": "張り裂けそうな",
        "emotion": "N"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

5.(291.8万枚)だんご3兄弟

?
"sentiment": "Negative"
"score": 0.06456371454543736

Response
{
  "result": {
    "sentiment": "Negative",
    "score": 0.06456371454543736,
    "emotional_phrase": [
      {
        "form": "かたくな",
        "emotion": "不安"
      },
      {
        "form": "ささって",
        "emotion": "P"
      },
      {
        "form": "まれ",
        "emotion": "PN"
      },
      {
        "form": "たくさん",
        "emotion": "PN"
      },
      {
        "form": "こげ",
        "emotion": "N"
      },
      {
        "form": "かたくなりました",
        "emotion": "PN"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

6.(289.5万枚)君がいるだけで

?
"sentiment": "Positive"
"score": 0.009547567550434202

Response
{
  "result": {
    "sentiment": "Positive",
    "score": 0.009547567550434202,
    "emotional_phrase": [
      {
        "form": "憧れ",
        "emotion": "P"
      },
      {
        "form": "笑顔",
        "emotion": "安心"
      },
      {
        "form": "くやしい",
        "emotion": "N"
      },
      {
        "form": "何より大切な",
        "emotion": "P"
      },
      {
        "form": "涙",
        "emotion": "喜ぶ,切ない,不安,悲しい"
      },
      {
        "form": "儚い",
        "emotion": "PN"
      },
      {
        "form": "強く",
        "emotion": "PN"
      },
      {
        "form": "気付かせてくれたね",
        "emotion": "P"
      },
      {
        "form": "ありがちな",
        "emotion": "PN"
      },
      {
        "form": "つい引き込まれ",
        "emotion": "P"
      },
      {
        "form": "弱さ",
        "emotion": "N"
      },
      {
        "form": "忘れてた",
        "emotion": "PN"
      },
      {
        "form": "もっと素直になれなかった",
        "emotion": "N"
      },
      {
        "form": "わかって",
        "emotion": "P"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

7.(282.2万枚)SAY YES

?
"sentiment": "Negative"
"score": 0.2490204168421342

Response
{
  "result": {
    "sentiment": "Negative",
    "score": 0.2490204168421342,
    "emotional_phrase": [
      {
        "form": "愛",
        "emotion": "好ましい,安心"
      },
      {
        "form": "愛してる",
        "emotion": "P"
      },
      {
        "form": "恋",
        "emotion": "好ましい,切ない"
      },
      {
        "form": "寂しい",
        "emotion": "N"
      },
      {
        "form": "切なさ",
        "emotion": "PN"
      },
      {
        "form": "迷",
        "emotion": "不安"
      },
      {
        "form": "ワガママ",
        "emotion": "N"
      },
      {
        "form": "余計な",
        "emotion": "N"
      },
      {
        "form": "何げなく暮らさない",
        "emotion": "PN"
      },
      {
        "form": "あふれてる",
        "emotion": "N"
      },
      {
        "form": "勝てない",
        "emotion": "N"
      },
      {
        "form": "消えない",
        "emotion": "P"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

8.(276.6万枚)Tomorrow never knows

?
"sentiment": "Positive"
"score": 0.007165278534051281

Response
{
  "result": {
    "sentiment": "Positive",
    "score": 0.007165278534051281,
    "emotional_phrase": [
      {
        "form": "愛した",
        "emotion": "P"
      },
      {
        "form": "悲しい",
        "emotion": "N"
      },
      {
        "form": "孤独な",
        "emotion": "PN"
      },
      {
        "form": "寂しい",
        "emotion": "N"
      },
      {
        "form": "愛される喜び",
        "emotion": "P"
      },
      {
        "form": "消えた帰らぬ",
        "emotion": "P"
      },
      {
        "form": "すれ違う",
        "emotion": "N"
      },
      {
        "form": "無邪気に",
        "emotion": "P"
      },
      {
        "form": "裏切れる",
        "emotion": "N"
      },
      {
        "form": "欲しがっていた",
        "emotion": "P"
      },
      {
        "form": "分かり合えた",
        "emotion": "P"
      },
      {
        "form": "夢中で駆け抜ける",
        "emotion": "P"
      },
      {
        "form": "勝利",
        "emotion": "P"
      },
      {
        "form": "敗北もない",
        "emotion": "P"
      },
      {
        "form": "忘れてゆく",
        "emotion": "PN"
      },
      {
        "form": "避けて通れない",
        "emotion": "P"
      },
      {
        "form": "果てしない",
        "emotion": "P"
      },
      {
        "form": "優しさ",
        "emotion": "P"
      },
      {
        "form": "長い",
        "emotion": "PN"
      },
      {
        "form": "癒える",
        "emotion": "P"
      },
      {
        "form": "少し",
        "emotion": "PN"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

9.(258.8万枚)ラブ・ストーリーは突然に

?
"sentiment": "Positive"
"score": 0.060429544970368884

Response
{
  "result": {
    "sentiment": "Positive",
    "score": 0.060429544970368884,
    "emotional_phrase": [
      {
        "form": "もっと好き",
        "emotion": "P"
      },
      {
        "form": "もう心揺れたりしないで切ない",
        "emotion": "PN"
      },
      {
        "form": "揺れたり",
        "emotion": "興奮"
      },
      {
        "form": "いい",
        "emotion": "P"
      },
      {
        "form": "分からない",
        "emotion": "N"
      },
      {
        "form": "消えてゆく",
        "emotion": "N"
      },
      {
        "form": "あんまりすてきだ",
        "emotion": "P"
      },
      {
        "form": "甘く",
        "emotion": "PN"
      },
      {
        "form": "やわらかく",
        "emotion": "PN"
      },
      {
        "form": "心が動いた",
        "emotion": "P"
      },
      {
        "form": "忘れない",
        "emotion": "PN"
      },
      {
        "form": "心揺れたりしないで",
        "emotion": "PN"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

10.(248.9万枚)LOVE LOVE LOVE

?
"sentiment": "Positive"
"score": 0.24542462771149284

Response
{
  "result": {
    "sentiment": "Positive",
    "score": 0.24542462771149284,
    "emotional_phrase": [
      {
        "form": "愛",
        "emotion": "安心,好ましい"
      },
      {
        "form": "愛してる",
        "emotion": "P"
      },
      {
        "form": "すごく好きな",
        "emotion": "P"
      },
      {
        "form": "涙",
        "emotion": "喜ぶ,切ない,不安,悲しい"
      },
      {
        "form": "すっごく",
        "emotion": "PN"
      },
      {
        "form": "うまく",
        "emotion": "P"
      },
      {
        "form": "願う",
        "emotion": "P"
      },
      {
        "form": "少しずつ思い出になって",
        "emotion": "P"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

番外編(何となく気になった曲)

大きなのっぽの古時計

?
"sentiment": "Positive"
"score": 0.30601098537139876

Response
{
  "result": {
    "sentiment": "Positive",
    "score": 0.30601098537139876,
    "emotional_phrase": [
      {
        "form": "うれしい",
        "emotion": "P"
      },
      {
        "form": "かなしい",
        "emotion": "N"
      },
      {
        "form": "のっぽの",
        "emotion": "PN"
      },
      {
        "form": "ごじまんの",
        "emotion": "PN"
      },
      {
        "form": "きれいな",
        "emotion": "P"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

千の風になって

?
"sentiment": "Positive"
"score": 0.14780720679775194,

Response
{
  "result": {
    "sentiment": "Positive"
    "score": 0.14780720679775194,
    "emotional_phrase": [
      {
        "form": "泣かないでください",
        "emotion": "P"
      },
      {
        "form": "きらめく",
        "emotion": "P"
      },
      {
        "form": "見守る",
        "emotion": "P"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

パプリカ

?
"sentiment": "Positive"
"score": 0.01385186275421083

Response
{
  "result": {
    "sentiment": "Positive",
    "score": 0.01385186275421083,
    "emotional_phrase": [
      {
        "form": "泣いてた",
        "emotion": "N"
      },
      {
        "form": "喜び",
        "emotion": "P"
      },
      {
        "form": "晴れた",
        "emotion": "P"
      },
      {
        "form": "燻り",
        "emotion": "N"
      },
      {
        "form": "慰める",
        "emotion": "P"
      },
      {
        "form": "まれ",
        "emotion": "PN"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

WEBアプリ化

COTOHA API for Developersに登録した人限定ではあるが、同じ遊びが出来るようにした。(簡単に登録可)
以下URLで遊ぶには、登録後に取得できる、Client IDClient secretが必要。
https://cotoha-demo.now.sh/

2020-02-26_17h32_04.png

Client IDClient secretは、画面リフレッシュのたびに入力しなおすのは面倒。なので、ブラウザのlocalstrageにて情報を保持している。

技術

サービス化に伴い、以下の技術を使用。

  • Nuxt.js【JavaScript framework】
  • ant-design-vue【Css framework】
  • Now【Deploy】
  • Express【API】

server.js
server.js
const express = require('express')
const request = require('request')
const app = express()

// server
const port = process.env.PORT || 5000
app.listen(port, err => {
    if (err) throw err
    console.log(`> Ready On Server http://localhost:${port}`)
})

// API
app.get('/getToken', function(req, res, next) {
    (() => {
        return new Promise(r => {
            if (req.query.ClientID && req.query.Clientsecret && req.query.AccessTokenPublishURL){
                const AccessTokenPublishURL = req.query.AccessTokenPublishURL
                const ClientID = req.query.ClientID
                const Clientsecret = req.query.Clientsecret
                const headers = {
                    'Content-Type': 'application/json'
                }
                const data = {
                    'grantType': 'client_credentials',
                    'clientId': ClientID,
                    'clientSecret': Clientsecret
                }
                const options = {
                    url: AccessTokenPublishURL,
                    method: 'POST',
                    headers: headers,
                    json: data
                }
                request(options, (error, response, body) => {
                    return r(body)
                })
            }else{
                r('Parameter is incorrect')
            }
        })
    })().then(data => {
        res.send(data)
    }).catch(error => {
        res.send(error)
      })
})

app.get('/cotohaApiSimilarity', function(req, res, next) {
    (() => {
        return new Promise(r => {
            if (req.query.sentence && req.query.token){
                const sentence = req.query.sentence
                const token = req.query.token
                const headers = {
                    'Content-Type': 'application/json;charset=UTF-8',
                    'Authorization': `Bearer ${token}`
                }
                const data = {
                    'sentence': sentence,
                }
                const options = {
                    url: 'https://api.ce-cotoha.com/api/dev/nlp/v1/sentiment',
                    method: 'POST',
                    headers: headers,
                    json: data
                }
                request(options, (error, response, body) => {
                    return r(body)
                })
            }else{
                r('Parameter is incorrect')
            }
        })
    })().then(data => {
        res.send(data)
    }).catch(error => {
        res.send(error)
      })
})

あとがき

他のAPIと組み合わせれば、話す内容によって表情を変えるbotが簡単に出来そう。

あと要約(β)API・キーワード抽出API・言い淀み除去(β)・音声認識誤り検知(β)・音声認識API ここら辺のを使って、音声データから議事録を作るWEBアプリ(WEBサービス)作りたい。。。時間が欲しい。。。

おまけ

Q. さて、これは誰が歌っているのさくらの結果でしょうか???↓

さくら

?
"sentiment": "Positive",
"score": 0.0010436678748683892,

Response
{
  "result": {
    "sentiment": "Positive",
    "score": 0.0010436678748683892,
    "emotional_phrase": [
      {
        "form": "また気になる",
        "emotion": "PN"
      },
      {
        "form": "忘れた",
        "emotion": "PN"
      },
      {
        "form": "淡い",
        "emotion": "PN"
      },
      {
        "form": "意味なく",
        "emotion": "N"
      },
      {
        "form": "変わらない",
        "emotion": "PN"
      },
      {
        "form": "甦る",
        "emotion": "PN"
      },
      {
        "form": "長い",
        "emotion": "PN"
      },
      {
        "form": "たわいない",
        "emotion": "PN"
      },
      {
        "form": "気付けば",
        "emotion": "P"
      },
      {
        "form": "誘われ",
        "emotion": "P"
      },
      {
        "form": "すり抜けた",
        "emotion": "PN"
      },
      {
        "form": "分からなかった",
        "emotion": "N"
      },
      {
        "form": "若かった",
        "emotion": "P"
      },
      {
        "form": "香る",
        "emotion": "P"
      },
      {
        "form": "暖かい",
        "emotion": "P"
      },
      {
        "form": "こぼれる",
        "emotion": "N"
      },
      {
        "form": "消えてしまうよ",
        "emotion": "N"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

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

AIに歌を聞かせた。そして彼は笑顔?になった。(COTOHA-感情分析API)

概要

歌を聞かせた。
彼はその歌を聴き、
Positiveな感情になると笑い?、
Negativeな感情になると怒り?、
Neutralな感情になると。。。?

(COTOHA感情分析APIを使って、歌の歌詞からどのような感情をくみ取るのかを試した。COTOHA API for Developersに登録してすぐに、感情分析APIを試せるようWEBアプリも作った。)

結果

とりあえず歴代CDシングル売り上げ枚数ランキングを聞かせた。
「1位から順に!カウントアップ!?」

1.(457.7万枚)およげ!たいやきくん

?
"sentiment": "Negative"
"score": 0.5

Response
{
  "result": {
    "sentiment": "Negative",
    "score": 0.5,
    "emotional_phrase": [
      {
        "form": "こころがはずむ",
        "emotion": "喜ぶ"
      },
      {
        "form": "たのしい",
        "emotion": "P"
      },
      {
        "form": "おもい",
        "emotion": "N"
      },
      {
        "form": "ひろいぜ",
        "emotion": "PN"
      },
      {
        "form": "いじめられる",
        "emotion": "N"
      },
      {
        "form": "もがいて",
        "emotion": "N"
      },
      {
        "form": "こげある",
        "emotion": "N"
      },
      {
        "form": "うまそう",
        "emotion": "P"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

2.(325.6万枚)女のみち

?
"sentiment": "Negative"
"score": 0.2011977885862898

Response
{
  "result": {
    "sentiment": "Negative",
    "score": 0.2011977885862898,
    "emotional_phrase": [
      {
        "form": "みちならば暗い",
        "emotion": "N"
      },
      {
        "form": "恋",
        "emotion": "好ましい,切ない"
      },
      {
        "form": "幸せ",
        "emotion": "P"
      },
      {
        "form": "つらい",
        "emotion": "N"
      },
      {
        "form": "泣",
        "emotion": "喜ぶ,悲しい,切ない"
      },
      {
        "form": "すがって泣いたうぶな",
        "emotion": "PN"
      },
      {
        "form": "いけない",
        "emotion": "N"
      },
      {
        "form": "みちならば",
        "emotion": "PN"
      },
      {
        "form": "捨てた",
        "emotion": "N"
      },
      {
        "form": "いじめる",
        "emotion": "N"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

3.(313.2万枚)世界に一つだけの花

?
"sentiment": "Positive"
"score": 0.08995079776388203

Response
{
  "result": {
    "sentiment": "Positive",
    "score": 0.08995079776388203,
    "emotional_phrase": [
      {
        "form": "うれしそうな",
        "emotion": "P"
      },
      {
        "form": "笑顔",
        "emotion": "安心"
      },
      {
        "form": "好み",
        "emotion": "好ましい"
      },
      {
        "form": "困った",
        "emotion": "N"
      },
      {
        "form": "迷",
        "emotion": "不安"
      },
      {
        "form": "笑い",
        "emotion": "喜ぶ"
      },
      {
        "form": "もともと特別な",
        "emotion": "PN"
      },
      {
        "form": "ひとそれぞれ",
        "emotion": "PN"
      },
      {
        "form": "きれいだ",
        "emotion": "P"
      },
      {
        "form": "争う",
        "emotion": "N"
      },
      {
        "form": "誇らしげ",
        "emotion": "P"
      },
      {
        "form": "咲かせる",
        "emotion": "P"
      },
      {
        "form": "一生懸命",
        "emotion": "PN"
      },
      {
        "form": "頑張って",
        "emotion": "P"
      },
      {
        "form": "仕方ないね",
        "emotion": "N"
      },
      {
        "form": "色とりどり",
        "emotion": "P"
      },
      {
        "form": "気づかない",
        "emotion": "N"
      },
      {
        "form": "小さい",
        "emotion": "PN"
      },
      {
        "form": "同じものはない",
        "emotion": "PN"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

4.(293.6万枚)TSUNAMI

?
"sentiment": "Negative"
"score": 0.10121480950551423

Response
{
  "result": {
    "sentiment": "Negative",
    "score": 0.10121480950551423,
    "emotional_phrase": [
      {
        "form": "愛",
        "emotion": "安心,好ましい"
      },
      {
        "form": "愛しい",
        "emotion": "P"
      },
      {
        "form": "怯えてる",
        "emotion": "不安"
      },
      {
        "form": "悲しみ",
        "emotion": "悲しい"
      },
      {
        "form": "恋",
        "emotion": "好ましい,切ない"
      },
      {
        "form": "好きな",
        "emotion": "P"
      },
      {
        "form": "戸惑う",
        "emotion": "N"
      },
      {
        "form": "泣いた",
        "emotion": "N"
      },
      {
        "form": "涙",
        "emotion": "喜ぶ,不安,悲しい,切ない"
      },
      {
        "form": "微笑",
        "emotion": "安心"
      },
      {
        "form": "弱気な",
        "emotion": "N"
      },
      {
        "form": "すがる",
        "emotion": "N"
      },
      {
        "form": "涙もろい",
        "emotion": "PN"
      },
      {
        "form": "清か",
        "emotion": "P"
      },
      {
        "form": "魔性",
        "emotion": "N"
      },
      {
        "form": "彷徨う",
        "emotion": "N"
      },
      {
        "form": "枯れる",
        "emotion": "N"
      },
      {
        "form": "素直に",
        "emotion": "P"
      },
      {
        "form": "侘しさ",
        "emotion": "PN"
      },
      {
        "form": "鏡のような",
        "emotion": "P"
      },
      {
        "form": "深い",
        "emotion": "PN"
      },
      {
        "form": "気付いてる",
        "emotion": "P"
      },
      {
        "form": "張り裂けそうな",
        "emotion": "N"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

5.(291.8万枚)だんご3兄弟

?
"sentiment": "Negative"
"score": 0.06456371454543736

Response
{
  "result": {
    "sentiment": "Negative",
    "score": 0.06456371454543736,
    "emotional_phrase": [
      {
        "form": "かたくな",
        "emotion": "不安"
      },
      {
        "form": "ささって",
        "emotion": "P"
      },
      {
        "form": "まれ",
        "emotion": "PN"
      },
      {
        "form": "たくさん",
        "emotion": "PN"
      },
      {
        "form": "こげ",
        "emotion": "N"
      },
      {
        "form": "かたくなりました",
        "emotion": "PN"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

6.(289.5万枚)君がいるだけで

?
"sentiment": "Positive"
"score": 0.009547567550434202

Response
{
  "result": {
    "sentiment": "Positive",
    "score": 0.009547567550434202,
    "emotional_phrase": [
      {
        "form": "憧れ",
        "emotion": "P"
      },
      {
        "form": "笑顔",
        "emotion": "安心"
      },
      {
        "form": "くやしい",
        "emotion": "N"
      },
      {
        "form": "何より大切な",
        "emotion": "P"
      },
      {
        "form": "涙",
        "emotion": "喜ぶ,切ない,不安,悲しい"
      },
      {
        "form": "儚い",
        "emotion": "PN"
      },
      {
        "form": "強く",
        "emotion": "PN"
      },
      {
        "form": "気付かせてくれたね",
        "emotion": "P"
      },
      {
        "form": "ありがちな",
        "emotion": "PN"
      },
      {
        "form": "つい引き込まれ",
        "emotion": "P"
      },
      {
        "form": "弱さ",
        "emotion": "N"
      },
      {
        "form": "忘れてた",
        "emotion": "PN"
      },
      {
        "form": "もっと素直になれなかった",
        "emotion": "N"
      },
      {
        "form": "わかって",
        "emotion": "P"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

7.(282.2万枚)SAY YES

?
"sentiment": "Negative"
"score": 0.2490204168421342

Response
{
  "result": {
    "sentiment": "Negative",
    "score": 0.2490204168421342,
    "emotional_phrase": [
      {
        "form": "愛",
        "emotion": "好ましい,安心"
      },
      {
        "form": "愛してる",
        "emotion": "P"
      },
      {
        "form": "恋",
        "emotion": "好ましい,切ない"
      },
      {
        "form": "寂しい",
        "emotion": "N"
      },
      {
        "form": "切なさ",
        "emotion": "PN"
      },
      {
        "form": "迷",
        "emotion": "不安"
      },
      {
        "form": "ワガママ",
        "emotion": "N"
      },
      {
        "form": "余計な",
        "emotion": "N"
      },
      {
        "form": "何げなく暮らさない",
        "emotion": "PN"
      },
      {
        "form": "あふれてる",
        "emotion": "N"
      },
      {
        "form": "勝てない",
        "emotion": "N"
      },
      {
        "form": "消えない",
        "emotion": "P"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

8.(276.6万枚)Tomorrow never knows

?
"sentiment": "Positive"
"score": 0.007165278534051281

Response
{
  "result": {
    "sentiment": "Positive",
    "score": 0.007165278534051281,
    "emotional_phrase": [
      {
        "form": "愛した",
        "emotion": "P"
      },
      {
        "form": "悲しい",
        "emotion": "N"
      },
      {
        "form": "孤独な",
        "emotion": "PN"
      },
      {
        "form": "寂しい",
        "emotion": "N"
      },
      {
        "form": "愛される喜び",
        "emotion": "P"
      },
      {
        "form": "消えた帰らぬ",
        "emotion": "P"
      },
      {
        "form": "すれ違う",
        "emotion": "N"
      },
      {
        "form": "無邪気に",
        "emotion": "P"
      },
      {
        "form": "裏切れる",
        "emotion": "N"
      },
      {
        "form": "欲しがっていた",
        "emotion": "P"
      },
      {
        "form": "分かり合えた",
        "emotion": "P"
      },
      {
        "form": "夢中で駆け抜ける",
        "emotion": "P"
      },
      {
        "form": "勝利",
        "emotion": "P"
      },
      {
        "form": "敗北もない",
        "emotion": "P"
      },
      {
        "form": "忘れてゆく",
        "emotion": "PN"
      },
      {
        "form": "避けて通れない",
        "emotion": "P"
      },
      {
        "form": "果てしない",
        "emotion": "P"
      },
      {
        "form": "優しさ",
        "emotion": "P"
      },
      {
        "form": "長い",
        "emotion": "PN"
      },
      {
        "form": "癒える",
        "emotion": "P"
      },
      {
        "form": "少し",
        "emotion": "PN"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

9.(258.8万枚)ラブ・ストーリーは突然に

?
"sentiment": "Positive"
"score": 0.060429544970368884

Response
{
  "result": {
    "sentiment": "Positive",
    "score": 0.060429544970368884,
    "emotional_phrase": [
      {
        "form": "もっと好き",
        "emotion": "P"
      },
      {
        "form": "もう心揺れたりしないで切ない",
        "emotion": "PN"
      },
      {
        "form": "揺れたり",
        "emotion": "興奮"
      },
      {
        "form": "いい",
        "emotion": "P"
      },
      {
        "form": "分からない",
        "emotion": "N"
      },
      {
        "form": "消えてゆく",
        "emotion": "N"
      },
      {
        "form": "あんまりすてきだ",
        "emotion": "P"
      },
      {
        "form": "甘く",
        "emotion": "PN"
      },
      {
        "form": "やわらかく",
        "emotion": "PN"
      },
      {
        "form": "心が動いた",
        "emotion": "P"
      },
      {
        "form": "忘れない",
        "emotion": "PN"
      },
      {
        "form": "心揺れたりしないで",
        "emotion": "PN"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

10.(248.9万枚)LOVE LOVE LOVE

?
"sentiment": "Positive"
"score": 0.24542462771149284

Response
{
  "result": {
    "sentiment": "Positive",
    "score": 0.24542462771149284,
    "emotional_phrase": [
      {
        "form": "愛",
        "emotion": "安心,好ましい"
      },
      {
        "form": "愛してる",
        "emotion": "P"
      },
      {
        "form": "すごく好きな",
        "emotion": "P"
      },
      {
        "form": "涙",
        "emotion": "喜ぶ,切ない,不安,悲しい"
      },
      {
        "form": "すっごく",
        "emotion": "PN"
      },
      {
        "form": "うまく",
        "emotion": "P"
      },
      {
        "form": "願う",
        "emotion": "P"
      },
      {
        "form": "少しずつ思い出になって",
        "emotion": "P"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

番外編(何となく気になった曲)

大きなのっぽの古時計

?
"sentiment": "Positive"
"score": 0.30601098537139876

Response
{
  "result": {
    "sentiment": "Positive",
    "score": 0.30601098537139876,
    "emotional_phrase": [
      {
        "form": "うれしい",
        "emotion": "P"
      },
      {
        "form": "かなしい",
        "emotion": "N"
      },
      {
        "form": "のっぽの",
        "emotion": "PN"
      },
      {
        "form": "ごじまんの",
        "emotion": "PN"
      },
      {
        "form": "きれいな",
        "emotion": "P"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

千の風になって

?
"sentiment": "Positive"
"score": 0.14780720679775194,

Response
{
  "result": {
    "sentiment": "Positive"
    "score": 0.14780720679775194,
    "emotional_phrase": [
      {
        "form": "泣かないでください",
        "emotion": "P"
      },
      {
        "form": "きらめく",
        "emotion": "P"
      },
      {
        "form": "見守る",
        "emotion": "P"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

パプリカ

?
"sentiment": "Positive"
"score": 0.01385186275421083

Response
{
  "result": {
    "sentiment": "Positive",
    "score": 0.01385186275421083,
    "emotional_phrase": [
      {
        "form": "泣いてた",
        "emotion": "N"
      },
      {
        "form": "喜び",
        "emotion": "P"
      },
      {
        "form": "晴れた",
        "emotion": "P"
      },
      {
        "form": "燻り",
        "emotion": "N"
      },
      {
        "form": "慰める",
        "emotion": "P"
      },
      {
        "form": "まれ",
        "emotion": "PN"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

WEBアプリ化

COTOHA API for Developersに登録した人限定ではあるが、同じ遊びが出来るようにした。(簡単に登録可)
以下URLで遊ぶには、登録後に取得できる、Client IDClient secretが必要。
https://cotoha-demo.now.sh/

2020-02-26_17h32_04.png

Client IDClient secretは、画面リフレッシュのたびに入力しなおすのは面倒。なので、ブラウザのlocalstrageにて情報を保持している。

技術

アプリ化に伴い、以下の技術を使用。

  • Nuxt.js【JavaScript framework】
  • ant-design-vue【Css framework】
  • Now【Deploy】
  • Express【API】

server.js
server.js
const express = require('express')
const request = require('request')
const app = express()

// server
const port = process.env.PORT || 5000
app.listen(port, err => {
    if (err) throw err
    console.log(`> Ready On Server http://localhost:${port}`)
})

// API
app.get('/getToken', function(req, res, next) {
    (() => {
        return new Promise(r => {
            if (req.query.ClientID && req.query.Clientsecret && req.query.AccessTokenPublishURL){
                const AccessTokenPublishURL = req.query.AccessTokenPublishURL
                const ClientID = req.query.ClientID
                const Clientsecret = req.query.Clientsecret
                const headers = {
                    'Content-Type': 'application/json'
                }
                const data = {
                    'grantType': 'client_credentials',
                    'clientId': ClientID,
                    'clientSecret': Clientsecret
                }
                const options = {
                    url: AccessTokenPublishURL,
                    method: 'POST',
                    headers: headers,
                    json: data
                }
                request(options, (error, response, body) => {
                    return r(body)
                })
            }else{
                r('Parameter is incorrect')
            }
        })
    })().then(data => {
        res.send(data)
    }).catch(error => {
        res.send(error)
      })
})

app.get('/cotohaApiSimilarity', function(req, res, next) {
    (() => {
        return new Promise(r => {
            if (req.query.sentence && req.query.token){
                const sentence = req.query.sentence
                const token = req.query.token
                const headers = {
                    'Content-Type': 'application/json;charset=UTF-8',
                    'Authorization': `Bearer ${token}`
                }
                const data = {
                    'sentence': sentence,
                }
                const options = {
                    url: 'https://api.ce-cotoha.com/api/dev/nlp/v1/sentiment',
                    method: 'POST',
                    headers: headers,
                    json: data
                }
                request(options, (error, response, body) => {
                    return r(body)
                })
            }else{
                r('Parameter is incorrect')
            }
        })
    })().then(data => {
        res.send(data)
    }).catch(error => {
        res.send(error)
      })
})

あとがき

他のAPIと組み合わせれば、話す内容によって表情を変えるbotが簡単に出来そう。

あと要約(β)API・キーワード抽出API・言い淀み除去(β)・音声認識誤り検知(β)・音声認識API ここら辺のを使って、音声データから議事録を作るWEBアプリ(WEBサービス)作りたい。。。時間が欲しい。。。

おまけ

Q. さて、これは誰が歌っているのさくらの結果でしょうか???↓

さくら

?
"sentiment": "Positive",
"score": 0.0010436678748683892,

Response
{
  "result": {
    "sentiment": "Positive",
    "score": 0.0010436678748683892,
    "emotional_phrase": [
      {
        "form": "また気になる",
        "emotion": "PN"
      },
      {
        "form": "忘れた",
        "emotion": "PN"
      },
      {
        "form": "淡い",
        "emotion": "PN"
      },
      {
        "form": "意味なく",
        "emotion": "N"
      },
      {
        "form": "変わらない",
        "emotion": "PN"
      },
      {
        "form": "甦る",
        "emotion": "PN"
      },
      {
        "form": "長い",
        "emotion": "PN"
      },
      {
        "form": "たわいない",
        "emotion": "PN"
      },
      {
        "form": "気付けば",
        "emotion": "P"
      },
      {
        "form": "誘われ",
        "emotion": "P"
      },
      {
        "form": "すり抜けた",
        "emotion": "PN"
      },
      {
        "form": "分からなかった",
        "emotion": "N"
      },
      {
        "form": "若かった",
        "emotion": "P"
      },
      {
        "form": "香る",
        "emotion": "P"
      },
      {
        "form": "暖かい",
        "emotion": "P"
      },
      {
        "form": "こぼれる",
        "emotion": "N"
      },
      {
        "form": "消えてしまうよ",
        "emotion": "N"
      }
    ]
  },
  "status": 0,
  "message": "OK"
}

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

VueCLIの導入とプロジェクト作成からデプロイの備忘録

VueCLIの導入

node.jsのインストール

下記を参考に行う。
MacにNode.jsをインストール - Qiita

VueCLIのインストール

$ npm install -g @vue/cli

VueCLIでプロジェクト作成〜デプロイ

新しいプロジェクトの作成

$ cd '作成したい場所'
$ vue create 'プロジェクト名'

Vueプロジェクトの起動

$ cd 'プロジェクト名'
$ npm run serve

ビルド・デプロイ

$ npm run build
distフォルダが作成されるので、サーバーに配置

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

Vue.jsでCSSのスタイルをデータに紐付ける方法

classをデータにバインディング(紐付け)する

パターン1

<div id="app">
  <h1 :class="classObject">Text</h1>
  <button @click="isActive = !isActive">switch</button>
</div>

<style>
  .red {
    color: red;
  }
  .bg-blue {
    background-color: blue;
  }
</style>

<script>
  new Vue({
    el: "#app",
    data: {
      isActive: true
    },
    computed: {
      classObject: function () {
        return {
          red: this.isActive,
          'bg-blue': !this.isActive
        }
      }
    },
  })
</script>

パターン2

<div id="app">
  <h1 :class="classObject">Text</h1>
  <button @click="isActive = !isActive">switch</button>
</div>

<style>
  .red {
    color: red;
  }
  .bg-blue {
    background-color: blue;
  }
</style>

<script>
  new Vue({
    el: "#app",
    data: {
      isActive: true
    },
    computed: {
      classObject: function () {
        return {
          red: this.isActive,
          'bg-blue': !this.isActive
        }
      }
    },
  })
</script>

styleをデータにバインディング(紐付け)する

<div id="app">
  <h1 :style="{ color: textColor, 'background-color': bgColor }">Text</h1>
  <h1 :style="styleObject">Text</h1>
</div>

<script>
new Vue({
  el: "#app",
  data: {
    textColor: 'red',
    bgColor: 'blue',
    styleObject: {
      color: 'red',
      'background-color': 'blue'
    }
  }
})
</script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsコンポーネントの作り方

基本的なコンポーネント

Vue.jsの仕様で、コンポーネントでdataをオブジェクト型にしてしまうとエラーで動きません。
これはオブジェクトにしてしまうと、データが一つのメモリに保存され、複数のコンポーネントで同じメモリにアクセスしてしまうからです。
なのでコンポーネント化するときは、dataをfunctionにして初期値の値をreturnする形式にする必要があります。

<div id="app">
  <my-component></my-component>
  <my-component></my-component>
  <my-component></my-component>
</div>

<script>
Vue.component('my-component', {
  data: function () {
    return {
      number: 0
    }
  },
  template: '<p>カウント({{number}})<button @click="increment">+1</button></p>',
  methods: {
    increment: function () {
      this.number += 1
    }
  },
})

new Vue({
  el: "#app"
})
</script>

コンポーネントのローカル登録

先程までの書き方だと、コンポーネントはグローバルとして登録されるため、どこからでもアクセスできてしまいます。
実際に開発する際は基本的にこちらを使います。

<!-- ローカル登録済み -->
<div id="app1">
  <my-component></my-component>
  <my-component></my-component>
  <my-component></my-component>
</div>

<!-- ローカル未登録なので表示されない -->
<div id="app2">
  <my-component></my-component>
  <my-component></my-component>
  <my-component></my-component>
</div>

<script>
var component = {
  data: function () {
    return {
      number: 0
    }
  },
  template: '<p>カウント({{number}})<button @click="increment">+1</button></p>',
  methods: {
    increment: function () {
      this.number += 1
    }
  },
}

new Vue({
  el: "#app1",
  components: {
    'my-component': component
  },
})

new Vue({
  el: "#app2"
})
</script>

単一ファイルコンポーネント(.vue)の書き方

コンポーネントを一つのファイルとして管理できます。
VueCLIを導入していないと使えません。

コンポーネントのtemplateタグは、ルート要素を一つしか設定できません。

<template>
  <p>いいね({{ number }})</p>
</template>

<script>
export default {
  data() {
    return {
      number: 12
    }
  },
};
</script>

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

Vue.js computedプロパティとwatchの使い方

computedプロパティ

動的な処理をプロパティとして持たせることができます。

<div id="app">
  <p>{{ number }}</p>
  <button @click="number += 1">+1</button>
  <p>{{ lessThanThree }}</p>
</div>
<script>
new Vue({
  el: "#app",
  data: {
    number: 0
  },
  computed: {
    lessThanThree: function () {
      return this.number > 3 ? '3より上' : '3より下'
    }
  },
})
</script>

methodとの違いについて

methodだとテンプレート内が少しでも再描画された際に実行されます。
ですが、computedの場合だと、表示されているかつ、プロパティ内部で参照している値が変更したときのみ、実行されます。
今回の例だと、「numberの中身が変わったときだけ」実行されることになります。めちゃくちゃ賢いですね。
そのため無駄な処理を省くことができます。

ウォッチャについて

基本的にはcomputedプロパティを使用したほうが良いですが、computedではまかないきれないときがあります。
例としては、画面に表示されていない場合などです。

そのようなときはウォッチャで実装することができます。
非同期通信などはウォッチャを使用する良い例です。

<div id="app">
  <p>{{ number }}</p>
  <button @click="number += 1">+1</button>
  <p>{{ lessThanThree }}</p>
</div>

<script>
new Vue({
  el: "#app",
  data: {
    number: 0
  },
  computed: {
    lessThanThree: function () {
      return this.number > 3 ? '3より上' : '3より下'
    }
  },
  watch: {
    number: function () {
      var vm = this;
      setTimeout(function () {
        vm.number = 9999
      }, 3000)
    }
  }
})
</script>

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

Vue.jsの繰り返しレンダリング(v-for)のまとめ

v-for

繰り返し描画ができます。
indexを引数に取ることもできます。

<div id="app">
  <ul>
    <li v-for="fruit in fruits">{{fruit}}</li>
  </ul>

  <ul>
      <li v-for="(fruit, index) in fruits">{{index}}{{fruit}}</li>
    </ul>

  <ul>
    <template v-for="fruit in fruits">
      <li>{{fruit}}</li>
      <hr>
    </template>
  </ul>
</div>

<script>
  new Vue({
    el: "#app",
    data: {
      fruits: ['りんご', 'バナナ', 'ぶどう']
    }
  })
</script>

オブジェクトで指定する場合

<div id="app">
  <ul>
    <li v-for="value in object">{{value}}</li>
  </ul>
  <ul>
    <li v-for="(value, key, index) in object">{{index}}{{key}}{{value}}</li>
  </ul>
</div>

<script>
  new Vue({
    el: "#app",
    data: {
      object: {
        firstName: '太郎',
        lastName: '田中',
        age: 20
      }
    }
  })
</script>

v-forには必ずkeyをつけるようにしましょう

vueはDOMの移動を最低限に抑えるため、keyを使用しないと意図しない動きになることがあります。
key属性を外して、inputに何らか入力して、削除ボタンを押してみるとわかると思います。

<div id="app">
  <ul>
    <div v-for="fruit in fruits" :key="fruit">
      <p>{{fruit}}</p>
      <input />
    </div>
  </ul>
  <button @click="remove">先頭を削除</button>
</div>

<script>
  new Vue({
    el: "#app",
    data: {
      fruits: ['りんご', 'バナナ', 'ぶどう']
    },
    methods: {
      remove: function () {
        this.fruits.shift()
      }
    }
  })
</script>

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

Vue.jsの条件付きレンダリング(v-ifなど)まとめ

v-if

条件を満たしている場合、描画されます。

<div id="app">
  <h1 v-if="visible">Text</h1>

  <!--templateタグ指定でもOK-->
  <template v-if="visible">
    <h1>Text1</h1>
    <h1>Text2</h1>
    <h1>Text3</h1>
  </template>

  <button @click="visible = !visible">
    {{ visible ? "表示してます" : "非表示です"}}
  </button>
</div>

<script>
  new Vue({
    el: "#app",
    data: {
      visible: true
    }
  })
</script>

v-show

条件を満たしていない場合、display:noneをスタイルに追加します。
そのためtemplateタグでは使用できません。ですが初期描画は遅いですが、v-ifと比べて処理が発生するときのコストが低いです。

とても頻繁に、切り替える場合はv-showを使用しましょう。

<div id="app">
  <h1 v-show="visible">Text</h1>
  <button @click="visible = !visible">
    {{ visible ? "表示してます" : "非表示です"}}
  </button>
</div>

<script>
  new Vue({
    el: "#app",
    data: {
      visible: true
    }
  })
</script>

v-else

条件を満たしていない場合、描画されます。
v-ifの直後でないと使用できません。

<div id="app">
  <p v-if="visible">OK!</p>
  <p v-else="visible">Not OK...</p>
  <button @click="visible = !visible">
    反転
  </button>
</div>

<script>
  new Vue({
    el: "#app",
    data: {
      visible: true
    }
  })
</script>

v-else-if

ちょっと複雑な条件式を指定できます。
v-ifの直下でないと使用できません。

<div id="app">
  <p v-if="count == 0">カウント0</p>
  <p v-else-if="count >= 1 && count <= 5">カウント1以上かつ5以下</p>
  <p v-else>カウント6以上</p>
  <button @click="count += 1">
    +1
  </button>
</div>

<script>
  new Vue({
    el: "#app",
    data: {
      count: 0
    }
  })
</script>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

VueJSの基本的なディレクティブまとめ

v-html

データをhtmlとして、そのまま出力できる。
クロスサイトスクリプティングに気をつける。
ユーザーから入力されたものを表示しないようにすること。

v-once

一度だけ描画する。
一度描画されたあとは、あとで内容が書き換わることがないです。

v-bind

属性を指定して、データを出力させることができる。

<a v-bind:href="url">Google</a>

<!-- このように省略して書くことも可能 -->
<a :href="url">Google</a>

<script>
new Vue({
  el: "#app"
  data: {
    url: 'https://google.com'
  }
})
</script>

属性に関しても、Vueから指定することが可能

<a :[attribute]="google">リンク</a>

<!-- オブジェクト形式で書くことも可能 -->
<a v-bind="{ href: yahoo, id: number }">リンク</a>

<!-- 上を省略したもの -->
<a v-bind="yahooObject">リンク</a>

<script>
new Vue({
  el: "#app"
  data: {
    google: 'https://google.com'
    yahoo: 'https://yahoo.co.jp'
    number: 1
    attribute: 'href'
    yahooObject: {
      href: 'https://yahoo.co.jp',
      id: 1
    }
  }
})
</script>

v-on

イベントの検知

DOMの提供してるイベントを検知できる。
v-onで使用できるイベントのリストは以下のURLを参考にしましょう。
イベントリファレンス | MDN

<p v-on:click="countUp">{{ number }}回クリック</p>

<!-- 省略するときは@をつける --->
<p @click="countUp">{{ number }}回クリック</p>

<!-- イベントタイプを動的に指定してもいけます。--->
<p @[event]="countUp">{{ number }}回クリック</p>

<script>
new Vue({
  el: "#app"
  data: {
      number: 0,
      event: 'click'
    }
  },
  methods: {
    countUp: function() {
      this.number += 1
    }
  }
})
</script>

イベント修飾子

イベントについて、よく使うものに関しては、デフォルトでVueが用意してくれています。

<!-- preventによって、タグ本来の機能を停止 -->
<a v-on:click.prevent href="https://google">このリンクは発動しません。</a>

キー修飾子

キーアクションにおいても、よく使うものに関しては、デフォルトでVueが用意してくれています。

<!-- エンターキー押下時に、myFuncが呼ばれる -->
<input type="text" v-on:keyup.enter="myFunc" />

v-model

双方向データバインディングが行なえます。
リアルタイムで、データモデルが変更された際に変更させることができます。

<div id="app">
  <input type="text" v-model="message" />
  <h1>{{ message }}</h1>
</div>

<script>
new Vue({
  el: "#app",
  data: {
    message: 'Hello World!'
  }
})
</script>

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

Vue.jsの基本的なディレクティブまとめ

v-html

データをhtmlとして、そのまま出力できる。
クロスサイトスクリプティングに気をつける。
ユーザーから入力されたものを表示しないようにすること。

v-once

一度だけ描画する。
一度描画されたあとは、あとで内容が書き換わることがないです。

v-bind

属性を指定して、データを出力させることができる。

<a v-bind:href="url">Google</a>

<!-- このように省略して書くことも可能 -->
<a :href="url">Google</a>

<script>
new Vue({
  el: "#app"
  data: {
    url: 'https://google.com'
  }
})
</script>

属性に関しても、Vueから指定することが可能

<a :[attribute]="google">リンク</a>

<!-- オブジェクト形式で書くことも可能 -->
<a v-bind="{ href: yahoo, id: number }">リンク</a>

<!-- 上を省略したもの -->
<a v-bind="yahooObject">リンク</a>

<script>
new Vue({
  el: "#app"
  data: {
    google: 'https://google.com'
    yahoo: 'https://yahoo.co.jp'
    number: 1
    attribute: 'href'
    yahooObject: {
      href: 'https://yahoo.co.jp',
      id: 1
    }
  }
})
</script>

v-on

イベントの検知

DOMの提供してるイベントを検知できる。
v-onで使用できるイベントのリストは以下のURLを参考にしましょう。
イベントリファレンス | MDN

<p v-on:click="countUp">{{ number }}回クリック</p>

<!-- 省略するときは@をつける --->
<p @click="countUp">{{ number }}回クリック</p>

<!-- イベントタイプを動的に指定してもいけます。--->
<p @[event]="countUp">{{ number }}回クリック</p>

<script>
new Vue({
  el: "#app"
  data: {
      number: 0,
      event: 'click'
    }
  },
  methods: {
    countUp: function() {
      this.number += 1
    }
  }
})
</script>

イベント修飾子

イベントについて、よく使うものに関しては、デフォルトでVueが用意してくれています。

<!-- preventによって、タグ本来の機能を停止 -->
<a v-on:click.prevent href="https://google">このリンクは発動しません。</a>

キー修飾子

キーアクションにおいても、よく使うものに関しては、デフォルトでVueが用意してくれています。

<!-- エンターキー押下時に、myFuncが呼ばれる -->
<input type="text" v-on:keyup.enter="myFunc" />

v-model

双方向データバインディングが行なえます。
リアルタイムで、データモデルが変更された際に変更させることができます。

<div id="app">
  <input type="text" v-model="message" />
  <h1>{{ message }}</h1>
</div>

<script>
new Vue({
  el: "#app",
  data: {
    message: 'Hello World!'
  }
})
</script>

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

【Vue.js】Formバリデーションを作ってみた【Javascript】

はじめに

インフラ系IT企業を2年でやめ、受託開発会社に勤務して約7ヶ月の3年目のエンジニアです。
今はほとんどインフラはやっておらず、フロントエンドばっかり書いています。勉強のためバリデーションでも作って見よう!と思って作りました。
※バリデーション自体はvuetifyやプラグイン使えばかんたんに実装できますが、自分で作るのって結構大事ですよね、プラグインに依存しないですし。

機能

1.フォームの送信ボタンをクリックするとバリデーションが発火する。
2.入力必須、文字数、形式(stringとかnumber)をチェックする。
3.エラーが発生してる状態だと送信できないようにする。

見た目

スクリーンショット 2020-02-27 13.45.20.png

formの見た目は適宜調整してください。

まずは、vueのformのソースからです。
formの部分は結構見ずらいので、コンポーネントに分けた方が良いと思います。

コード

Form

<template>
  <div class="form">
      <div class="form-item">
        <div class="form-item__label">
          {{ input.name.name }}
          <span class="form-item__require" v-if="input.name.require">必須</span>
        </div>
        <div class="form-item__input">
          <div class="underline">
            <input v-model="input.name.value" :id="input.name.name" type="text" placeholder />
            <label :for="input.name.name">{{ input.name.name }}</label>
          </div>
          <div class="validation-message">{{input.name.errorMessage[0]}}</div>
        </div>
      </div>
      <div class="form-item">
        <div class="form-item__label">
          {{ input.name_kana.name }}
          <span class="form-item__require" v-if="input.name_kana.require">必須</span>
        </div>
        <div class="form-item__input">
          <div class="underline">
            <input v-model="input.name_kana.value" :id="input.name_kana.name" type="text" placeholder />
            <label :for="input.name_kana.name">{{ input.name_kana.name }}</label>
          </div>
          <div class="validation-message">{{input.name_kana.errorMessage[0]}}</div>
        </div>
      </div>
      <div class="form-item">
        <div class="form-item__label">
          {{ input.mail.name }}
          <span class="form-item__require" v-if="input.mail.require">必須</span>
        </div>
        <div class="form-item__input">
          <div class="underline">
            <input v-model="input.mail.value" :id="input.mail.name" type="text" placeholder />
            <label :for="input.mail.name">{{ input.mail.name }}</label>
          </div>
          <div class="validation-message">{{input.mail.errorMessage[0]}}</div>
        </div>
      </div>
        <button type="button" class="submit-btn" @click="submit">登録</button>
  </div>
</template>

<script>
import {validation} from '@/utilities/validation/validation.js';
const form = {
  name: 'aaaaaaa',
  name_kana: 'a',
  mail: 'testes@gmail.com',
}

export default {
  data() {
    return {
      form,
      input: {
        name: {
          name: '名前',
          value: '',
          length: [1, 28],
          require: true,
          errorMessage: []
        },
        name_kana: {
          name: '名前(かな)',
          value: '',
          length: [1, 28],
          format: 'Hiragana',
          require: true,
          errorMessage: []
        },
        mail: {
          name: 'メールアドレス',
          value: '',
          format: 'Mail',
          length: [1, 28],
          require: false,
          errorMessage: []
        },
      }
    };
  },
  watch: {
    form: {
      handler(newValue) {
        for(let key in this.input) {
            this.input[key].value = newValue[key];
        }
      },
      immediate: true
    }
  },
  methods: {
    handleValidate(result){
      return validation(this.input);
    },
    submit() {
      const result = this.handleValidate(this.input);
      if (result.validStatus) {
        console.log('送信')
      }
    }
  }
};
</script>

<style lang="scss">
.form{
  padding: 24px 120px;
}
.form-item {
  min-height: 72px;
  padding: 16px 0;
  display: flex;
  &__label {
    font-size: 13px;
    color: black;
    text-align: left;
    font-weight: 600;
    text-align: right;
    min-width: 140px;
    max-width: 140px;
    margin-right: 12px;
  }
  &__input{
    width: 100%;
  }
  &__require{
    background-color: red;
    font-size: 10px;
    color: white;
    border-radius: 4px;
    display: inline-block;
    padding: 1px 4px;
    vertical-align: middle;
  }
}

.validation-message {
  font-size: 12px;
  color: red;
  margin-top: 8px;
  text-align: left;
}
.underline {
  border-bottom: 2px solid #bbb;
  padding: 0 8px 2px 8px;
  position: relative;
  &::before {
    left: 0;
    content: "";
    height: 5px;
    width: 2px;
    position: absolute;
    bottom: 0;
    background-color: #bbb;
  }
  &::after {
    content: "";
    height: 5px;
    width: 2px;
    position: absolute;
    right: 0;
    bottom: 0;
    background-color: #bbb;
  }
  input {
    position: relative;
    border: none;
    border-image-width: 0;
    outline: none;
    margin: 0;
    font-size: 12px;
    padding: 0;
    z-index: 1;
    -webkit-appearance: none;
    border-radius: 0;
    background-color: transparent;
    width: 100%;
    display: block;
    box-sizing: border-box;
    color: #111;
    font-family: "Roboto", sans-serif;
    font-weight: bold;
  }
  label {
    width: 100%;
    padding-bottom: 2px;
    cursor: default;
    font-weight: bold;
    position: absolute;
    font-size: 12px;
    font-family: "Roboto", sans-serif;
    top: -14px;
    left: 8px;
    opacity: 0;
    color: #777;
  }
}
.placeholder {
  user-select: none;
  position: absolute;
  left: 8px;
  bottom: 8px;
  color: #777;
  font-family: "Roboto", sans-serif;
  font-weight: normal;
  font-size: 18px;
  z-index: 0;
  opacity: 0.99;
}
.submit-btn {
  margin: 0 auto;
    display: block;
    position: relative;
    width: 160px;
    padding: 0.8em;
    text-align: center;
    text-decoration: none;
    color: #fff;
    background: #3A8BA8;
  &:hover {
    cursor: pointer;
    text-decoration: none;
    background:#fff;
  color:  #3A8BA8;
  border:solid 1px #3A8BA8;
  }
}

</style>

まず data() オプションのinputオブジェクトにv-modelやバリデーションで使う値を定義しています。
名前のフォームを例に上げて説明すると、、、

 name: {
    name: '名前', // フォームの名前を定義します。エラーメッセージやラベルで使用されます。
    value: '', // v-modelで使用します。
    length: [1, 28], // 入力値の長さを確認に使用します。1つ目の数字が最小文字数、2つ目は最大文字数です。定義しない場合はチェックを行いません。
    require: true, // 入力必須の確認に使用します。定義しない場合は入力必須チェックを行いません。
    errorMessage: [] // エラーメッセージの表示に使用します。配列なのでv-forで回せば全てのエラーを表示できます。
 },

これらを定義したあと、「登録」ボタンが押されたタイミングでバリデーションのメソッドが発火します。エラーがあればメッセージが表示されます。
エラーが無ければ任意のメソッドを実行させることができます。

※watchでform変数をみて、inputに代入していますが、これは編集画面等でバリデーションしたい場合に使用します。
vuex等でvalueをstateに入れてそれをinputのvalueにのみ入れます。

バリデーション本体

かなりコードが汚いとおもいますが、一応動きます。

// formatチェックで使用する正規表現です。必要に応じてファイルを分けたり、追加が可能です。
const ValidateSchema = {
  Number: {
    schema: /^[0-9]*$/,
    errorMessage: "は数字で入力してください"
  },
  NumberHyphen: {
    // eslint-disable-next-line no-useless-escape
    schema: /^[0-9\-]+$/,
    errorMessage: "に数字とハイフン以外が入力されています"
  },
  Hiragana: {
    schema: /^[ぁ-んー]+$/,
    errorMessage: "はひらがなで入力してください"
  },
  Mail: {
    // eslint-disable-next-line no-useless-escape
    schema: /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/,
    errorMessage: "は正しい形式で入力してください"
  }
};

// 必須チェック
function requireCheck(input) {
  let result;
  let errorMessage;
    if (input.value.length <= 0 || input.value === null) {
      errorMessage = input.name + "は必須です。";
      result = { valid: false };
    } else {
      result = { valid: true };
      errorMessage = '';
    }
  return { errorMessage, ...result };
};

// 長さチェック
function lengthCheck(input) {
  const minLength = input.length[0];
  const maxLength = input.length[1];
  let errorMessage;
  let result;
  if (input.value.length >= minLength && input.value.length <= maxLength) {
    result = { valid: true };
    errorMessage = '';
  } else {
    errorMessage = input.name + "" + minLength + "文字以上" + maxLength + "文字以下で入力してください";
    result = { valid: false };
  }
  return { ...result, errorMessage };
};

// フォーマットチェック
function formatCheck(input) {
  const formatType = input.format;
  let errorMessage;
  let result;
  if (formatType && !input.value == '' || !input.value == null) {
    const validate = ValidateSchema[formatType].schema.test(input.value);
    if (validate) {
      result = { valid: true };
      errorMessage = '';
    } else {
      errorMessage = input.name + ValidateSchema[formatType].errorMessage;
      result = { valid: false };
    }
  } else {
    result = { valid: true };
    errorMessage = "";
  }
  return { ...result, errorMessage };
};

// エラーを初期化するメソッド
function clearErrorMessage(input) {
  return input.errorMessage = [];
}

// バリデーションメソッド
export const validation = (input) => {
  // 最初はエラーは0
  let errors = 0;
  const result = input;
  for (let key in input) {
    //エラーを初期化
    clearErrorMessage(result[key], errors);
    // 入力必須が設定されていた場合のみ実行
    if (input[key].require) {
      const requireCheckResult = requireCheck(result[key]);
      if (requireCheckResult.valid != true) {
        result[key].errorMessage.push(requireCheckResult.errorMessage);
        errors++;
      }
    }
    // 入力必須が設定されていて、長さチェックが設定されていた場合のみ実行
    if (input[key].length && input[key].require) {
      const lengthCheckResult = lengthCheck(result[key]);
      if (lengthCheckResult.valid != true) {
        result[key].errorMessage.push(lengthCheckResult.errorMessage);
        errors++;
      }
    }
    // フォーマットが設定されていた場合のみ実行
    if (input[key].format) {
      const formatCheckResult = formatCheck(result[key]);
      if (formatCheckResult.valid != true) {
        result[key].errorMessage.push(formatCheckResult.errorMessage);
        errors++;
      }
    }
    if (errors == 0) {
      clearErrorMessage(result[key], errors);
    }
  }
  if (errors === 0) {
    return { validStatus: true };
  }
  return { validStatus: false, input: { ...result } };
};

コンポーネントの方で定義したinputのオブジェクトを単純にforでまわして一つひとつバリデーションしています。
最終的にerrorsが0になった場合のみvalidStatusをtrueにして返します。

まとめ

かなり遠回りなコードだと思いますが、一応バリデーションを実行することができます。
改善することとして、バリデーションのスキーマをファイルに分けたり、formをコンポーネント化したりすることですね。
何か不具合等あればコメントいただければと思います。

今後もjavascript頑張っていきたいです。

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

CSSのbox-shadowプロパティのプレイグラウンドを作ってみた

動作ページ

https://playcss.dev/box-shadow
image.png
カンマ区切りで複数の影を追加できるので、「add shadow」ボタンで複数の値を追加できるようにした。

実装

縦に長くなるので、縮められるようにしてみた、
boxsizeanime.gif
heightを変えているけれど、カラーピッカーの時には、overflowをvisibleにする必要があるので、カラーピッカーを表示したときに変化させる形になってる。

追加部分は、コンポーネント分割をして、v-forで回す感じにしてる。

<transition-group name="side">
  <template v-for="(box, index) in boxShadow">
    <box-shadows
      v-model="boxShadow[index]"
      :key="box.id"
      :index="index"
      @delete="deleteBox"
    />
  </template>
</transition-group>

border-radiusとかもコンポーネント分割して、値だけを返すようにしてどのページでも追加できるようにしていきたい。

valuesの部分のスタイルにfieldset要素がちょうどよかったので、使ってみてる。
https://developer.mozilla.org/ja/docs/Web/HTML/Element/fieldset

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

Vue-cliによるフロントエンドの開発環境構築まとめ

PHPやPythonと言ったサーバーサイドとは独立したフロントエンド(vue.jsとvue-router,vuex,jest)の環境構築方法をまとめます。
新しくフロントエンドを作った時に確認出来るように修正や更新が必要になった箇所は対応して行きます。

環境

npm:6.12.1
node:12.13.1
yarn:1.21.1
Vue-cli:4.1.1

npmとnode環境の構築

vue-cliをインストールする為にnpmの環境を用意する必要があります。
下記どちらかの手順で実施します。

nvmを使う場合

MacやAmazon Linuxで環境を構築する場合はnvmをインストールします。

git clone https://github.com/creationix/nvm.git ~/.nvm

nvmへのパスを通す。

$ source ~/.nvm/nvm.sh

OS再起動時にも設定を読み込めるように~/.bash_profileに下記の内容を追記する。

$ vim ~/.bash_profile

# nvm
if [[ -s ~/.nvm/nvm.sh ]] ; then
        source ~/.nvm/nvm.sh ;
fi

安定版のインストールと設定

$ nvm install stable
$ nvm use stable

これでnpmとnodeコマンドが使えるようになります。

nを使う場合

Dockerコンテナ上で環境構築した際に古いバージョンのものがインストールされる場合があります。
安定版のインストールの為にnをインストールしてバージョンの更新をかけます。

nodejs,npm,wgetのインストール

$ apt install -y nodejs npm wget

nのインストール

$ npm install n -g

安定版のインストールと設定

$ n stable

nodejs,npmのパージ

$ apt purge -y nodejs npm

これでnpmとnodeのバージョンが更新されます。

yarnのインストール

npmでもvue-cliをインストール出来ますが、パッケージのインストール速度を速める為にyarnをインストールします。

$ npm install -g yarn

vue-cliのインストール

yarnを用いてvue-cliをインストールします。

$ yarn global add @vue/cli

Vue-cliプロジェクトの作成

プロジェクトの作成
*vuex等は後でインストールすることにする。

$ vue create testProject

パッケージのインストール

$ cd testProject
$ yarn install

ローカルサーバーの起動

$ yarn serve

ブラウザで「localhost:8080」にアクセスするとTOPページを表示出来ます。

パッケージの追加

下記のパッケージを追加します。
テンプレートを別途用意する場合は「vuetify」はインストールしないようにします。

$ yarn add vue-router
$ yarn add vuex
$ yarn add axios
$ yarn add axios-mock-server
$ vue add vuetify
$ yarn add material-design-icons-iconfont
$ yarn add @vue/test-utils
$ yarn add --dev prettier
$ yarn add --dev stylelint

パッケージの設定

package.jsonの編集

  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "lint:css": "stylelint src/**/*.css",
    "fmt": "prettier --write \"src/**/*.js\"",
    "mock:build": "axios-mock-server -b",
    "mock:watch": "axios-mock-server -w"
  },

.eslintrc.jsの作成と編集

module.exports = {
    root: true,
    env: {
        node: true
    },
    'extends': [
        'plugin:vue/essential',
        'eslint:recommended'
    ],
    rules: {
        'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
        'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
    },
    parserOptions: {
        parser: 'babel-eslint'
    }
}

.stylelintrcの作成と編集

{
  "rules": {
    "color-hex-length": "short",
    "color-no-invalid-hex": true,
    "custom-property-no-outside-root": true,
    "indentation": 2,
    "length-zero-no-unit": true,
    "media-feature-name-no-vendor-prefix": true,
    "number-leading-zero": "never",
    "selector-root-no-composition": true,
    "string-quotes": "single"
  }
}

Vue Routerの設定

/src/router.jsを下記の通り編集します。
*TestPageは独自のコンポーネントを指します。

import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from './components/HelloWorld.vue'
import TestPage from './components/Pages/TestPage.vue'

Vue.use(Router)

export default new Router({
    mode: 'history',
    routes: [
        {
            path: '/',
            name: 'home',
            component: HelloWorld
        },
        {
            path: '/test',
            name: 'test',
            component: TestPage
        },
    ]
})

vuexの設定

/src/store.jsの作成し、下記の通りに記載します。
testModuleは作成したコンポーネントのモジュールです。
testModuleにてstate, getters, actions, mutationsの設定を記述します。
(詳しくは割愛。)

import Vue from 'vue'
import Vuex from 'vuex'
import testModule from './store/modules/testModule'

Vue.use(Vuex)

const store = new Vuex.Store({
    strict: process.env.NODE_ENV !== 'production',
    modules: {
        test:testModule
    },
    state: {

    },
    mutations: {

    },
    actions: {

    }
})

export default store

main.jsの設定

/src/main.jsを下記の通りに編集します。
axiosもimportして設定します。

import Vue from 'vue'
import App from './App.vue'
import store from './store'
import router from './router'
import axios from 'axios'

Vue.config.productionTip = false
Vue.prototype.$axios = axios

new Vue({
    router,
    store,
    render: h => h(App),
}).$mount('#app')

テスト環境の構築

Jestと関連のパッケージをインストールします。

$ yarn add @vue/test-utils
$ yarn add jest
$ yarn add vue-jest
$ yarn add babel-jest

babel-core、babel-preset-envは、package.jsonにてバージョンを指定した上でyarn installコマンドでインストールします。

2020年2月現在のバージョンは下記の通りです。

"babel-core": "^7.0.0-bridge.0",
"babel-preset-env": "^7.0.0-beta.3",

package.jsonのscriptsにJestを追記

"scripts": {
    "test:unit": "jest",
}

jest.config.jsの作成をし、テストの設定を記載します。

module.exports = {
  "moduleFileExtensions": [
    "js",
    "json",
    "vue"
  ],
  "transform": {
    ".*\\.(vue)$": "vue-jest",
    "^.+\\.js$": "<rootDir>/node_modules/babel-jest"
  },
  "moduleNameMapper": {
    "^@/(.*)$": "<rootDir>/src/$1"
  }
}

これで一通りvue.jsとvue-router,vuexを使いunitテストも出来る開発の環境は構築出来ます。
後は好きなように実装して行きます!

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