20200805のvue.jsに関する記事は17件です。

無料の天気予報APIでWEBアプリを作ってみた

初めに

vue.jsを初めて使用してアプリを作ってみました。ボタンをクリックすると各地域のリアルタイム天気が表示される予定でした。

作ったもの

長野県のボタンのみ実装された長野地域限定のWEBアプリが完成?しました。長野県の現在の様子を画像と共に教えてくれます。

使用したフレームワーク・ライブラリ

Vue.js
axios
bootstrap
OpenWeatherMap API

デモ

https://happy-fermat-76197f.netlify.app/

ディレクトリ構成図

root/
  ├ img/
  │  ├ Rain.png
  │  ├ cloudy.jpg
  │  └ sunny.jpg  
 ├ app.js
 ├ index.html
 └ style.css

ソースコード

index.html
<!DOCTYPE html>
<html>
  <head>
    <title>Nagano weather</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <!-- CSS only -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">

    <!-- JS, Popper.js, and jQuery -->
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
  </head>
  <body>
    <div id="app" class="weather_box">

      <button v-on:click="upCount" type="button" class="btn btn-outline-primary">長野</button>

      <div class="weather_info">
        <ul v-if="city" id="samp">
          <li>{{ city }}</li>
          <li>{{ condition.description }}</li>   
          <li>{{ temp }}&deg;C</li>
        </ul>
      </div>
          <p v-if="condition.main == 'Rain'"><img src="img/Rain.png" alt="Rainy"></p>
          <p v-else-if="condition.main == 'Clouds'"><img src="img/cloudy.jpg" alt="Cloudy"></p>
          <p v-else-if="condition.main == 'Clear'"><img src="img/sunny.jpg" alt="Sunny"></p>
       </div>
    <div class="twitter"></div>
    <a href="https://twitter.com/Hirasawa1987?ref_src=twsrc%5Etfw" class="twitter-follow-button" data-show-count="false">Follow @Hirasawa1987</a><script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
    </div>
    <script src="app.js"></script>
    <link href="style.css" rel="stylesheet">


  </body>
</html>
style.css
ul {
    list-style: none;
  }

.weather_box {
    position: relative;
    }

  .weather_box ul {
    position: absolute;
    top: 50%;
    left: 50%;
    -ms-transform: translate(-50%,-50%);
    -webkit-transform: translate(-50%,-50%);
    transform: translate(-50%,-50%);
    margin-top: 10px;
    margin-bottom: 10px;
    padding:0;
    font-size: 60px;
    line-height: 3;/*行高は1に*/
    font-family: sans-serif;
    font-weight: bold;
    color: #000;
    -webkit-text-stroke: 1px #FFF;
    }

  .weather_box img {
    width: 100%;
    }

.twitter{
  margin-top: 10px;
}

app.js
const app = new Vue({
  el: '#app',
  data: {
    city: null,
    temp: null,
    condition: {
      main: null
    }
  },

  methods: {
    upCount: async function (event) {
      let response;
      try {
        response = await axios.get(
          'https://api.openweathermap.org/data/2.5/weather?q=Nagano,jp&units=metric&lang=ja&appid= APIkey '
        );
        this.city = response.data.name
        this.temp = response.data.main.temp
        this.condition = response.data.weather[0]
        this.iconPath = `img/${response.data.weather[0].icon}.svg`;
      } catch (error) {
        console.error(error);
      }

    },
  },
});

参考にした記事・サイト

APIを使用する際に参考にさせていただきました。
無料天気予報APIのOpenWeatherMapを使ってみる

vue.jsを書く際に参考にせていただきました。
Vue.jsを100時間勉強して分かったこと
お天気アプリで学ぶVue.jsのAPI連携

ありがとうございました。

反省点

自分の作りたかったアプリがあったので、理想と現実に劣等感を感じました。

今回は長野限定になってしまった。
とりあえず動いたこととアウトプットできたことを良しとしたいと思います。

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

【Vue.js】動的に画面表示を切り替える方法(条件付きレンダリング)

【Vue.js】動的に画面表示を切り替える方法(条件付きレンダリング)

ディレクティブを使って、条件に合わせ画面表示を切り替えることができる。

  1. v-ifとv-else
  2. v-else-if
  3. v-show
  4. v-ifとv-showの違い
  5. 確認用ソースコード

v-ifとv-else

v-ifとv-elseをセットで用いることで、真偽値の状態により表示を切り替えることができる。

v-if="プロパティ名 or 条件式"
  ┗ プロパティの場合、値は真偽値

v-else
  ┗ ifの直下で使用
  ┗ プロパティ名は不要

<注意点>
v-elseを適用した要素は、v-ifの次に来ること。
他のタグを間に挟むと動作しなくなる。

1. 真偽値の値により表示が切り替わるプログラム

v-if="プロパティ"の事例

・ボタンをクリックすることで、プロパティの真偽値が反転。
・真偽値の状態を画面上に表示する。

image.png
.js
var app = new Vue({
    el:'#app',
    data:{
        toggle: true
    },
    methods:{
        toggleBtn: function(){
            this.toggle == true ? this.toggle = false : this.toggle = true
        }
    }
})
.html
<body>
    <div id="app">
      <p v-if="toggle">真偽値:true</p>
      <p v-else=>真偽値:false</p>
      <br>
      <button v-on:click="toggleBtn">toggle</button>  

    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
    <script src="script.js"></script>
</body>


2. 入力値により表示を切り替えるプログラム

v-if="条件式"の事例

image.png

.js
var app = new Vue({
    el:'#app',
    data:{
        number: 0
    }
})
.html
<body>
    <div id="app">
          ■入力した数値の大きさにより表示を切り替え
          <p><input type="text" v-model="number"></p>

          <p v-if="number >= 5">入力値は5より:大きい</p>
          <p v-else>入力値は5より:小さい</p>

    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
    <script src="script.js"></script>
</body>


v-else-if

複数の条件分岐を設定する時に使用。

  • v-if = "真偽値"
  • v-else-if = "真偽値"
  • v-else
image.png
.js
var app = new Vue({
    el:'#app',
    data:{
        number: 0
    }
})
.html
<body>
    <div id="app">
        ■複数の条件分岐
        <p><input type="text" v-model="number"></p>

        <p v-if="number == 1">入力値:1</p>
        <p v-else-if="number == 2">入力値:2</p>
        <p v-else-if="number == 3">入力値:3</p>
        <p v-else-if="number == 4">入力値:4</p>
        <p v-else-if="number == 5">入力値:5</p>
        <p v-else>入力値:整数の1〜5以外</p> 

      </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
    <script src="script.js"></script>
</body>


v-show

cssスタイル「display」を切り替えることで、表示・非表示を切り替える。

v-show="真偽値"
真偽値がfalseの場合、スタイルをdisplay:noneにする。

<注意点>
・v-elseとの組み合わせはできない。

ボタンクリックで表示・非表示を切り替えるプログラム

image.png
.js
var app = new Vue({
    el:'#app',
    data:{
        toggle: true
    },
    methods:{
        toggleBtn: function(){
            this.toggle == true ? this.toggle = false : this.toggle = true
        }
    }
})
.html
<body>
    <div id="app">
        ■v-showによる表示・非表示の切り替え

        <p v-show="toggle">真偽値はtrue</p>
        <br>
        <button v-on:click="toggleBtn">toggle</button> 

    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
    <script src="script.js"></script>
</body>


v-ifとv-showの違い

▼v-if
- 要素をDOMから削除/追加
- 切り替え時のコストが高い
- v-else, v-else-ifが合わせて使える



▼v-show
- cssのdisplayプロパティの値を切り替え
- v-else, v-else-ifが使えない。


確認用ソースコード

▼ブラウザの表示

image.png
.js
var app = new Vue({
    el:'#app',
    data:{
        //v-ifとv-else
        toggle: true,
        number: 0

    },
    methods:{
        toggleBtn: function(){
            this.toggle == true ? this.toggle = false : this.toggle = true
        }
    }
})
.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>条件付きrendering</title>
</head>
<body>
    <div id="app">
        <div>
            ■v-ifとv-else プロパティの真偽値による表示切り替え
            <p v-if="toggle">真偽値:true</p>
            <p v-else=>真偽値:false</p>
            <br>
            <button v-on:click="toggleBtn">toggle</button> 
        </div>
      <hr>

      <div>
          ■入力した数値の大きさにより表示を切り替え
          <p><input type="text" v-model="number"></p>
          <p v-if="number >= 5">入力値は5より:大きい</p>
          <p v-else>入力値は5より:小さい</p>
      </div>

      <hr>

      <div>
        ■複数の条件分岐
        <p><input type="text" v-model="number"></p>
        <p v-if="number == 1">入力値:1</p>
        <p v-else-if="number == 2">入力値:2</p>
        <p v-else-if="number == 3">入力値:3</p>
        <p v-else-if="number == 4">入力値:4</p>
        <p v-else-if="number == 5">入力値:5</p>
        <p v-else>入力値:整数の1〜5以外</p> 
     </div>

     <hr>

     <div>
        ■v-showによる表示・非表示の切り替え
        <p v-show="toggle">真偽値はtrue</p>
        <br>
        <button v-on:click="toggleBtn">toggle</button> 
    </div>

    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
    <script src="script.js"></script>
</body>
</html>

以上。

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

【Vue.js】v-bindを使ったclass, styleの変更方法。動的な変更もできる

【Vue.js】v-bindを使ったclass, styleの変更方法。動的な変更もできる。

v-bindを使って、class属性を動的・静的に付与することでスタイルを変更できる。

また、style属性に適用することで、インラインスタイルで変更できる。

目次

  1. 動的な適用(真偽値)
  2. 静的と動的を合わせて適用
  3. 静的に適用(単体と配列構文)
  4. オブジェクトデータによる適用
  5. 三項演算子を使った適用
  6. インラインスタイルで適用
  7. オブジェクトデータをインラインスタイルで適用
  8. 確認用ソースコード


使用するCSSファイル

はじめに、4つのスタイルを用意。

.css
.large{
    font-size: 36px;
}

.redCollor{
    color: red;
}

.under-line{
    text-decoration: underline;
}

.bg-color{
    background-color: beige;
}

注意点

・Vue.jsの領域でハイフンを含む値はシングルクオテーションをつける。

動的な適用(真偽値)

真偽値の値がtrueの場合に、指定したクラス名を付与することができる。

v-bind:class="{クラス名: プロパティ名}
 ┗ プロパティ名は値が真偽値のもの。
 ┗ [例] isLarge: true

.js
data: {
        //動的な適用
        isLarge: true,
        isRed: true,
        isUnderLine: true
}

▼一つ指定

.html
apply <span v-bind:class="{large: isLarge}">dynamically</span>

▼複数指定

.html
apply <span v-bind:class="{large: isLarge, redCollor: isRed, 'under-line':isUnderLine}">multi style dynamically</span>

・複数指定時はカンマでつなげる。
・ハイフンを含む場合はシングルクオテーションをつける。(Vue.jsの表記ルール)


静的と動的を合わせて適用

class="クラス名" v-bind:class="{クラス名: プロパティ名}"

.html
apply <span class="redCollor under-line" v-bind:class="{large: isLarge, 'bg-color':isBgBeige}">dynamically</span>


静的に適用(単体と配列構文)

v-bindを使ってスタイルを静的に適用する方法。
複数適用する場合は配列を使う。

v-bind:class="プロパティ名"
 ┗ プロパティ名の値はクラス名
 ┗ [例] LargeClass: 'large'
 ┗ 複数指定する場合は配列構文にする "[プロパティ名1,プロパティ名2,,,]"

.js
data: {
        //静的な適用
        LargeClass: 'large',
        bgColorClass: 'bg-color',
        redClass: 'redCollor',
}

▼一つ指定

.html
 apply <span v-bind:class="bgColorClass">v-bind data property</span>

▼複数指定(配列構文)

.html
apply <span v-bind:class="[redClass, LargeClass]">v-bind multi data properties</span>


オブジェクトデータによる適用

v-bind:class="オブジェクト名"
 ┗ 複数のプロパティを持ったオブジェクトで指定できる。
 ┗ スタイルの一括適用
 ┗ [※注] プロパティ名はクラス名
 ┗ [※注] 値は真偽値。trueの場合プロパティ名をクラス名として適用

.js
data: {
        //オブジェクトデータでの適用(cssのセレクタを記述)
        classObject:{
            large: true,
            redCollor: true,
            'bg-color': true  //ハイフンを含む時はシングルクオテーションで囲む
        }
}
.html
apply <span v-bind:class="classObject">v-bind classオブジェクト</span>


三項演算子を使った適用

if文の省略形である三項演算子を使ってクラス名を適用できる。

v-bind:class="プロパティ名? trueで適用するプロパティ名 : falseで適用するプロパティ名"

.js
data: {
        isLarge: true,

        LargeClass: 'large',
        bgColorClass: 'bg-color'
}
.html
apply <span v-bind:class="isLarge? LargeClass : bgColorClass">三項演算子による適用</span>


インラインスタイルで適用

style属性に対してv-bindを使う。

v-bind:style="{cssスタイル: プロパティ名}
  ┗ 波カッコで囲む
  ┗ プロパティ名の値が適用される
  ┗ ハイフンを含む場合はシングルクオテーションで囲む
  ┗ 追加文字が必要な場合は「+」でつなげる

<ハイフン有無の表記>
fontSize: 24px
'font-size': 24px

<追加文字が必要な場合>
fontSize: textSize + "px"
┗ プロパティ(textSize: 24)

.js
data: {
        //インラインスタイルでの適用
        colorBlue:"blue",
        textSize:24,
}
.html
 apply <span v-bind:style="styleObject">inline-style</span>


オブジェクトデータをインラインスタイルで適用

スタイルをオブジェクトでひとまとめにして適用できる。

v-bind:style="オブジェクト名"
オブジェクトのプロパティは color:"blue"のようにインラインスタイルの形式。

複数のプロパティをまとめているため、個別にv-bindする場合と異なりテキストを後から追加できない。
 
× textSize + "px" (textSize=24)
○ textSize (textSize="24px")

.js
        //オブジェクトデータをインラインスタイルで適用
        styleObject:{
            color:"blue",
            fontSize:"24px",
        }
.html
apply <span v-bind:style="styleObject">inline-style</span>


確認用ソースコード

▼ブラウザの出力
・ボタンクリックで真偽値を切り替え、スタイルを変更させる機能を実装。

image.png
.js
var app = new Vue({
    el:"#app",
    data: {
        //動的な適用
        isLarge: true,
        isRed: true,
        isUnderLine: true,
        isBgBeige: true,

        //静的な適用
        LargeClass: 'large',
        bgColorClass: 'bg-color',
        redClass: 'redCollor',

        //オブジェクトデータでの適用(cssのセレクタを記述)
        classObject:{
            large: true,
            redCollor: true,
            'bg-color': true
        },
        //インラインスタイルでの適用
        colorBlue:"blue",
        textSize:24,

        //オブジェクトデータをインラインスタイルで適用
        styleObject:{
            color:"blue",
            fontSize:"24px",
        }

    },
    methods:{
        isLargeToggle: function(){
            this.isLarge == true ? this.isLarge = false : this.isLarge = true          
        },
        isRedToggle: function(){
            this.isRed == true ? this.isRed = false : this.isLarge = true          
        }
    }
})
.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>class, style binding</title>
    <link rel="stylesheet" href="css/style.css">
</head>
<body>
    <div id="app">
        <div>・静的に適用(通常のCSS)
            <p>
                apply <span class="large">statically</span>
            </p>
        </div>
        <hr>
        <div>・動的に適用(真偽値)
            <p>
                apply <span v-bind:class="{large: isLarge}">dynamically</span><br>
                apply <span v-bind:class="{large: isLarge, redCollor: isRed, 'under-line':isUnderLine}">multi style dynamically</span>

                <br>
                <br>
                <button v-on:click="isLargeToggle">isLarge</button>
                <button v-on:click="isRedToggle">isRed</button>
            </p>
        </div>
        <hr>
        <div>・静的と動的の共存
            <p>
                apply <span class="redCollor under-line" v-bind:class="{large: isLarge, 'bg-color':isBgBeige}">dynamically</span><br> 
            </p>
        </div>
        <hr>
        <div>・静的に適用(v-bind 単体 & 配列構文による適用)
            <p>
                apply <span v-bind:class="bgColorClass">v-bind data property</span><br> 
                apply <span v-bind:class="[redClass, LargeClass]">v-bind multi data properties</span><br> 
            </p>
        </div>
        <br>
        <hr>
        <div>・オブジェクトデータによる適用(ひとまとめの設定をv-bindで渡す)
            <p>
                apply <span v-bind:class="classObject">v-bind classオブジェクト</span>
            </p>
        </div>
        <hr>
        <div>・三項演算子を使った適用
            <p>
                apply <span v-bind:class="isLarge? LargeClass : bgColorClass">三項演算子による適用</span>
                <br><button v-on:click="isLargeToggle">isLarge</button>
            </p>
        </div>
        <hr>
        <div>・インラインスタイルで適用(v-bind:style="{}")
            <p>
                apply <span v-bind:style="{color: colorBlue, 'font-size': textSize + 'px'   }">inline-style</span>
            </p>
        </div>
        <hr>
        <div>・オブジェクトデータをインラインスタイルで適用(v-bind:style="")
            <p>
                apply <span v-bind:style="styleObject">inline-style</span>
            </p>
        </div>
        <hr>
        <div>
            <pre>{{$data}}</pre>
        </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
    <script src="js/script.js"></script>
</body>
</html>



以上。

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

Laravel+Vue.jsで作成したSPAサイトでOGP対応

はじめに

個人開発でLaravel5.5とVue.jsを使用して作成したSPAサイトがあり、長らく放置していたのですが、最近勉強を兼ねて少しリファクタリングをしようかなと思いました。

ラブライブ専用掲示板

上記が作成したサイト(掲示板)になります。
治すべき部分はたくさんあるのですが、まず気になったのがTwitterカードの表示です。
特に、掲示板のスレッドURLをツイートした際に、デフォルトの情報が表示されてしまうのが見た目の部分で致命的でした。
image.png

まずやったこと

最初にapiでスレッドの情報を読み込んだ後のタイミングでquerySelectorを使用して動的にmetaタグを変更しました。

//APIでデータ取得後
document.title = this.thread_header[0].title + ' | LoveLiveBBS'; 
document.querySelector("meta[property='og:title']").setAttribute('content', this.thread_header[0].title + ' | LoveLiveBBS');
document.querySelector("meta[property='description']").setAttribute('content', this.thread_response[0]['writing']);
document.querySelector("meta[property='og:description']").setAttribute('content', this.thread_response[0]['writing']);

当たり前ですが、これでは書き換わる前のmetaが読み込まれてしまうので結果は変わりませんでした。。。

今回は/thread/[thread_id]ページのみの修正ということで、その為だけにわざわざプリレンダリングやSSRはしたくないなと思い、以下のような対応を行いました。

Laravelのbladeファイルでの対応

/thread/[thread_id]にアクセスされたときにLaravel側でデータを取得してmetaタグに設定する方法をとりました。

bladeファイルの作成

修正前は初回アクセス時にのみ使用するspa.blade.phpのみが存在していました。
スレッドページ用のthread.blade.phpを作成しました。

resources/views/threadPage.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
    //コントローラーで作成したデータを表示
    <title>{{ $title }}</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <meta name="csrf-token" content="{!!csrf_token()!!}">
    <link href="https://fonts.googleapis.com/css?family=Kosugi+Maru&display=swap&subset=japanese" rel="stylesheet">
  //コントローラーで作成したデータを表示
    <meta name="description" content="{{ $description }}">
    <meta property="og:url" content="https://lovelivebbs.jp" />
  //コントローラーで作成したデータを表示
    <meta property="og:title" content="{{ $title }}" />
    <meta property="og:type" content="website">
  //コントローラーで作成したデータを表示
    <meta property="og:description" content="{{ $description }}" />
    <meta name="twitter:card" content="summary" />
    <meta name="twitter:site" content="@lovelivebbs" />
  //コントローラーで作成したデータを表示
    <meta property="og:site_name" content="{{ $title }}" />
    <meta property="og:locale" content="ja_JP" />
</head>
<body>
<div id="app">
    <app></app>
</div>

<script src="{{ mix('js/app.js') }}"></script>
</body>
</html>

コントローラーにスレッドページ用のメソッドを追加

spa.blade.phpを返す役割だけのコントローラーにthreadPageメソッドを追加しました。

app/Http/Controllers/SpaController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Response;
use App\Thread;
use Exception;
use Illuminate\Support\Facades\Log;

class SpaController extends Controller
{
    public function index()
    {
        return view('spa');
    }

    /**
     * スレッドページのみmetaタグをbladeファイルで設定
     * @param $id
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
     */
    public function threadPage($id) {
        $meta = [
            'title' => 'LoveLive!BBS!',
            'description' => '当サイトはラブライブシリーズ専用掲示板です。'
        ];
        try {
            $threadDetail = Thread::where('id', $id)->first();
            if (is_null($threadDetail)) {
                return view('threadPage', $meta);
            } else {
                //取得したデータをmetaタグに設定
                $response1 = Response::where('thread_id', $id)->first();
                $meta['title'] = $threadDetail->title . ' | LoveLive!BBS!';
                $meta['description'] = $response1->writing;
                return view('threadPage', $meta);
            }
        } catch (Exception $exception) {
            Log::error('thread page exception');
            return view('spa');
        }
    }
}

DBから取得した値をまとめてbladeファイルに渡しています。

ルーティングの追加

routes/web.php
+ Route::get('/thread/{id}', 'SpaController@threadPage');

Route::get('/{any}', 'SpaController@index')->where('any', '.*');

結果

上記の変更を加えた後に、Twitterにスレッドのリンクを張ってみました。
image.png

無事、スレッドタイトルと内容が反映されました!???
こうなると次はOGPの画像生成をしたくなりますね。

あまり良いやり方ではないかもしれませんが、なんとかなりました。
もっといい方法があったら教えてください。

後このサイトですが、残念ながら全然使われてないのでラブライブが好きな方はぜひ書き込みだけでもしてみてください。。。(切実)
ラブライブ専用掲示板
ラブライブ専用掲示板:ABOUTページ

よろしくお願いします。?

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

【Vue.js】さ迷うハロオタがお誕生日カレンダーを作った

はじめに


アンジュルム元メンバー・室田瑞希

私はハロープロジェクト所属(筆頭はモーニング娘。’20ですね)のアンジュルムというグループとメンバーの室田瑞希さんという方のファンで、ここ4年くらい足繁くライブやイベントに参加していました。
昨年の誕生日イベントや地元・千葉公演など良き思い出がたくさんあります。
アンジュルム|ハロー!プロジェクト オフィシャルサイト

悲劇は起きた

アンジュルム室田瑞希が卒業を発表

2020年1月、突然の卒業発表。
当時の職場でのシビアな話合いの直後に入ってきたニュースだったので
余計にダメージを喰らいました。
その後の卒業イベントのチケットのプレミアチケット化、果てはコロナによる無観客での実施など、最後まで激動でしたね。私は今だに辛くて映像も見ておりません。
アンジュルム・室田瑞希、「ひなフェス」無観客開催で卒業「約5年間、本当にありがとうございました!」

企画

私はアンジュルムの単推しの人間なので他のグループの方のお顔と名前以外はほとんど知りません。
新しい推しを探すためにもまずは所属メンバーのことを知ることだと思いました。
新たな推しが見つかるといいな!
今回はプロフィールには必ず載っている誕生日を一覧できるハロメン誕生日カレンダーの制作を試みました。

APIを探そう

私は最近APIを叩くことに喜びを感じているので隙あらば今回も組み込んでやろうと鼻息荒くAPIを探しました。

https://api.helloproject.com/v2/artists

このようなURLで所属メンバープロフィールを取得するAPIがあるか探しました。

【結果】ありませんでした。

自分で作ろう

スクリーンショット 2020-08-05 17.42.13.png

無ければ作れば良いと思いました。
SpreadeSheetをデータベースの代わりにしてVue.jsでデータを受けとります。
現役でグループに所属しているメンバーの用情報を公式ホームページからピックアップしていきます。
ハロー!プロジェクト オフィシャルサイト
データ数は53人分なので地道に作業をしています。スクレイピングでちゃっとやりたい気持ちもありますが(できるかは別として)この作業で各メンバーのプロフィールを読んでいる時が一番楽しかったことを告白します。

構成

Untitled Diagram (1).jpg

フロントエンドのアプリケーションフレームワークにVue.jsを使用してカレンダーライブラリはFullCalendarを使用して作成しました。
Vue.jsで作成されたアプリケーションからリクエストを飛ばしてSpreadSheetのデータを取得、カレンダーに反映させます。
データの取得はこちらの記事が大変参考になりました。

Google Spreadsheet のデータを JSON 形式で取得する Web API をサクッと作る

FullCalendar
設定するためのコードを書くだけでカレンダーをブラウザに表示させることができます。
オプションも豊富です。
先輩の執筆された記事ですね、大変お世話になりました。
Vue.js×FullCallendarでWEBカレンダー作成(花粉カレンダー作成①)

moment.js
日付処理のライブラリです。
どんなプログラミング言語でも日付周りの操作は面倒なことが多いですが
moment.jsはすごく使いやすかったです。
減算加算、未来過去応用の効く使い方もできますし、かなりの好印象。

BULMA
CSSフレームワークはbulmaを選択しました。
BootStrap以外触れたことが無かったのですが
FullCalendarにより画面はほぼ決まってしまっていたため、CSSは全然触らなかったのでいずれの機会にもう一度bulmaに挑戦したいと思います。

制作物のデモ

こちらのURLで公開しております。
https://nostalgic-bhaskara-023640.netlify.app/
何度も確認しましたが間違いを見つけた同志の方がいらっしゃいましたら
教えてくださいね!


UIはFullCalendarの力によってきちんとカレンダーの役割を果たしたものになっています。
スクリーンショット 2020-08-05 19.16.55.png
イベントの色はメンバーカラーが表示されるようにしました。
これはカラフルで良いと思います。
スクリーンショット 2020-08-05 19.17.41.png

イベントをマウスオーバーするとメンバーの写真が出現します。
皆さん本当に美しいです。
いつも元気をもらっております。
コードはFullCalendarの設定が主です。
ほとんど書いてないです。

// ライブラリ関連の読み込みなど
const dayGridPlugin = window.FullCalendarDayGrid.default;
const FullCalendarInteraction = window.FullCalendarInteraction.default;
const VModal = window["vue-js-modal"].default
Vue.use(VModal);
// FullCalendar
Vue.component('calender', {
  template: '#calender',
  data: function() {
    return {
      name: "",
      imgUrl: "",
      id: "",
      calendarPlugins: [ 
        dayGridPlugin, 
        FullCalendarInteraction 
      ],
      events: [],
      // mouseOvernによるモーダルの呼び出し
      eventMouseEnter: function(event, jsEvent, view) {
        var title = event.event.title;
        app.name = title.substring(0,title.indexOf(" "))
        app.imgUrl = event.event.url
        app.id = event.event.id
        app.show()
      },
      header:{
        left:   'title',
        center: 'addPostButton',
        right:  'today prev,next'
      },
      buttonText: {
        today: '今日'
      },
    }
  },
  mounted: function(){
    this.getData();
  },
  // spreadSheetからデータを取得
  methods: {
      getData: async function (event) {
        let response;
        try {
          response = await axios.get(
            'https://script.google.com/macros/s/AKfycbwzkyHjVYKu53Hh-zS5PBGebOsT5b0kLNtnJWJ7X7s1bxcV3Me-/exec'
          );
          var this_year = moment().get('year');
          for (var i = 0; i < response.data.length; i++) {
            for (var j = 0; j < 5; j++) {
                var name = response.data[i].name;
                //  生まれ年を現在年に置換する
                var now = moment(response.data[i].date).set('year', this_year);
                // 5年後まで表示
                var target_date = moment(now, 'YYYY-MM-DD').add(j, 'year');
                var current_date = moment().add(j, 'years');
                var birth_date = moment(response.data[i].date);
                var age = this.calcAge(current_date, birth_date);
                var color = response.data[i].member_color;
                var url = response.data[i].image;
                var id = response.data[i].id;
                this.events.push({title: `${name} ${age}歳`, date: target_date.format('YYYY-MM-DD'), color: `${color}`, url: url, id: id });
             }
          }
        } catch (error) {
          console.error(error);
        }
      },
      // 年齢の計算
      calcAge: function(today, dateOfBirth) {  
        let baseAge = today.year() - dateOfBirth.year();
        let birthday = moment(
            new Date(
                today.year() + "-" + (dateOfBirth.month() + 1) + "-" + dateOfBirth.date()
            )
        );
        return baseAge;
      } 
  }
});  

let app = new Vue({
  el: '#app',
  data: function () {
    return {
        name: "",
        imgUrl: "",
        id: ""
    }
  },
  methods: {
    show : function() {
      this.$modal.show('my_modal');
    },
    hide : function () {
      this.$modal.hide('my_modal');
    },
  }
});

結論

企画にも実装にも新たな推しメン探しにも、全てにさ迷った果てに、
出来上がったものに対しての一番の感想は、
これはGoogleCalandarで十分だな、ということです。
もう少し工夫を施さないと差別化は難しいなと感じます。
制作過程はとても楽しかったですが制作物でその楽しさをなかなか表現できませんでした。

新たな推しは見つからなかった、だが・・・

見出しの通りですが新たな推しは見つかりませんでした。
50人以上のプロフィーリと写真を何度もチェックしたけど
やはり皆さん素敵でした。選べません。
ライブも再開し始めたし早く生で見たいですね!

さて、今回はWEBアプリを作成してみましたが企画段階ですごく時間がかかりました。
自分の思いついたものって大体世の中に存在することの方が多い。
obnizのように目新しいものを使えば組み合わせ次第で目を引くものは作れそうですがWEBだけで表現するのはとても難しいと感じました。

ただ今回のようにAPIの代わりとしてSpreadSheetを作ったことで解決策の新しいパターンができたこと、
制作後の結論として既存のアプリとの差別化がうまくいかなかったことなどが早いうちから分かるなど、学ぶことが多かったです。

久しぶりのWEBアプリの作成でしたが過去に何度か試みた時は環境構築を終えた時点で力尽きることばかりでした。
仮想環境を用意し、サーバーやフレームワーク、DBをインストールするなどなかなか面倒な工程をふまなければなりません。
今回の環境構築的な作業は全てCDNを使用、DBもスプレッドシートを用意するだけという、とても簡単なものでした。
限りある時間を企画→制作→発表に割き、制作物のブラッシュアップに取り組む、このサイクルはとても合理的で良い手法だと思います。
まだまだ頑張ります。

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

【JavaScript】条件(三項)演算子(if文の省略形)の使い方

【JavaScript】三項演算子(if文の省略形)の使い方

if文を省略した形で記述できる三項演算子の使い方。
名称は条件演算子、三項演算子、条件参考演算子など呼び方はまちまち。

①条件式、②trueの処理、③falseの処理の3つの値を必要とするのが名前の由来。

<構文>
条件式 ? trueの処理 : falseの処理

if文との比較

三項演算子(数値)
10 > 0 ? 'Yes' : 'No' 
if文
if(10 > 0){
       'Yes'
    }else{
       'No'
    } 

if文よりもシンプルに記述できる。

使い方の例

条件式は変数も使える

三項演算子(数値)
ok = true
ok ? 'Yes' : 'No' 

真偽値を切り替える

三項演算子(数値)
isLargeToggle: function(){
    this.isLarge == true ? this.isLarge = false : this.isLarge = true          
},
isRedToggle: function(){
    this.isRed == true ? this.isRed = false : this.isLarge = true          
}
if文
isLargeToggle: function(){
    if(this.isLarge == true){
       this.isLarge = false
    }else{
       this.isLarge = true
    }
},
isRedToggle: function(){
    console.log(this.isRed)
    if(this.isRed == true){
       this.isRed = false
    }else{
       this.isRed = true
    }
}

三項演算子を使うと短く記述できる。

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

Vuetify Sparklinesに垂直ラベル、基準線がない件

Vuetifyで用意されている線グラフのコンポーネントSpark lines
https://vuetifyjs.com/ja/components/sparklines/

見た目も綺麗でぜひ利用したいと思い
プレイグラウンドで色々いじってみて気づいたのが、

各データポイントの下にラベルを表示するのは
show-labelstrueにすることでできるが

垂直ラベルを表示する機能がついていない

ということ。

スパークラインのVuetifyのスタンスとしては
あまり細かくデータを見せる気は無いようで、
https://vuetifyjs.com/ja/components/sparklines/#%E3%81%A1%E3%82%87%E3%81%A3%E3%81%A8%E4%BC%91%E6%86%A9

簡潔な情報を得るために、完全なチャートはやり過ぎかもしれません。グラデーションをいれたトレンドラインを使用すると、あまり多くの情報を表示することなく、ユーザーに十分な詳細を提供できます。

色のグラデで基準線を表現してるんですね。

基準線と垂直ラベルの
切実なリクエストはあるようですが、改めて対応予定を否定していました。
https://github.com/vuetifyjs/vuetify/issues/6173

あくまでSparklinesは動向をなんとなく見せるような感じで
例えばInstagramのアクティビティのような…
E0725ADD-B697-4DB5-BF72-D9BE8EA60BD4.jpeg
(基準線がない/垂直ラベルがなく水平方向のラベルのみ)
この程度の情報量だけで簡潔に伝える場合に使うものと考えた方が良さそうです。

結果、今回はより詳細な情報として線グラフの情報を出したいため、
Chartjsなどを使用することにしました。
https://www.chartjs.org/

参考になれば幸いです。

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

【Vue.js】QiitaのAPIを使ったリアルタイム検索プログラムの作成方法

【Vue.js】QiitaのAPIを使ったリアルタイム検索プログラムの作成方法

任意の検索KWを入力すると、該当するQiitaの最新20記事のタイトル、リンク、LGTM数を一覧で表示するプログラムの作成方法。

何かをDLしたりインストールする必要がない。ファイルもHTMLファイルとjsファイルの2つだけあれば作成可能。

▼完成図(ブラウザの表示例)

image.png

目次

  1. 使用するAPI
  2. 実装する機能
  3. 利用するソフトウェア
  4. ソフトウェアの読み込み
  5. オプションの用意
  6. dataオプションの準備
  7. methodsオプションの準備
    1. APIデータ取得条件の設定
    2. axiosの取得条件の設定
  8. createdオプションの準備
    1. createdオプションとは?(Vueライフサイクルの理解)
    2. createdの記述
    3. lodashとは?
    4. debounceとは?
  9. テンプレートの作成
  10. watchオプションの作成
  11. 完成版

実装する機能

  • 検索窓の設置。
  • 入力KWの変化を自動で検知し、自動検索を実行する。
  • 自動検索まで6秒間待機する(リクエスト上限があるため)
  • KWに該当する最新記事のtitle, リンク(URL), LGTMの一覧を表示
  • KW入力中は「Waitting 6 seconds for your typping...」を表示
  • Loading中は「Loading...」を表示
  • 検索窓が空白の場合は自動検索を実行しない。

使用するAPI

・API: Qiita API v2
・公式ページ:https://qiita.com/api/v2/docs
・API詳細:記事の一覧を更新日時の降順で返すAPI(GET item)
- 説明:https://qiita.com/api/v2/docs#get-apiv2items
- APIエンドポイント:https://qiita.com/api/v2/items

▼APIの種類は下記ページがわかりやすい。
https://qiita.com/tag1216/items/b0b90e30c7e581aa2b00

<注意点>
リクエスト回数制限あり
・認証しているユーザー:1時間に1000回まで
・非認証ユーザー: 1時間に60回まで

利用するソフトウェア

  1. Vue.js
  2. Qiita API v2
  3. HTTPクライアント axios
  4. ユーティリティライブラリ lodash
  • axios: 指定したURLのデータを取得するツール

- lodash: キーボード入力完了まで処理を待機するツールdebounceのライブラリ

ソフトウェアの読み込み

htmlファイルのbodyタグ上にCDNのURLを貼り付け。

.html
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
    <script src="https://cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.19/lodash.min.js"></script>

オプションの用意

jsファイルで使用するオプションは5つ。

  1. el
  2. data
  3. watch
  4. created
  5. methods
空のオプションを準備
var app1 = new Vue({
    el: '#app',
    data: {

    },
    watch:{

    },
    //HTTPリクエスト用。mountedでも可。DOMにアクセスする必要がなければcreatedの方が早い
    created: function(){

    },
    methods:{

    }
})

dataオプションの準備

3つのプロパティを準備する。

  1. items: APIから取得した検索結果を格納
  2. keyword: 検索クエリ
  3. message: ユーザーへの表示内容
dataオプション
data: {
        items: null,
        keyword: '',
        message: ''
    }

methodsオプションの準備

  1. axiosでAPIを叩きデータを取得するメソッド
  2. 検索クエリが未入力の場合はAPIを叩かない(処理終了)
  3. エラー発生時の処理
  4. データロード時の表示(Loading...)
methodオプション
methods:{
        getAnswer: function(){
            //検索クエリが空の場合は処理終了
            if(this.keyword === ''){
                this.items = null
                //2回目以降の検索時にメッセージを空にする
                this.message = ""
                return 
            }

            //ロード中の表示
            vm.message = 'Loading...'

            //Vueインスタンスを格納する変数を定義
            var vm = this

            //リクエスト条件を定義。1ページ表示、表示件数は20件、検索クエリはキーワードの入力値
            var params = {page:3, per_page:20, query: this.keyword}

            //axiosのgetメソッドでAPIをたたく
            axios.get('https://qiita.com/api/v2/items', params)
                .then(function(res){
                    console.log(res) //データ取得できたか確認用
                    this.item = res.data //itemプロパティに取得データを格納


                })
            //エラー発生時の処理
            .catch(function(error){
                vm.message = 'Error!' + error
            })

            //通信が完了したらLoadingの表示をクリア
            .finally(function(){
                vm.message = ''
            })
        }

    }

APIデータ取得条件の設定

APIデータ要求。必要な取得条件はQiita APIの公式ページに記載されている。
axios.get('URL', 取得条件)

取得データは「.then()」内で「functionの引数」の値に入る。
.then(function(res){処理})

APIの取得条件の確認

公式ページに記載されている3つのパラメータを記載する。
https://qiita.com/api/v2/docs#get-apiv2items

image.png

(1)page, (2)per_page, (3)queryが必要となる。

axiosの取得条件の設定

取得条件は①パラメータで直接URLに付与するか、②引数で指定することができる。

①パラメータで直接URLに付与
axios.get('https://qiita.com/api/v2/items?page=1&per_page=20&query=javascript')

②引数で指定
axios.get('https://qiita.com/api/v2/items', {page:3, per_page:20, query: this.keyword})

②'引数を変数に代入
axios.get('https://qiita.com/api/v2/items', {params})
 ※変数に波カッコをつける。ないと指定がない状態になってしまう。


createdオプションの準備

createdオプションとは?(Vueライフサイクルの理解)

jsファイル読み込み時に初期段階で実行する。
Vueインスタンス作成時のライフサイクルで、elを読み込む前の段階で実行される。

PHPなどのコンストラクターのようなイメージ。

もし、DOMを読み込む必要があれば、createdの代わりにmountedを使用する。

Vue公式ページのライフサイクル図

image.png

createdの記述

<デバッグ用>
1. 検索クエリのセット
2. APIを叩く(getAnswer)メソッドを実行

.js
    //初期実行動作。el読み込み前のタイミングで以下処理を実行する。
    created: function(){
        this.keyword = 'JavaScript'
        this.getAnswer()

<本番用>
上記2つをコメントアウトし、lodashライブラリのdebounce関数を設定する。

指定時間経過後に、APIを叩くgetAnswerメソッドを実行するプログラム。

getAnswerは検索ボックスが空白の時、if文で実行しないようになっている。(初回ロード時は何も発生しない)

.js
   //初期実行動作。el読み込み前のタイミングで以下処理を実行する。
    created: function(){
        // this.keyword = 'JavaScript'
        // this.getAnswer()
        this.debouncedGetAnswer = _.debounce(this.getAnswer, 6000)

lodashとは

JavaScriptの便利な関数をまとめたライブラリ。
lodashの機能一覧

lodashはVue同様CDNを読み込めば使える。

CDN
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.19/lodash.min.js"></script>

debounceとは

lodashに用意された関数の一つ。指定秒数経過後に、指定した関数を実行する。
キーボード入力が終わるまで待つ場合などに使える。

公式ページ:https://lodash.com/docs/4.17.15#debounce

_.debounce(func, ミリ秒)
 ┗ デフォルトはミリ秒 = 0


テンプレートの作成

  1. キーワード入力欄(v-modelで双方向バインディング)
  2. Loadingメッセージ
  3. 取得データ表示(タイトル名、リンク、LGTM数)
.html
    <div id="app">
        <p>
            <input type="text" v-model="keyword">
            <button v-on:click="getAnswer()">検索</button>
        </p>
        <p>
            {{ message }}
        </p>
        <ul>
            <li v-for="item in items">
                <!-- {{ item.title }}  -->
                <a v-bind:href=" item.url " target="_blank">{{ item.title }} </a> LGTM:{{ item.likes_count}}
            </li>
        </ul>
    </div>


watchオプションの作成

検索ボックスに新しいキーワードが入力されたら、表示を自動更新する。

  1. タイピング中に表示するメッセージの作成
  2. タイピング終了まで待機し、終了後にAPIへのリクエストメソッドを実行
watchオプション
 watch:{
        keyword: function(newKW, oldKW){
            //文字入力中にメッセージを表示
            this.message = 'Waitting 6 seconds for your typping...'
            //作成したgetAnswerメソッドを実行する
            this.debouncedGetAnswer()
        }

リクエスト回数上限があるため、6秒間待機するよう設計している。


完成版

.js
var app1 = new Vue({
    el: '#app',
    data: {
        items: null,
        keyword: '',
        message: ''
    },
    watch:{
        keyword: function(newKW, oldKW){
            //文字入力中にメッセージを表示
            this.message = 'Waitting 6 seconds for your typping...'
            //作成したgetAnswerメソッドを実行する
            this.debouncedGetAnswer()
        }

    },
    //初期実行動作。el読み込み前のタイミングで以下処理を実行する。
    created: function(){
        // this.keyword = 'JavaScript'
        // this.getAnswer()
        this.debouncedGetAnswer = _.debounce(this.getAnswer, 6000)


    },
    methods:{
        getAnswer: function(){
            //検索クエリが空の場合は処理終了
            if(this.keyword === ''){
                this.items = null
                this.message = ""
                return 
            }

            //ロード中の表示
            this.message = 'Loading...'

            //Vueインスタンスを格納する変数を定義
            var vm = this

            //リクエスト条件を定義。1ページ表示、表示件数は20件、検索クエリはキーワードの入力値
            var params = {page:3, per_page:20, query:this.keyword}

            //axiosのgetメソッドでAPIをたたく
            axios.get('https://qiita.com/api/v2/items', {params})
            // axios.get('https://qiita.com/api/v2/items', {page:3, per_page:20, query: this.keyword})
            // axios.get('https://qiita.com/api/v2/items?page=1&per_page=20&query=javascript')
                .then(function(res){
                    console.log(res) //データ取得できたか確認用
                    console.log(res.data)
                    console.log(vm.keyword)
                    vm.items = res.data //itemプロパティに取得データを格納 ※thisはNG
                })
            //エラー発生時の処理
            .catch(function(error){
                vm.message = 'Error!' + error
            })

            //通信が完了したらLoadingの表示をクリア
            .finally(function(){
                vm.message = ''
            })
        }

    }
})
.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Qiita API</title>
</head>
<body>
    <div id="app">
        <p>
            <input type="text" v-model="keyword">
            <button v-on:click="getAnswer()">検索</button>
        </p>
        <p>
            {{ message }}
        </p>
        <ul>
            <li v-for="item in items">
                <!-- {{ item.title }}  -->
                <a v-bind:href=" item.url " target="_blank">{{ item.title }} </a> LGTM:{{ item.likes_count}}
            </li>
        </ul>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
    <script src="https://cdn.jsdelivr.net/npm/axios@0.19.2/dist/axios.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/lodash@4.17.19/lodash.min.js"></script>
    <script src="js/main.js"></script>
</body>
</html>



以上。

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

【JS】JSを根本から再復習、スコープとかクロージャーとか

JS(VueやReact)をなんとなく使ってしまっているので、色々学習しています。
この記事は下記コースのアウトプットになります。
【JS】初級者から中級者になるためのJavaScriptメカニズム | Udemy

スコープ

  • グローバルスコープ
  • スクリプトスコープ
  • 関数スコープ
  • ブロックスコープ
  • モジュールスコープ

グローバルスコープ

windowsオブジェクト = グローバルスコープ

関数スコープ

function scope() {
  // ここに囲まれたスコープのこと指します
}

ブロックスコープ

{}で囲まれた範囲のことを指します。
varを使った場合は、ブロックスコープは生成されません。
var,let,constの違いは、ブロックスコープと巻き上げ - 30歳からのプログラミング

レキシカルスコープ(外部スコープ)

実行中のコードから見た外部のスコープのことを指します。
image.png

// 下記の場合、関数 a がレキシカルスコープになります。
function a() {
  function b() {
    console.log('だだだだだ')
  }
}

クロージャー

レキシカルスコープを関数としてしようしている状態のことをさします。

// 下記のような状態のこと
function soto() {
  let aaa = "test"
  function uchi() {
    console.log(aaa)
  }
}

プライベート変数を設定する

外部からアクセスできない変数を定義します。

function incrementFactory() {
    // 外部からアクセスできない変数
    let num = 0;
    function add() {
        num += 1;
        console.log(num);
    }
    // 関数addを返している
    return add;
}

// 変数に関数の実行を定義する
const increment = incrementFactory();

increment(); // 1
increment(); // 2

// プライベート担っているため、外部からは呼び出せない
console.log(num) // エラー

動的な関数

function addNumberFactory(num) {
    function addNumber(value) {
        return num + value;
    }
    return addNumber;
}

const add5 = addNumberFactory(5);
const add10 = addNumberFactory(10);
const result = add10(10);
console.log(result);

あとこんな書き方もできます。知らんんかった、、、

function factory(val) {
    return {
        plus: function(target) {
            const newVal = val + target;
            console.log(`${val} + ${target} = ${newVal}`);
            val = newVal;
        },
        minus: function(target) {
            const newVal = val - target;
            console.log(`${val} - ${target} = ${newVal}`);
            val = newVal;
        },
    };
}

const cal = factory(10);
cal.plus(5);
cal.minus(5);

即時関数

関数定義と同時に一度だけ実行される関数のことをさします。

実装は以下のようになります。
最初のカッコは、グループ化をさし、次のカッコは、関数の実行のことをさします。
基本的に function には名前をつけなければいけないので、()を外したらエラーになります。

// 通常の関数
function test() {
  console.log('called');
}

// 即時関数
(function() {
  console.log('called');
})()

// 変数に入れる場合は、
const c = (function() {
  let val = 10;

  function fn() {
    console.log('fn is called');
  }

  return {
    val,
    fn
  };
})()

// 関数の呼び方
c.fn();
// 変数の呼び出し方
console.log(c.val);

暗黙的な型変換

変数が呼ばれた状況によって、変数の型が自動的に変換されるので、覚えておく。

プリミティブとオブジェクト

プリミティブ

オブジェクト以外の物を全て指します。
具体的には、 String, Null, Symbolなどですね。
イミュータブルで、再代入の場合は、値の参照が切り替わります。

コピー

参照先の値がコピーされます。
元の変数を変更しても、元の変数、代入した変数は独立しているので、値は変わりません。

// address に a という変数を使って、値が読み込まれています。
let a = "Yo"
// "Yo"自体(値)がコピーされます
let b = a
// bの向き先(値)が変わります。
b = "Taka"

image.png

再代入

constを使う場合、もちろん再代入ができません。
つまり、constを定義した場合は、変数 aの向き先を変更できなくなるのです。

const a = "Yo"
a = "Taks"

オブジェクト

ミュータブルで、参照を名前付きで管理している入れ物です。
この時のミュータブルの意味は、"Yo"の値が変わったとしても、propへの参照が変わらないことから、ミュータブルと呼ばれます。

let a = {
  prop: "Yo";
}

image.png

コピー

下記図の通り、オブジェクトへの参照がコピーされることになります。(背景色が異なっているところ)
{ prop } がみている値が変更されるので、元の変数の値も変更されることになります。

let a = {
  prop: "Yo"
};

let b = a;

b.prop = "Taka";

image.png

再代入

オブジェクトの再代入はできないが、オブジェクト内のプロパティは再代入できます。

比較

オブジェクトのプロパティを比較する必要があり、オブジェクトの比較をしても、参照を比較することになるので、期待する結果とはなりません。

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

VueCLI3導入方法

自身のメモ用に記録しておきます。

VueCLIのインストール

//オプショングローバルをつける
npm install -g @vue/cli

vue -v
@vue/cli 4.4.6

プロジェクトの作成

vue create traning-vue.js
?  Your connection to the default yarn registry seems to be slow.
   Use https://registry.npm.taobao.org for faster installation? No
//今回はdefaultを選択
?  Your connection to the default yarn registry seems to be slow.
   Use https://registry.npm.taobao.org for faster installation? No
Vue CLI v4.4.6
✨  Creating project in /Users/XXXX/training-vue.js
⚙️  Installing CLI plugins. This might take a while...

アプリケーションの起動

//開発環境のサーバーを立ち上げる
cd training-vue.js
npm run serve
packeage.json
{
  "name": "training-vue.js",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    //ここ
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint"
  },

ビルド

npm run build

本番環境用に最適化された、distディレクトリが作成される。
image.png

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

班決めを考えるのが嫌すぎて「良い感じに班決め君」作ってみた

はじめに

コロナ前を想像してみてください。

飲み会で居酒屋にいざ着いたとき、みんながみんな
「どこの卓に行こう」
「先輩に先に席選んでもらわないと」
「新人の子はこの席のほうがいいよね」
とか考えてナッカナカ席につかない、壮絶な牽制大会が始まることありませんか?
僕は鳥貴族でよくありました。
安くてしかも旨いので大人数で行きがちなんですよね。

仕事であれば、例えばグループディスカッションをするとなったときに、どんな班分けにするかって地味に悩むと思います。
そこまで深く考えずえいやで決めてしまえばいいとは思うんですが、いざ班分けをする場面になると「この人とこの人は関わりが薄いよなあ」とか「ここ若手一人になっちゃうわ」とかいろいろ考えちゃうんですよね。

色々情報をインプットして、自動で班分けをしてくれるツールあればなあと思い、作ってみちゃいました。
アプリはデプロイして試験利用できるようにあるので是非是非利用してみてくださいね。
コードはGistをご参照ください!
App: https://dazzling-albattani-2c12ef.netlify.app/hangimekun.html
Gist: https://gist.github.com/canonno/e08ba37eff5e736aa50cd08b08b9ec01

全然関係ないですけど「班」って響きなんか小学生っぽくて懐かしいですよね

完成デモ

班の数・メンバーインプット

まず班の数を決めます。
今回は最大6人班の個数のみを設定できるようにしています。
4人班と6人班を組み合わせた場合も考えたんですが、トリッキー過ぎて断念しました。

そのあと参加者の属性をインプットします。
偉い人・先輩・新人の身分3種類と、男女の性別2種類の合計6属性の人数をインプットします。
ここでインプットすると、原則同じ属性の人がばらけるように席が配置されます
例えば新人は同じ班には固まらないとか、女性が同じ班に固まらないとか、というイメージですね。

いざ班決め

GOボタンを押すと下の図に班分けが描画されます。
コロナ収束後に飲み会の席決めにも使えるように、机っぽい配置にしてあります。
青色が男性・ピンクが女性になっています。

ロジックの中に乱数を組み込んでいるので、ボタンを押すとまた新しい配置になります。
この配置気に食わんなという場合はボタンを連打して良い感じの配置を探すという八百長も可能になっています。

もちろん、一度班分けをした後に条件変更しても大丈夫です。
一つの班の人数調整に利用できそうですね。

応用テク

例えば偉い人男性2人・先輩男性6人女性1人・新人男性2人女性1人で合計12人の時を考えます。
女性が2人しかおらず、女性2人を同じ卓に入れたいとしましょう。
こういう時は女性2人を、先輩男性6人に足して8人とみなして実行します。

すると、こんな結果になります。
image.png

この結果を受けて後出しでどこかの班の先輩男性2人を女性2人に置き換えると、必然的に女性2人を同じ班にすることができます。
ややトリッキーですが、意外といろんな用途に使えるのかなあとかぼんやり思っていたりします。
「この人たちは固める」みたいな機能をつけたかったんですが、ロジックが地味難しく途中で力尽きました。
いつかジツゲンシテェ

環境

Visual Studio Code 1.47.1

手順

いい感じのヘッダーを付ける

Progateの「HTML&CSS学習コース 初級編」というオンライン講座をかなり参考にしました。
ベイビーステップで少しずつ少しずつ、手を動かしながら勉強できるのでProgateかなりおすすめです。
オンラインですのでコロナ自粛の間是非是非お試しくださいね。
(Progate: https://prog-8.com/courses)

image.png
こんなのがサクサクつくれます

条件インプット部分作る

まずボタンをポチー押して条件がニョロンと出る部分については、簡単に見栄えのいいアイテムを追加できるBootstrapのAccordionというコンポを利用しました。
人数を選ぶラジオボタンもBootrstrapのButton Toolbarを利用しています。
型にペコペコいれるだけで見栄え良く仕上がるので是非利用してみてくださいね。

(Accordion: https://getbootstrap.com/docs/4.5/components/collapse/)
(Button Toolbar: https://getbootstrap.com/docs/4.5/components/button-group/)

<!--Bootstrapのアコーディオンを利用-->        
    <div class="accordion" id="accordionExample">
      <div class="input">
        <!--班の数指定カード-->
        <div class="input-float">
          <div class="card" style = "width:18rem;">
            <div class="card-header" id="headingOne">
              <h2 class="mb-0">
                <button class="btn btn-link btn-block text-left collapsed" type="button" data-toggle="collapse" data-target="#collapseOne" aria-expanded="false" aria-controls="collapseOne">
                  <h3 class="section-title"></h3>
                </button>
              </h2>
            </div>

            <div id="collapseOne" class="collapse" aria-labelledby="headingOne" data-parent="#accordionExample">
              <div class="card-body">
                <!--4人班の数を選ぶボタン-->
                <div class = "condition-items">
                  <p>最大4人班</p>
                  <div class="btn-group btn-group-toggle" data-toggle="buttons">
                    <label class="btn btn-secondary active">
                      <input type="radio" name="yoninseki" id="option0" checked> 0
                    </label>
                    <label class="btn btn-secondary">
                      <input type="radio" name="yoninseki" id="option1" disabled> 1
                    </label>
                    <label class="btn btn-secondary">
                      <input type="radio" name="yoninseki" id="option2" disabled> 2
                    </label>
                    <label class="btn btn-secondary">
                      <input type="radio" name="yoninseki" id="option3" disabled> 3
                    </label>
                    <label class="btn btn-secondary">
                      <input type="radio" name="yoninseki" id="option4" disabled> 4
                    </label>
                  </div>
                </div>

                <!--6人班の数を選ぶボタン-->
                <div class = "condition-items">
                  <p>最大6人班</p>
                  <div class="btn-group btn-group-toggle" data-toggle="buttons">
                    <label class="btn btn-secondary active">
                      <input type="radio" name="rokuninseki" id="option0" value=0 disabled> 0
                    </label>
                    <label class="btn btn-secondary">
                      <input type="radio" name="rokuninseki" id="option1" value=1 disabled> 1
                    </label>
                    <label class="btn btn-secondary">
                      <input type="radio" name="rokuninseki" id="option2" value=2 checked> 2
                    </label>
                    <label class="btn btn-secondary">
                      <input type="radio" name="rokuninseki" id="option3" value=3> 3
                    </label> 
                    <label class="btn btn-secondary">
                      <input type="radio" name="rokuninseki" id="option4" value=4> 4
                    </label>
                  </div>
                </div>
              </div>
            </div>
          </div>
        </div>

これが
image.png
これになりますベンリィ!

このまま次のAccordionを作ると、下へ下へボタンができていきます。
今回作成したように、右側にボタンを作りたい場合はcss側でfloat:leftを指定してあげましょう。
ただ指定すると今度は下へ作ってくれなくなるので、下へ追加したくなったらclear:leftを追加しましょう。
(参考:https://udemy.benesse.co.jp/development/web/css-float.html)

.input-float{
  padding: 0px 20px 0px 0px;
  float:left;
}

.contents {
  padding: 20px 20px;
  margin-top: 20px;
  clear:left;
}

クリックしたときのロジック

最初に考えたこと

GOボタンを押すと、getMemberメソッドが走って条件の読み取りと班決めを行ってくれます。
一番シンプルな班決めロジックはこれではないでしょうか。
image.png

条件から「偉男,偉男,先男,先男・・・」のようなメンバーリストを作り、左から「1班、2班、3班、4班、1班、、、」と決める手法です。
これであれば同じ属性の人はばらけそうですよね。
ただ、全て結果が同じになってしまってつまらないなあと思い、この部分に一つランダム要素を仕込んでみました。

実装したロジック

今回実装したロジックの全体像はこちらです。
大きな変更点は、一度作ったメンバーリストから、ランダムに(だけどランダム過ぎない程度に)組み替えて新しいメンバーリストを作る部分です。
image.png

このちょこっとランダム要素の具体的な処理はこちら。
全部をランダムにしてしまうと、属性で分ける意味がなくなってしまいますよね。
なので敢えて回りくどく、「先頭の4人からランダムで取り出し」⇒「新しいメンバーリストに追加」の操作を繰り返し、全体の傾向が崩れすぎない程度にランダムにシャッフルしています。
こうすることで、前半に男性がきて後半に女性がくるという全体の傾向は保持しつつ、シャッフルすることができます(たぶん)。

image.png

ジャストアイデアで実装した感じなので、実際効果あるのか検証していませんがきっとうまくいっていると信じていますスミマセンンンンン

班分けを描画する

班分けの描画にはp5jsというライブラリを使いました。
絵も描けるしマウスとインテラクティブな実装もできるし、使い勝手がめちゃめちゃ良いライブラリになっています。
是非こちらも使ってみてくださいね。
p5js: https://p5js.org/examples/

画面更新した際にはまずsetup()が実行され、描画スペースの作成と机の描画が行われます。

//画面更新の度に実行、描画範囲の新規作成
      function setup() {
        // Create the canvas
        createCanvas(920, 300);
        background(235);

        // Set colors
        fill(126, 149, 230, 127);
        stroke(0, 0, 0);
        for (i = 0;i<4;i++){
          rect(40+i*210, 70, 180, 70);
        }
      }

その後、先ほどのシャッフルしたメンバーリストを受け取り一人ずつ描画していく処理が走ります。

      //テーブルに描画していく
      function maketable(tablelist,numoftable){
        // A rectangle x座標、y座標,横,縦
        // An ellipse
        textSize(24);
        textAlign(CENTER);

        //ひたすら一人ずつ配置(テーブル左上)
        for (i=0;tablelist.length != 0 && i<numoftable ;i++){     
          gender = tablelist[0].substr(1,1);
          //「偉男」の二文字目を取り出し、男女によって色を変える
          if (gender==""){
            fill(126, 149, 230, 127);
            console.log("男や");
          }else if (gender==""){
            fill(255, 140, 255 , 127);
            console.log("女や");
          }

          //「偉男」の一文字目を取り出し描画
          mibun = tablelist[0].substr(0,1);
          ellipse(75+i*210, 40, 40, 40);
          text(mibun, 75+i*210, 50);
          tablelist.shift();
          console.log(tablelist);
        }

        //テーブル右上
        for (i=0;tablelist.length != 0 && i<numoftable ;i++){     
          gender = tablelist[0].substr(1,1);
          if (gender==""){
            fill(126, 149, 230, 127);
            console.log("男や");
          }else if (gender==""){
            fill(255, 140, 255 , 127);
            console.log("女や");
          }

・・・
・・・
・・・

あとはhtmlに仕込むだけ!

この作業が地味に難しかったんですがなんとかできました!
Bootstrap君スゲェッスマジデ
コードの全体像はGistに載せたので是非ご覧になってくださいね。
Gist: https://gist.github.com/canonno/e08ba37eff5e736aa50cd08b08b9ec01

さいごに

これで少しはストレスフリーに班決めができるのではないでしょうか!
いや個人それぞれをどこに配置するか結局決める必要あるやんとか言わないで
個人的には名前のインプット欄も作って、それを描画できたらいいなあとか思ってたんですが、人数が増えたときに全員分名前入れるの?と思い断念。
良いUI思いついた人いらっしゃいましたらコメントいただけると嬉しいです!

最後までご覧いただきありがとうございました!
Qiita毎週投稿頑張っております、LTGMいただけると励みになります!
何卒宜しくお願い致します!

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

MobileNetでチー牛診断

See the Pen チー牛 by John Doe (@04) on CodePen.

スマホの方は こちら

MobileNetで顔写真からチー牛顔かどうか判定するアプリ作りました。

チー牛とは

チーズ牛丼好きそうな顔のことです。

人工知能なら特徴量を獲得できるとのことなので、
チーズ牛丼好きそうな顔の特徴量も獲得できると考えました。

teachablemachine.withgoogle.com_train_image (3).png

TensorFlowで学習しており、精度も大変高いです。

ご自分の顔がチーズ牛丼好きそうな顔かどうか高い精度で判断できます。

ぜひ試して見えてください

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

【Vue.js】監視プロパティ(watchオプション)の使い方

【Vue.js】監視プロパティ(watchオプション)の使い方

監視プロパティ、別名ウォッチャ。
指定したデータに変化があった場合に、登録した処理を自動的に実行する。

相互変換が可能になる。

目次

  1. 監視プロパティの書き方
  2. 距離の相互変換プログラム
  3. 算出プロパティと監視プロパティの比較
  4. 監視プロパティのオプション
    1. deep
    2. immediate

監視プロパティの書き方

▼.jsファイル
watch:{監視するプロパティ名: function(新しい値用の変数, 古い値用の変数){処理}}

▼console.logで変更前後のテキストを表示する

.js
var app = new Vue({
    el:"#app",
    data:{
        hello: 'Hello World!'
    },
    watch:{
        hello: function(newValue, oldValue){
            console.log('new: %s, old: %s',newValue, oldValue)
        }
    }  
})

書式化演算子(%d)

書式化演算子%による書式指定。
・%d : 文字列

▼.htmlファイル

<body>
    <div id="app">
        <p>
            {{ hello }}
        </p>
        <p>
            <input type="text" v-model:value="hello">
        </p>
        <pre>{{ $data }}</pre>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
    <script src="js/main.js"></script>
</body>

距離の相互変換プログラム

ウォチャーを使い、km, m, mmを相互に変換するプログラムを作成する。

inputタグを3つ用意し、どれか一つの入力内容を変更すると連動して変化する。

作成手順

  1. 初期値0のdataオブジェクトを用意(.js)
  2. inputタグを用意(.html)
  3. v-modelで双方向バインディング(.html)
  4. watchプロパティをセット(.js)

▼完成形(ブラウザ表示)
image.png

.js
var app = new Vue({
    el:"#app",
    // 1. 初期値0のdataオブジェクトを用意(.js)
    data:{
        km:0,
        m:0,
        mm:0
    },
    // 4. watchプロパティをセット(.js)
    watch:{
        km:function(value){
            this.km = value
            this.m = value*1000
            this.mm = value*1000000
        },
        m:function(value){
            this.km = value/1000
            this.m = value
            this.mm = value*1000
        },
        mm:function(value){
            this.km = value/1000000
            this.m = value/1000
            this.mm = value
        }
    }
})

▼.htmlファイル

<body>
    <div id="app">
        <!-- 2. inputタグを用意(.html) -->
        <p>
            <!-- 3. v-modelで双方向バインディング(.html) -->
            <input type="text" v-model="km">km
        </p>
        <p>
        <input type="text" v-model="m">m
        </p>
        <p>
            <input type="text" v-model="mm">mm
        </p>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
    <script src="js/main.js"></script>
</body>


算出プロパティと監視プロパティの比較

どちらでも書ける場合は記述が簡易な算出プロパティを使う。

・算出プロパティ:returnで返す
・監視プロパティ:代入する
┗ 監視する値の数だけ数式を記述する必要がある。

▼コード比較

算出プロパティ
    computed:{
        fullName: function(){
            return this.firstName + ' ' + this.lastName
        }
    }
監視プロパティ
    watch:{
        firstName: function(value){
            this.fullName = value + " " + this.lastName
        },
        lastName: function(value){
            this.fullName = this.lastName + " " + this.firstName
        }
    }

▼フルコード

.js(算出プロパティの場合)
var app = new Vue({
    el:"#app",
    data:{
        firstName:'',
        lastName:'',
        fullName:''
    },
    computed:{
        fullName: function(){
            return this.firstName + ' ' + this.lastName
        }
    }
})
.js(監視プロパティの場合)
var app = new Vue({
    el:"#app",
    data:{
        firstName:'',
        lastName:'',
        fullName:''
    },
    watch:{
        firstName: function(value){
            this.fullName = value + " " + this.lastName
        },
        lastName: function(value){
            this.fullName = this.lastName + " " + this.firstName
        }
    }
})
.html
<body>
    <div id="app">
        <p>
            firstName: <input type="text" v-model:value="firstName">
        </p>
        <p>
            lastName: <input type="text" v-model:value="lastName">
        </p>
        <p>
            fullName: {{ fullName }}
        </p>

        <br>
        <pre>{{$data}}</pre>

    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
    <script src="js/main.js"></script>
</body>


監視プロパティのオプション

監視プロパティにはオプションが用意されている。

  1. deep
  2. immediate

使い方

・処理をhandlerプロパティにする。
・handler, deep, immediateが並列な要素となる。

deep

trueの場合、ネストされたオブジェクトも監視する。

deep = true :ネストしたオブジェクトも監視
deep = false :ネストは監視しない。デフォルト

deep実例

colorsプロパティをwatch対象にし、deep= trueのとき。colorsプロパティ配下のnameプロパティを変更したときに、console.logで変更有無を感知する。

image.png

.js
var app1 = new Vue({
    el: '#app',
    data: {
        colors: [
            {name: 'Red'},
            {name: 'Green'},
            {name: 'Blue'}
        ]
    },
    //▼deepオプションありの監視プロパティ
    watch: {
        colors:{
            handler: function(newVal, oldVal){
                console.log('deepが適用されました!')
            },
            deep: true
        }
    }
})
.html
<body>
    <div id="app">
        <ul>
            <li v-for="color in colors">
                {{ color.name }}   
            </li>
        </ul>
        <hr>
        <p>(確認用)dataオブジェクト</p>
        <pre>{{ $data }}</pre>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
    <script src="js/main.js"></script>
</body>

immediate

初期読み込み時にも呼び出す。デフォルトはfalseになっている。
deepとの併用も可能。

immediate = ture: 初回読み込み時に監視プロパティの処理を実行。
immediate = false: 初回は実行しない。



▼immediate = tureにし、ページをロードした場合

image.png

.js
var app1 = new Vue({
    el: '#app',
    data: {
        colors: [
            {name: 'Red'},
            {name: 'Green'},
            {name: 'Blue'}
        ]
    },
    //▼deepオプションありの監視プロパティ
    watch: {
        colors:{
            handler: function(newVal, oldVal){
                console.log('deepが適用されました!')
            },
            deep: true,
            immediate: true
        }
    }
})
.html
<body>
    <div id="app">
        <ul>
            <li v-for="color in colors">
                {{ color.name }}   
            </li>
        </ul>
        <hr>
        <p>(確認用)dataオブジェクト</p>
        <pre>{{ $data }}</pre>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
    <script src="js/main.js"></script>
</body>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Vue.js】ChromeのConsoleからプロパティを変更する方法

【Vue.js】ChromeのConsoleからプロパティを変更する方法

Chrome DevツールのConsoleからプロパティの値を変更することができる。

目次

  1. Devツールの起動方法
  2. 変更方法
  3. 入れ子のプロパティの場合
  4. 配列の場合
  5. 確認用プログラム


Devツールの起動方法

Devツールの起動は、F12。
もしくはMACならcommand + option + I。Windowsはctrl+shift+I。(optionかshiftかの違い)

変更方法

DevツールのConsoleタグを開き、
Vueインスタンスの変数.プロパティ名 = "変更後の値"

var app1 = new Vue({
    el: '#app',
    data: {
        number: 100,
    }
})

上記のnumberプロパティの数値を変更するには、
app1.number = 1234と入力して、Enterをクリックする。

image.png

100が1234に変更となる。


入れ子のプロパティの場合

プロパティが入れ子になっている場合は、「.」でつないでアクセスする。

var app1 = new Vue({
    el: '#app',
    data: {
         user:{
            name: 'Tanaka',
            age: 27
         }
     }
})

上記のuserプロパティのnameプロパティの値を変更するには、
app1.user.name = "Steve"とする。

image.png

nameプロパティの値がSteveに変更となる。


配列の場合

配列番号を指定することで変更できる。

var app1 = new Vue({
    el: '#app'
    colors: [
            {name: 'Red'},
            {name: 'Green'},
            {name: 'Blue'}
        ]
    }
})

app1.colors[2].name = "オレンジ色"
配列番号2(3番目の要素)を変更する。

image.png

確認用プログラム

.js
var app1 = new Vue({
    el: '#app',
    data: {
        number: 100,
        user:{
            name: 'Tanaka',
            age: 27
        },
        colors: [
            {name: 'Red'},
            {name: 'Green'},
            {name: 'Blue'}
        ]
    },
    //色が変更された場合に、consoleに「updated!」と表示する監視プロパティ。
    watch: {
        colors:{
            handler: function(newValue, oldValue){
                console.log('Updated!')
            },
            deep: true
        }
    }
})

.html
<body>
    <div id="app">
        <p>
            ①数字: {{ number }}
        </p>
        <p>
            ②ユーザー名: {{ user.name }}
        </p>
        <span>③Color</span>
        <ul>
            <li v-for="color in colors">
                {{ color.name }}   
            </li>
        </ul>
        <hr>
        <p>(確認用)dataオブジェクト</p>
        <pre>{{ $data }}</pre>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
    <script src="js/main.js"></script>
</body>

▼ブラウザの表示
image.png





consoleはいろいろな使い方がありそう。

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

Vue.js ユニットテストの基本まとめ

Vue.js アプリでユニットテストを書くには、Vue Test UtilsJestなど、知っておくべきことがそれなりにあります。

現在、Vue CLI でアプリを作っていますが、ユニットテストを書くために色々と調べないといけませんでした。

今回はその過程で理解した Vue.js でのユニットテストの基本を以下にまとめます。

Vue.jsのユニットテスト

まず、Vue.js では何を「ユニットテスト」として考えるのかを整理します。

ユニットテストの単位

Vue.js アプリは、複数のコンポーネントで構成され、それぞれのコンポーネントが連動しながら動きます。

そのため、ユニットテストの単位は「コンポーネント」となり、コンポーネントごとにテストを書いていきます。

何をテストすべきか?

コンポーネントごとにユニットテストを書くということですが、コンポーネントのどの部分に対してテストを書くべきでしょうか?

もちろん、細かいことはそれぞれの開発方針によって異なりますが、基本的には、パブリックインターフェースの部分(インプット / アウトプット)についてユニットテストを書くべきです。

ユニットテストの目的は、各コンポーネントが意図したとおり動くかどうかです。なので、内部のビジネスロジックや各関数を1行1行気にするのではなく、インプットに応じた適切なアウトプットを得られるかどうかに着目します。

Vue コンポーネントの具体的なインプット・アウトプットの例は次のとおりです。

インプットの例

  • コンポーネントのdata
  • コンポーネントのprops
  • ユーザのアクション(ボタンクリックなど)
  • ライフサイクルメソッド(mounted(),created()など)
  • ステート管理のデータ
  • ルーティングのパラメータ

Unit Testing in Vue: What to Test? - Vue Test Utils」より引用

アウトプットの例

  • DOMへの描画
  • コンポーネントから呼び出すイベント
  • ルーティングの変化
  • ステートの更新
  • 子コンポーネントとの連動

Unit Testing in Vue: What to Test? - Vue Test Utils」より引用

Vue.js のテストツール

続いて、Vue.js で使われる一般的なテストツールを整理します。

Vue Test Utils

Vue.js では一般的に、標準テストライブラリのVue Test Utils を使ってテストを書きます。

具体的な書き方は後述しますが、テストコードは次のような感じで書きます。

Guide - Vue Test Utils」より引用

test.js
import { mount } from '@vue/test-utils'
import Counter from './counter'

describe('Counter', () => {
  // Now mount the component and you have the wrapper
  const wrapper = mount(Counter)

  it('renders the correct markup', () => {
    expect(wrapper.html()).toContain('<span class="count">0</span>')
  })

  // it's also easy to check for the existence of elements
  it('has a button', () => {
    expect(wrapper.contains('button')).toBe(true)
  })
})

Jest

JestはFacebook によって作られたオープンソースの JavaScript テストフレームワークです。

Vue.jsのアプリでは、この Jest を使ってVue Test Utilsで書いたテストコードを実行するのが一般的です(テストランナー)。

公式ドキュメントでは、mocha-webpackというテストランナーも紹介されていますが、 Jest の方が初期設定が簡単(らしい)ので、以下では Jest を使っていきます。

Vue CLIのユニットテスト

続いて、実際に Vue CLI でユニットテストを書くために必要なことを整理します。

コード例はこちら(GitHub)

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

あなたにピッタリなジェイソン・ステイサムの映画を教えてくれるアプリを作りました【Vue.js】

はじめに

こんにちは。ジェイソン・ステイサムが大好きなたわちゃんと申します。
(参照:「ジェイソン・ステイサムで妄想するのが日課になっていたので、いっそBOTにしてみた。」https://qiita.com/twtjudy1128/items/88f3e8f09c449f49456c

今回も愛情が溢れすぎた結果、ステイサム関連のアプリを作成してしまいました。
ステイサムが好きな方も、そうでない方も、ぜひ一度お試しいただけますと幸いです。

目的

もっとステイサムの魅力を伝えたい
ステイサムの映画をみんなにも見てほしい
なんならステイサムと結婚したい
・Vue.jsを使ったフロントエンドの実装に挑戦する

完成したもの

▼コチラが実際に作ったものです!
https://trusting-yalow-80ebbf.netlify.app/jason.html

2枚の画像から、気になるステイサムたんの画像を選んでいきます。
それを10回繰り返すと・・・

最終的に選んだ画像を元に、あなたにオススメのステイサムたんの映画を教えてくれます!より魅力が伝わるように、YoutubeからTrailerを埋め込んでます♪

作り方

▼ソースコードの全貌はGistから
https://gist.github.com/twtjudy1128/86f9adb21072c6ffc5f79bb28049872f

フレームワーク・ライブラリなど

・Vue.js
・Youtube Player API
・Bootstrap

画像の切り替え部分

Bookstrapのcardを使って、まず枠組みを作ります!

      <div class="card-group">

      <!-- A card -->
      <div class="card bg-dark" style="width: 30rem;" v-show="show">
        <img v-bind:src="a.imgsrc" class="card-img-top" alt="img-thumbnail" />
        <div class="card-body">
          <button class="btn btn-lg btn-block btn-danger" v-on:click="clickA()" ></button>
        </div>
      </div>

      <!-- B card -->
      <div class="card bg-dark" style="width: 30rem;" v-show="show">
        <img v-bind:src="b.imgsrc" class="card-img-top" alt="img-thumbnail" />
        <div class="card-body">
          <button class="btn btn-lg btn-block btn-danger" v-on:click="clickB()" ></button>
        </div>
      </div>

script部分に全画像分の画像URL、動画URL、紹介文をまとめて、配列がシャッフルされるようにします!クリックされるごとに、crickCountが増えていくので、その数字と同じ配列番号の画像が出てくるようにすることで、重複しないようにしました!

   <script>
      let clickCount = 0;
      const app = new Vue({
        el: '#app',
        data: {
          show: true,
          show2: false,
          message : '',
          a: {
            imgsrc: '',
            movie:'',
            resultText:''
          },
          b: {
            imgsrc: '',
            movie:'',
            resultText:''
          },
          imgs: [
            {//メカニック
              imgsrc: 'https://cdn-ak.f.st-hatena.com/images/fotolife/u/unias_tawa/20200804/20200804083430.jpg',
              movie: "https://www.youtube.com/embed/CMklQNn0OH0",
              resultText: "あなたにオススメな映画は「メカニック」何事も完璧なスマートなステイサムたんが見れるよ!"
            },
            //(省略しますが、ここに全画像分の情報を入れます)
          ],
        },
        async created() {
          // 配列をシャッフル
          this.imgs.sort(() => Math.random() - 0.5);
          // 最初の画像もシャッフルした0と1番目
          this.a.imgsrc = this.imgs[0].imgsrc;
          this.a.movie = this.imgs[0].movie;
          this.a.resultText =this.imgs[0].resultText;
          this.b.imgsrc = this.imgs[1].imgsrc;
          this.b.movie = this.imgs[1].movie;
          this.b.resultText =this.imgs[1].resultText;
          // 次に出てくる画像の配列番号
          clickCount = 2;
        },
        methods: {
          //Aボタンを押したら、Bの画像を次の画像に切り替え
          clickA: function () {
            console.log('入れ替える配列番号:' + clickCount);
            //ランダムで画像変更
            this.b.imgsrc = this.imgs[clickCount].imgsrc;
            this.b.movie = this.imgs[clickCount].movie;
            this.b.resultText =this.imgs[clickCount].resultText; 
            clickCount++;                  
          },
          //Bボタンを押したら、Aの画像を次の画像に切り替え
          clickB: function () {
            console.log('入れ替える配列番号:' + clickCount);
            //ランダムで画像変更
            this.a.imgsrc = this.imgs[clickCount].imgsrc;
            this.a.movie = this.imgs[clickCount].movie;
            this.a.resultText =this.imgs[clickCount].resultText;
            clickCount++;
          }
        }
      });

結果画面への切り替え

まず、divにshow2のクラスをつけて、初期値をfalseにして非表示にしておきます。(前述のコード参照)

      <div class="result" v-show="show2">
         <!-- Youtube -->
         <iframe width="640" height="360" v-bind:src="movie"
         frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

        <h4><br>{{message}}</h4>
      </div>

そして、scriptでclickCountが11回以上になったら、画像画面を非表示にし、結果画面が表示されるようにしました!

        methods: {
            clickA: function () {
               if(clickCount > 11){
                  this.show = !this.show;
                  this.show2 = true;
                  this.movie = this.a.movie;
                  this.message = this.a.resultText;
                }
            },
            clickB: function () {
              if(clickCount  > 11){
                 this.show = !this.show;
                 this.show2 = true;
                 this.movie = this.b.movie;
                 this.message = this.b.resultText;
              }
            }
          }

作ってみた感想・反省点

こうやって見ると比較的シンプルですが、初心者の私は制作に15時間以上かかりました。とても苦しかったですが、当初に思い描いていたものが作れた喜びは大きいです。

そして、何よりステイサムがかっこいい。

彼の画像や動画があったからこそ、15時間も向き合えたのだと思います。
もうほんと好き。大好き。

HTMLとCSSも今回初めてまともに触れたのですが、フロントエンド楽しいですね。
それがわかったのも嬉しいなぁって思いました。

反省点は、スマホ最適化ができなかったことと、できればポップアップみたいな感じで動画を出したかったことです。プラスアルファの実装ができるように、早く基礎を積みたいです!

おわりに

是非アプリ使ってみて、どの映画をオススメされたか、コメントやTwitterなどで教えてください♪
また、こうしたらもっと良いよ!といったアドバイスもいただけると嬉しいです。
<< アプリURL▶https://trusting-yalow-80ebbf.netlify.app/jason.html >>

本業は営業企画なので、仕事と両立して勉強するの正直しんどいんですけど、毎週のQiita投稿が本当に励みになってます!

最後までご覧いただき、ありがとうございました~~~!

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

Vue.js 3 Tips

Vue 3 では this.$refs が使えない

setup context にもないので...

解決策

Access template refs in Composition API in Vue.js 3

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