20201027のvue.jsに関する記事は15件です。

Vue.jsで、いろんなやり方の画像プレビュー方法

ref属性から画像を取得して、表示する

// refからアップロード
input(type="file", ref="file", @change="leftSetImage")
v-img(:src="image.source")
// refから一時的なURLに変換して取得
leftSetImage() {
  const files = this.$refs.file;
  const fileImg = files.files[0];
  if (fileImg.type.startsWith("image/")) {
    this.image.source = window.URL.createObjectURL(fileImg);
  }
},

イベントオブジェクトから画像を取得して、表示する

一時的なURLでプレビュー表示

// イベントオブジェクトからアップロード
input(
  type="file",
  @change="setImageLeft($event)"
)
v-img(:src="office.left_image.image", type="submit")

// イベントオブジェクトから、一時的URLに変換して取得
setImageLeft(e) {
  const file = (e.target.files || e.dataTransfer)[0];
  this.LeftUploadedImage = file;
  if (file.type.startsWith("image/")) {
    this.office.left_image.image = window.URL.createObjectURL(file);
  }
},

base64に変換して、プレビュー表示

今までのやり方だと、ブラウザを放置して更新したり、そのデータを画像を保存したりした時の再表示の動作ができなくなるということが起きる。

base64に変換する場合は、そのような事象は起きない。

// イベントオブジェクトからアップロード
input(
  type="file",
  @change="setImageRight($event)"
)
v-img(:src="office.right_image.image", type="submit")

// イベントオブジェクトから、base64に変換して取得
setImageRight(e) {
  const file = e.target.files[0] || e.dataTransfer.files;
  const reader = new FileReader();
  reader.onload = (e) => {
    this.office.right_image.image = e.target.result;
  };
  reader.readAsDataURL(file);
},
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.jsならではの、いろんなやり方の画像プレビュー方法

ref属性から画像を取得して、表示する

// refからアップロード
input(type="file", ref="file", @change="leftSetImage")
v-img(:src="image.source")
// refから一時的なURLに変換して取得
leftSetImage() {
  const files = this.$refs.file;
  const fileImg = files.files[0];
  if (fileImg.type.startsWith("image/")) {
    this.image.source = window.URL.createObjectURL(fileImg);
  }
},

イベントオブジェクトから画像を取得して、表示する

一時的なURLでプレビュー表示

// イベントオブジェクトからアップロード
input(
  type="file",
  @change="setImageLeft($event)"
)
v-img(:src="office.left_image.image", type="submit")

// イベントオブジェクトから、一時的URLに変換して取得
setImageLeft(e) {
  const file = (e.target.files || e.dataTransfer)[0];
  this.LeftUploadedImage = file;
  if (file.type.startsWith("image/")) {
    this.office.left_image.image = window.URL.createObjectURL(file);
  }
},

base64に変換して、プレビュー表示

今までのやり方だと、ブラウザを放置して更新したり、そのデータを画像を保存したりした時の再表示の動作ができなくなるということが起きる。

base64に変換する場合は、そのような事象は起きない。

// イベントオブジェクトからアップロード
input(
  type="file",
  @change="setImageRight($event)"
)
v-img(:src="office.right_image.image", type="submit")

// イベントオブジェクトから、base64に変換して取得
setImageRight(e) {
  const file = e.target.files[0] || e.dataTransfer.files;
  const reader = new FileReader();
  reader.onload = (e) => {
    this.office.right_image.image = e.target.result;
  };
  reader.readAsDataURL(file);
},
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue 3.0にてcomposition apiを使ったcomposableパターンでテストを書く

昔Advent Calendarのために書いた記事をvue-next版に書き直しました。
https://qiita.com/jiko21/items/12c79b7b831276e9a088

TL;DR

  • composition-apiを使うことでロジックをUIから分離してテストできる
  • ただし、少し考えてテストを書く必要あり

はじめに

9/18にVue 3.0がリリースされました!
これにより、composition-apiがプラグイン無しで利用できるようになりました。
composition-apiの詳しい使い方はここでは省略しますが、これにより、コンポーネントからロジックを別ファイルへと分離することもできます。

今回は、コンポーネントからロジックを分離した際のテストの書き方を説明します。

コードはすべてこちらにあります。

ロジックとUIの分離

例えば、TODOアプリの場合は、TODOリストに追加、削除する処理や、TODOリストのデータなどをComponentに直接記述するのではなく、
あくまで別のファイルに記述し、Component側でそれらを呼び出す、といったことがComposition-apiでは可能となります。

@/composable/todo.ts
import { computed, reactive } from 'vue';

const useTodo = () => {
  const todo = reactive({
    todos: [] as string[],
    length: computed(() => todo.todos.length),
  }) as any;
  const addTodo = (item: string) => {
    todo.todos.push(item);
  };
  const deleteTodo = (index: number) => {
    todo.todos.splice(index, 1);
  };
  return {
    todo,
    addTodo,
    deleteTodo,
  };
};

export default useTodo;
Todo.vue
<template>
  <div class="count">
    <input id="todo-input" v-model="text"/>
    <button class="add-btn" @click="onSubmit">追加</button>
    <ul>
      <li v-for="(task, i) in todo.todos" :key="i">
        <p>{{task}}</p>
        <button class="delete-btn" @click="deleteTodo(i)">Delete</button>
      </li>
    </ul>
  </div>
</template>

<script lang="ts">
import { defineComponent, ref } from 'vue';
import useTodo from '@/composable/todo';

export default defineComponent({
  name: 'Todo',
  setup() {
    const text = ref('');
    const { todo, addTodo, deleteTodo } = useTodo(); // 外部ファイルに書いたロジックを読み込む
    ...
  },
});
</script>

<style scoped>
</style>

テストの実装

このようにロジック(composable)とUIを分離したので次はテストの実装方針を説明します。
まずは、ロジック側からですが、

実際にモジュールとして正しく動くか

を検証します。(当たり前っちゃ当たり前かもですが)
そして、UI側では

ボタンタップ時にロジックをcallできているか

を検証します。

ロジック側のテスト

サービス層やutilで書いたテストコードと同様に、実際に呼び出しを行い検証を行います。
(もしcomosableに何らかの依存関係がある場合はそれをmockしてください)

todo.spec.ts
import useTodo from '@/composable/todo';

describe('todo.spec.ts', () => {
  it('addTodo should work properly', () => {
    const { todo, addTodo, deleteTodo } = useTodo();
    addTodo('hogehoge');
    expect(todo.todos).toEqual(['hogehoge']);
  });
  ...
});

UI側のテスト

ロジック(composable)をモックしてやり、それが呼ばれたかを検証します。
compsable内にstateがある場合はそれが表示されるかも確認しておきましょう。

Todo.spec.ts
import { mount, VueWrapper } from '@vue/test-utils';
import Todo from '@/components/Todo.vue';
import * as composable from '@/composable/todo';

describe('Todo.vue', () => {
  let wrapper: VueWrapper<any>;
  let addTodoMock: jest.Mock;
  let deleteTodoMock: jest.Mock;

  beforeEach(() => {
    jest.mock('@/composable/todo');
    addTodoMock = jest.fn();
    deleteTodoMock = jest.fn();
    const TODOS = [
      'アドベントカレンダー',
      '修論',
      '筋トレ',
    ];
    jest.spyOn(composable, 'default').mockReturnValue({
      // Reactiveはデータ構造そのままでOK!
      todo: {
        todos: TODOS,
        length: () => TODOS.length,
      },
      addTodo: addTodoMock,
      deleteTodo: deleteTodoMock,
    });
    wrapper = mount(Todo);
  });

  it('correctly renders initial html', () => {
    expect(wrapper.html()).toMatchSnapshot();
  });

  it('correctly call deleteTodo when `Delete` button is clicked', () => {
    const INDEX = 1;
    wrapper.findAll('.delete-btn')[INDEX].trigger('click');
    expect(deleteTodoMock).toHaveBeenCalledWith(INDEX);
  });
  ...
});

ここで重要なのは、Reactiveのモック方法です。
データ構造そのままのObjectをmockしてやればできます。

これはRefも同様で、

const countValue = ref(0);
countValue.value;

のように使用するので

jest.spyOn(composable, 'default').mockReturnValue({
  countValue: {
    value: 0,
  }, 
  increment: incrementMock,
  decrement: decrementMock,
});

と書いてしまいたくなりますが

count.spec.ts
jest.spyOn(composable, 'default').mockReturnValue({
  countValue: 0 as any,
  increment: incrementMock,
  decrement: decrementMock,
});
wrapper = mount(Count);

のように書いてやる必要があります。

最後に

このようにすれば、composableとしてロジックを分離した場合でもテストがかけます。
ただし、vue-test-utilsが9/28時点でまだ2.0.0-beta.5なのでまだまだ安定しないかもしれません。

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

vue.jsを初めて学ぶ ② [テンプレート構文]

前回までの記事

① hello world

目次

1. テンプレート構文とは?

2. newVueテンプレート解説

3. プロパティの種類

4. ディレクティブの種類

5. v-bindとは?

6. v-onとは?

7. イベント修飾子とは?

8. キー修飾子とは?

9. v-modelとは?

10. computedプロパティとは?

11. watchプロパティとは?

12. thisを理解する

13. ()括弧の使い方

14. classをデータにバインディングする方法(オブジェクトと配列)


テンプレート構文とは

index.js
new Vue({
    el: '#app',
    data: {
      message: 'Hello World'
    }
  1. Vue.jsが上記テンプレート構文をロード
  2. Vue.jsがhtmlを出力
  3. ブラウザがhtmlをロード
  4. 私達がブラウザで確認できる

new vueテンプレート解説

index.html
<div id = "app">
<!-- [1]文字列を出力 -->
  <p>{{message}}</p>

<!-- [2]和を出力 -->
  <p>{{number + 3}}</p>

<!-- [3]三項演算子 -->
  <p>{{ok ? 'yes' : 'no'}}</p>

<!-- [4]メソッドを出力 -->
  <p>{{ sayHi() }}</p>
</div>
index.js
new Vue({
    el: '#app',
    data: {
<!-- [1]文字列を出力 -->
      message: 'Hello World'

<!-- [2]和を出力 -->
      number: 3

<!-- [3]三項演算子 -->
      ok: true
    }
<!-- [4]メソッドを出力 -->
  methods: {
    sayHi: function() {
     return: 'Hi';
    }
   }
  })

三項演算子とは?
唯一の、3 つのオペランドをとる演算子です。
1. 条件に続いて疑問符 (?)
2. 条件が真値であった場合に実行する式
3. コロン (:) が続く
4. 条件がfalseであった場合に実行する式
が最後に来ます

注意: javascriptが出力されるのは、単一の式のみ


プロパティの種類

1. el

マウント先の要素を指定するブロック

2. data

変数を保持するブロック

3. method

関数を保持するブロック

4. computed

関数を保持するブロック
特徴として、returnを使用して返り値が必要。
そして、依存関係に基づきキャッシュされる。

5. watch

関数を保持するブロック
特徴として、returnを使用しないため非同期処理を記述できる


ディレクティブの種類

  • ディレクティブとは、v-から始まるVue.jsの特別な属性
  • 属性とは、htmlではhref,class,idといった要素 HTMLにおける、属性とは

1. v-text

2. v-once

3. v-html (xss脆弱性に注意

 対応:信頼のあるコンテンツしか置かないようにする。

4. v-bind

 1. href
 2. class
 3. id
 
属性を与える事ができる

5. v-on

 使用頻度が多い、DOMイベントを引数として発火する。
 様々な関数を、起動させることが可能。

6. v-model

 双方向データバインディングが可能


v-bindとは

属性を与える事ができる

index.html
<div id = "app">
    <a href="url">Google</a>
</div>
index.js
new Vue ({
  data: {
    url: 'https://google.com',
  }
})

Vueテンプレートで、url属性を与えようとする。。。
404エラーとなってしまう。


v-bind使用例

index.html
<div id = "app">
    <a v-bind:href="url">Google</a>
</div>
index.js
new Vue ({
  data: {
    url: 'https://google.com',
  }
})

v-bind省略記法

index.html
<div id = "app">
    <a :href="url">Google</a>
</div>
index.js
new Vue ({
  data: {
    url: 'https://google.com',
  }
})

v-onディレクティブ省略記法
- v-onを省略する。

index.html
    <p>現在{{number}}回クリックされています</p>
    <button @click="countUp">countUp</button>

省略が可能な記法(:や@)は、プロジェクト内で統一する。

v-bindを オブジェクト化 して使用する。

  1. hrefリンク
  2. class
  3. id
    複数の属性を与えたい時、 オブジェクト化 が最適

方法

index.html
<div id = "app">
    <a v-bind="objectGoogle">Google</a>
</div>
index.js
new Vue ({
  data: {
    objectGoogle: {
      href: 'https://google.com',
      class: 'sample',
      id: 31
    }
  }
})

chromeブラウザの「検証」より、v-bindで属性が与えられているのが確認できる
スクリーンショット 2020-10-27 13.40.08.png


v-onとは

DOMイベントが発火するタイミングで、実装した関数を実行できる

DOMイベントとは?(一覧)

ボタンクリックで数字がカウントされる処理を実装してみる。

v-on:に指定するのは、clickイベント

index.html
<div id = "app">
    <p>現在{{number}}回クリックされている</p>
    <button v-on:click="countUp">カウントアップ</button>
</div>
index.js
new Vue ({
      el: '#app',
      data: {
        number: 0
      },
      methods: {
        countUp: function() {
          this.number += 1
        }
      }
    })
  1. clickイベントが発火する
  2. countUp関数が起動 ( 変数numberに、+1 )
  3. {{ number }} に反映

マウスをかざした場所の、XY軸を表示する

v-on:に指定するのは、mousemoveイベント

index.html
<div id = "app">
    <p v-on:mousemove="changeMousePosition">マウスを載せてください</p>
    <p>X:{{x}},Y{{y}}</p>
</div>
index.js
new Vue ({
      el: '#app',
      data: {
        x:0,
        y:0,
      },
      methods: {
        changeMousePosition: function(event) {
          this.x = event.clientX;
          this.y = event.clientY;
        }
      }
    })

DOMイベントの情報を拾うには、ルールがある
1. 引数にはeventを文字として使用。他でも格納できるがよっぽど理由がなければ変更しない。
2. 格納したeventから読み取る事。


イベント修飾子とは

  1. 従来のstopPropagationを、stop で実装可能
  2. 従来のpreventDefaultを、prevent で実装可能

処理をSTOPさせる

従来のstopPropagation

index.html
<div id = "app">
    <p v-on:mousemove="changeMousePosition">マウスをかざしてください
      <span v-on:mousemove="noEvent">マウスをかざしても反応させない。</span>
    </p>
    <p>X:{{x}},Y{{y}}</p>
</div>
index.js
new Vue ({
      el: '#app',
      data: {
        x:0,
        y:0,
      },
      methods: {
        changeMousePosition: function(event) {
          this.x = event.clientX;
          this.y = event.clientY;
        },
        noEvent: function(event) {
          event.stopPropagation()
      }
    })
  1. spanタグにnoEvent関数を追加
  2. noEvent関数に、stopPropagationという処理をstopさせる命令を追加

Vue.jsのすごい所

  • v-on:mousemove.stopを与えるのみ!
index.html
<div id = "app">
    <p v-on:mousemove="changeMousePosition">マウスをかざしてください
      <span v-on:mousemove.stop>マウスをかざしても反応させない。</span>
    </p>
    <p>X{{x}},Y{{y}}</p>
</div>
index.js
new Vue ({
      el: '#app',
      data: {
        x:0,
        y:0,
      },
      methods: {
        changeMousePosition: function(event) {
          this.x = event.clientX;
          this.y = event.clientY;
        }
    })

処理をpreventさせる

  • preventとは、html側のデフォルト設定までも起動させない

リンクタグをクリック、飛ばないように設定。

従来のpreventDefault

index.html
<div id = "app">
    <a v-on:click="noEvent" href="https://google.com">google.com</a>
</div>
index.js
new Vue ({
      el: '#app',
      methods: {
        noEvent: function(event) {
          event.preventDefault();
       }
    })

Vue.jsのすごい所

  • html側に、v-on:mousemov.preventを与えるのみ!
index.html
<div id = "app">
    <a v-on:click.prevent href="https://google.com">google.com</a>
</div>
index.js
new Vue ({
      el: '#app',
    })

stopとpreventを、つなげる事も可能

v-on:mousemove.stop.prevent


キー修飾子とは

キーボード入力イベントに対する修飾子
1. エンターキーを入力時点で動作させる。.enter
2. スペースキー入力時点で動作させる。.space

index.html
<div id = "app">
    <input type="text" v-on:keyup.enter="myAlert">
</div>
index.js
new Vue ({
      el: '#app',
      methods: {
        myAlert() {
          alert('アラート');
        }
      }
    })

つなげる事も可能
v-on:keyup.enter.space


v-modelとは

双方向データバインディング
1. inputにテキストを入力
2. 変数messageの内容が置き換わる。
3. data内のmessageが置き換わる。
4. h1タグのmessageも変更される。

index.html
<div id = "app">
    <input type="text" v-model="message">
    <h1>{{message}}</h1>
  </div>
index.js
new Vue ({
      el: '#app',
      data: {
        message: 'こんにちは',
      }
    })

computedプロパティとは

  • dataプロパティでは、動的なデータは扱えない。(プレーンなテキストのみ)
  • computedプロパティを使用する
index.html
<div id = "app">
    <p>{{ counter }}</p>
    <button v-on:click="countUp">+1</button>
    <p>{{ lessThanThree }}</p>
</div>
index.js
new Vue ({
      el: '#app',
      data: {
        counter: 0
      },
      methods: {
        countUp: function() {
          this.counter += 1
        }
      },
      computed: {
        lessThanThree: function() {
          return this.counter > 3 ? '3より上' : '3以下'
        }
      }
    })

computedプロパティの関数、lessThanThreeには

  • 引数が必要ではない
  • returnで値を返す必要がある

関数lessThanThreeは、
methodsプロパティでは実現できないのか?

結論:
できるけど、、
最適化するならComputedメソッド一択!

  • computedプロパティ依存関係に基づきキャッシュされる特徴がある。
  • methodsプロパティは、全く関係のないメソッドの更新にも反応する。
  • 大規模になると、methodsプロパティで記述した関数が無駄に実行されてしまう。
index.html
  <div id = "app">
    <p>{{ counter }}</p>
    <button v-on:click="countUp">+1</button>
    <p>{{ lessThanThreeMethod() }}</p>
    <p>{{ lessThanThreeComputed }}</p>
  </div>
index.js
    new Vue ({
      el: '#app',
      data: {
        counter: 0
      },
      methods: {
        countUp: function() {
          this.counter += 1
        },
        lessThanThreeMethod: function() {
          return this.counter > 3 ? '3より上' : '3以下'
        }
      },
      computed: {
        lessThanThreeComputed: function() {
          return this.counter > 3 ? '3より上' : '3以下'
        }
      }
    })

watchプロパティとは

  • データが変わったとき、非同期処理 が実行
  • 非同期処理の中では、thisが使用不可
  • computedプロパティでは、returnが必要(同期処理)になる。
  • 非同期処理したい場合はwatchプロパティが最適。

3秒でカウントの値(counter)が0にリセットされる非同期処理

index.html
  <div id = "app">
    <p>{{ counter }}</p>
    <button v-on:click="countUp">+1</button>
    <p>{{ lessThanThreeComputed }}</p>
  </div>
index.js
    new Vue ({
      el: '#app',
      data: {
        counter: 0
      },
      methods: {
        countUp: function() {
          this.counter += 1
        }
      },
      computed: {
        lessThanThreeComputed: function() {
          return this.counter > 3 ? '3より上' : '3以下'
        }
      },
      watch: {
        counter: function() {
          var vm = this;
          setTimeout(function(){
            vm.counter = 0
          }, 3000)
        }
      }
    })

thisを理解する。

変数messageを、 関数sayHi内で 呼び出してみる。

index.js
new Vue({
  el: '#app',
  data: {
    message: 'Hello,World!!'
},
methods: {
  sayHi: function() {
<!--これはエラーになる-->
      return message;
<!--正しくは、thisが必要-->
      return this.message;
    }
  }
})
index.html
<div id = "app">
  <p>{{ sayHi() }}</p>
</div>

thisとは、
Vue.jsのインスタンスにアクセスするために必要なプロキシ


括弧の使い方

  • computedプロパティでは、メソッド名のみを{{二重カッコ内}}に記載
    computedプロパティに()を足すと、エラーとなり表示されなくなる。

  • methodsプロパティでは、メソッド名+()を{{二重カッコ内}}に記載
    methodプロパティから()を除くと、
    function () { [native code] }と表示される。

  • v-ディレクティブの引数としてメソッド名を指定する場合
     ()は付与してもしなくてもどちらでもOK。
    引数には、javascriptコードを書いてもOK。


classをデータにバインディングする方法

{ オブジェクト }で記載

  • ハイフンの記載がオブジェクト内にある場合、'bg-color'とシングルクォーテーションで括る必要あり。
index.html
  <div id = "app">
    <h1 :class="{red: true, 'bg-blue': false}">はろう</h1>
  </div>
index.js
    new Vue ({
      el: '#app'
})
sample.css
.red {
  color: red;
}

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

ボタンを設置して、クラスの付与を分岐する。

index.html
  <div id = "app">
    <h1 :class="classObject">はろう</h1>
    <button @click="isActive = !isActive">切り替えボタン</button>
  </div>
index.js
    new Vue ({
      el: '#app',
      data: {
        isActive: true
      },
      computed: {
        classObject: function() {
          return {
            red: this.isActive,
            'bg-blue': !this.isActive
          }
        }
      }
    })
sample.css
.red {
  color: red;
}

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

[ 配列 ] で記載

index.html
  <div id = "app">
    <h1 :class="[color, bg]">はろう</h1>
  </div>
index.js
    new Vue ({
      el: '#app',
      data: {
        color: 'red',
        bg: 'bg-blue',
      }
    })
text.sample.css
.red {
  color: red;
}

.bg-blue {
  background-color: blue;
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【vue】methodsの省略表記と注意点(アロー関数は使えない)

【vue】methodsの省略表記と注意点

・OK:functionを使わない省略表記を使うのが一般的。
・NG:アロー関数を使う

省略表記
methods:{
    メソッド名(){
      処理
    }
 }

メソッド名の後ろにカッコを直接つける。


NG事例(アロー関数)

アロー関数を使った場合、処理の中でプロパティを呼び出すことができない。

NG事例(アロー関数)
export default {
  data(){
    return{
      text: "ボタン"
    }
  },
  methods:{
    メソッド名:()=>{
      console.log(this.text)
    }
 }
}
エラー内容
[Vue warn]: Error in v-on handler: "TypeError: Cannot read property 'text' of undefined"

textを正しく設定してるのに、呼び出すことができない。


参考(3つの処理)

  1. 省略形
  2. function使用(冗長表記)
  3. アロー関数(NG事例)
Methods.vue
<template>
  <div>
    <v-btn @click="showConsole1">
      {{text}}1
    </v-btn>

    <br>

    <v-btn @click="showConsole2">
      {{text}}2
    </v-btn>

    <br>

    <v-btn @click="showConsole3">
      {{text}}3
    </v-btn>
  </div>
</template>

<script>
export default {
  data(){
    return{
      text: "ボタン"
    }
  },
  methods:{
    showConsole1(){
      console.log("showConsole1:", this.text)
    },
    showConsole2:function(){
      console.log("showConsole2:", this.text)
    },
    showConsole3:()=>{
      console.log("showConsole3:", this.text)
    }
  }
}
</script>

▼ブラウザの表記

image.png

▼実行結果

image.png

ボタン1、2は正常に作動するが、アロー関数を用いた3はエラーとなる。

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

Vueのvalueプロパティ。親で定義してないのに子のpropsにvalueがある理由。

Vueのvalueプロパティ。親で定義してないのに子のpropsにvalueがある理由。

子のpropsにvalueがあるのに、親のどこを探してもvalueがなくデータ元が不明すぎて散々悩んだので、その解決法について。

>propsの役割
親から子へのデータ受け渡しで、子のpropsプロパティに受け取る変数を定義する。

結論

v-modelで渡すデータの初期設定がvalueだった。。(text要素の場合)

<TextFiled
   v-model="rows"
/>
子(TextField)
export default {
  props:{
    value={},
  }

親のv-model="rows"でrowsという変数をTextFieldに渡している。

このとき、データはvalueに格納されている。

つまり、親から子へのデータ受け渡しだけに着目すると、以下2つは同じになる。

v-model="rows"
  ⇅
:value="rows"

v-modelのデフォルトの変数

要素 初期プロパティ名 初期イベント名
text要素/textarea要素 value input
チェックボックス/ラジオボタン checked change
セレクトボックス value change

(公式) v-modelの基本的な使用方法

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

Vuejsでフラッシュカードを作りましょう(音声でもコントロール可能)

やりたいこと

今回、Vuejsを勉強のため、簡単なフラッシュカードのウェッブアプリを作成します。
フラッシュカードは質問と答えの面が2つあって、その二つ面を交換すために、「フリップ」があります。

image.png

実装

データを準備する

まず、HTMLとVuejsの要素を作って、連携しましょう。

<div id="flashCardApp">
const flashCardApp = new Vue({
    el: '#flashCardApp',
    data: {},
    methods: {}

dataのほうはcardsとshowedCardがあります。cardsは全部アプリのカードです。showedCardは表示されるカードになります。で、アプリ起動する時、表示されるカードは最初のカードになります。

cards = [{カード1の情報}, {カード1の情報}, {カード3の情報},...];
data: {
    cards: cards,
    showedCard: cards[0]
}

カード情報は:

id: カードのID(例: 0, 1, 2)
frontText: 質問の言葉(例: BANK, CHURCH)
backText: 言葉の意味(例:銀行, 教会)
colorClass: カードの色(責任)
flipped: どんな面が表示されるか(例: true, false)

です。

前と次のカードのボータン

表示されるカードは一つだけなので、全部のカードを見えるように、カード間に移動するボータンは必要になります。また、最後の次のカードは最初のカードです。逆に最初のカードの前は最後のカードになります。

JS側

methods: {
    previosCard: function() {
        let showedId = this.showedCard.id - 1;
        if(showedId < 0){
            showedId = cards.length - 1;
        }
        this.showedCard = cards[showedId];
    },

    nextCard: function() {
        let showedId = this.showedCard.id + 1;
        if(showedId >= cards.length){
            showedId = 0;
        }
        this.showedCard = cards[showedId]
    }
}

HTML側

<button v-on:click="previosCard" class="button-right"></button></div>
<button v-on:click="nextCard" class="button-left"></button>

カードのフリップのボータン

ユーザはカードのフロント面とバック面を交換するボータンを押すと、現在表示されるカードのflippedの状態を変更して、Vuejsはその状態の変更に対して自動的にカードのフロントとバック面を交換してもらいます。
js側

methods: {
    flipCard: function() {
        this.showedCard.flipped = !this.showedCard.flipped;
    }
}

html側

<transition name="flip">
    <div v-show="!showedCard.flipped" @click="flipCard" class="card-text card-front-text">
        <div class="pretext">
            単語
        </div>
        <h2>
            {{showedCard.frontText}}
        </h2>

        <button class="button-white button-clear button-large">
            フリップ
            <i class="fas fa-sync-alt"></i>
        </button>
    </div>
</transition>

<transition name="flip">
    <div v-show="showedCard.flipped" @click="flipCard" class="card-text card-back-text">
        <div class="pretext">
            意味
        </div>
        <h2>
            {{showedCard.backText}}
        </h2>

        <button class="button-white button-clear button-large">
            フリップ
            <i class="fas fa-sync-alt"></i>
        </button>
    </div>
</transition>

音声でフラッシュカードをコントロールしましょう。

annyangは、ユーザが音声コマンドでサイトを制御できるようにする小さなjavascriptライブラリです。
annyangの使い方は本当に簡単です。

if (annyang) {
  // コマンドを定義する
  const commands = {
    '': () => { flashCardApp.nextCard(); },
    'フリップ': () => { flashCardApp.flipCard(); },
    '': () => { flashCardApp.previosCard(); },
  };

 //入力の言語を日本語に変更する
  annyang.setLanguage("ja");
  // ↑の定義したコマンドを追加しする
  annyang.addCommands(commands);

  // マイクロからの声が聞こえ始めます
  annyang.start();
}

それで、「次」と「前」と「フリップ」のコマンドを言ってもらうと、フラッシュカードをコントロールできます。ただ、音声認識はクラウドで処理するので、少しだけディレイ感じがあります。

以上です。

結果と参考

デモ: https://codepen.io/tardigrades2/full/oNLZovL
コード: https://codepen.io/tardigrades2/pen/oNLZovL)
参考したコード: https://codepen.io/mgnmrt/pen/pKZVYg

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

Uncaught ReferenceError: Vue is not definedが起こった話

はじめに

Vue.jsをCDN読み込みで簡単なミニアプリを使う際に起こったエラーです。
備忘録のため残しておきます。初学者のため間違えがあればご指摘下さい!

Uncaught ReferenceError: Vue is not defined

vue.jsの動作確認をするため以下のコードを記述したところvue.jsが読み込まれませんでした。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <link rel="stylesheet" href="vue.css">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <p>現在{{ number }}回クリックされています</p>
    <button>カウントアップ</button>
  </div>
  <script src="vue.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</body>
</html>

//vue.js

new Vue({
  el: '#app',
  data: {
    number: 0
  }
})

解決策は簡単。
下から3行目のVueを使うためのCDNをjsが読み込まれるより上に記述するだけです。
headタグの中に入れるかbodyタグの一番上に記述しましょう。

<!-- 修正後 -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <link rel="stylesheet" href="vue.css">
  <title>Document</title>
</head>
<body>
  <div id="app">
    <p>現在{{ number }}回クリックされています</p>
    <button>カウントアップ</button>
  </div>
  <script src="vue.js"></script>
</body>
</html>

最後に

Vue.jsの超初心者の方の助けになれば幸いです。

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

branchを指定してcloneする方法

背景

YouTubeで、Vuetifyのチュートリアルを学習しようと思った。動画投稿者が、githubにサンプルコードを載せてくれていた。それは、各lessonごとにbranchが切られていたので、ただgit cloneするだけだと、目的のファイルが入手できず、branchごとにcloneする必要があった。

これ→https://github.com/iamshaunjp/vuetify-playlist

方法

$ git clone -b ブランチ名 cloneのhttps

つまり

$ git clone -b git clone -b lesson-2 https://github.com/iamshaunjp/vuetify-playlist.git

です。

参考
https://www.sejuku.net/blog/71801

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

watchプロパティで入力内容を即時反映する方法。子から親へのデータ受け渡し

watchプロパティで入力内容を即時反映する方法。子から親へのデータ受け渡し

watchプロパティを使うと画面上の変更内容をリアルタイムで検知し、設定した処理を実行することができる。

watchプロパティを使う手順

①変数を定義する
 ↓
②変数に対してwatachプロパティを設定する

watchプロパティは変数の変更を感知するため、変更内容を監視する変数を指定する。



※注意点
・watchプロパティの中で監視する変数を指定。
  ┗ 変数名:function(){処理}

・変数内のネストしたデータを監視する場合は追加設定が必要。
  ┗ 関数を「handler(){}」にする。
  ┗ 「deep: true」を設定する。


watchプロパティの使い方(ネストしてない変数の監視)

  watch:{
     プロパティ名: function(){
       処理
     }
  }

実例

(子)TextField.vue
<template>
<div>
  <v-col
    cols="12"
    sm="3">
    <v-text-field
     v-model="text" />
  </v-col>
</div>
</template>

<script>
export default {
  data(){
    return{
      //①監視する変数を定義
      text: "初期値"
  },
  watch:{
    //②変数に対してwatchプロパティを設定
    //textの値が変わったら以下処理を実行。
    text:
      function (a){
        this.$emit('childChange', a)
    }
  }
}
</script>

関数内のthis.$emit('childChange', a)
 ┗ 関数内でプロパティを自分の呼び出す場合はthisが必要。
 ┗ $emitは子から親にデータを渡す
 ┗ $emit('親に渡すメソッド名', 引数)
   ┗ 引数は任意
   ┗ 引数には監視している変数が入る



▼引数のメリット
引数を使わずにデータを指定して渡す方法もあるが、引数を使った記述の方がより簡易的。

引数あり
  watch:{
    text:
      function (a){
        this.$emit('childChange', a)
    }
  }
引数なし(データを直接指定)
  watch:{
    text:
      function (){
        this.$emit('childChange', this.text)
    }
  }
(親)parent.vue
<template>
  <v-app>
    <TextField
    <!--子からイベントを受け取り、親のイベントに引き継ぐ-->
    @childChange="parentChange"
     />

    <p>
    ・v-modelのデータ:  {{text}}
    </p>
  </v-app>
</template>

<script>
import TextField from "./TextField"

export default {
  name: "Parent",
  components:{
    TextField
  },
  data(){
    return{
      text:'初期値'
    }
  },
  methods:{
    //親のイベントを実行
    parentChange(text){
      this.text = text
      console.log("text-filedが変更されました")
    }
  }
}
</script>

これで、text-fieldへの入力値がリアルタイムで反映される。


ネストした変数データの監視

変数がネストしている場合、以下2つの設定が必要。

(1) functionをhandlerに変更
(2) deep: true を設定

※watchプロパティの中が入れ子になるため、追加で{}が必要。

  watch:{
    プロパティ名:{ 
      handler(){
        処理
      },
      deep: true
    }
  }


実例

(子)TextField.vue
<template>
<div>
  <v-col
    cols="12"
    sm="3">
    <v-text-field
     v-model="text.sub" />
  </v-col>
</div>
</template>

<script>
export default {
  data(){
    return{
      //①監視する変数を定義(入れ子)
      text: {
        first: "初期値",
        sub: "subテキスト"}
    }
  },
  watch:{
    //②変数に対してwatchプロパティを設定
    text:{ 
      handler(a){
        this.$emit('childChange', a)
      },
      deep: true
    }
  }
}
</script>
parent.vue
<template>
  <v-app>
    <TextField
    <!--子からイベントを受け取り、親のイベントに引き継ぐ-->
    @childChange="parentChange"
     />

    <p>
    ・v-modelのデータ:  {{text}}
    </p>
  </v-app>
</template>

<script>
import TextField from "./TextField"

export default {
  name: "Parent",
  components:{
    TextField
  },
  data(){
    return{
      text:'初期値'
    }
  },
  methods:{
    parentChange(text){
      //受け取った入れ子の変数のsubを使う
      this.text = text.sub
      console.log("text-filedが変更されました")
    }
  }
}
</script>



▼初期状態

image.png



▼文字入力後

image.png
image.png

文字を入力する毎に、親で設定したメソッドが実行されている。

変更があったタイミングでwatchが作動するため、初期値は反映されていない。

immediate: trueを追加で記述することで、初期値から反映することができる。


immediate: true

(子)TextField.vue
  watch:{
    text:{
      //初期値からデータ連動
      immediate: true,
      handler(a){
        this.$emit('childChange', a)
      },
      deep: true
    }
  }
}
image.png
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue入力値変更と変化の検出 & 子から親にデータを渡す方法(v-model、@change)

Vue入力値変更と変化の検出 & 子から親にデータを渡す方法(v-model、v-on:change)

自分のテンプレート内での変化を検出するパターンと、子テンプレートでの変化を検出し親に情報を渡すパターンの2つを考えてみる。

自分のテンプレート内での変化を検出

手順

①タグにv-modelを設定する (v-model="変数")
 ↓
②dataオプションで変数を定義する
 ↓
③タグに@changeイベントを設定する(@change="イベント名")
 ↓
④methodsオプションで処理を定義する

①と②、③と④がそれぞれ対となる。

※注意点
・v-modelで指定した変数の値と画面入力が連動する
・「@」は「v-on:」の省略形
  @change = v-on:change


vue
<template>
<div>
  <v-col
    cols="12"
    sm="3">
    <v-text-field
     <!--①v-modelを設定-->
     v-model="text"

     <!--③changeイベントを設定-->
      @change="applyChanges" />
  </v-col>


  <p>
    <!--v-modelのデータ連動確認用-->
   ・v-modelのデータ:  {{text}}
  </p>
</div>
</template>

<script>
export default {
  data(){
    return{
      //②v-model用の変数を定義
      text: "初期値"
    }
  },
  methods:{
    //④changeイベント用のメソッドを定義
    applyChanges(){
      console.log("text-filedが変更されました")
    }
  }
}
</script>



実行結果の確認

▼変更前の状態。

image.png



▼変更後の状態

image.png
image.png

v-modelでデータが連動。changeイベントが発火している。


子から親へのデータ受け渡し

大まかな流れは、子テンプレでデータ発信を準備し、親側で受け取る用意をする。

子:①タグにv-modelを設定 (v-model="変数")
 ↓
子:②dataオプションで変数を定義
 ↓
子:③タグに@changeイベントを設定(@change="イベント名")
 ↓
子:④methodsオプションで発信処理を定義 (★親に引き渡すイベントと変数)
 ↓
親:⑤受け取ったイベント名を属性で定義し親のイベントに引き継ぐ (@受けとたイベント名 = "親のイベント名")
 ↓
親:⑥methodsオプションで処理を定義 (引数で子の変数を受け取る)
 ↓
親:⑦受け取ったデータをいれる変数を定義(dataオプション)
 ↓
親:⑧受け取ったデータを画面上に表示

※注意点
・子のmethods内で、プロパティを呼び出す場合は「this」をつける。
 ┗ this.$emit('受け渡すメソッド名', 変数)
 ┗ this.プロパティ名 


実例

(子)TextField.vue
<template>
<div>
  <v-col
    cols="12"
    sm="3">
     <!--①v-modelを設定-->
     v-model="text"

     <!--③changeイベントを設定-->
      @change="applyChanges" />
  </v-col>
</div>
</template>

<script>
export default {
  data(){
    return{
      //②v-model用の変数を定義
      text: "初期値"
    }
  },
  methods:{
    //④methodsオプションで発信処理を定義 
    applyChanges(){
      //データを変数で渡すため変数を定義
      const text = this.text
      //第1引数でイベント名、第2引数で変数を渡す
      this.$emit('childChange', text)
    }
  }
}
</script>
(親)parent.vue
<template>
  <v-app>
    <TextField
    <!--⑤受け取ったイベント名を属性で定義し親のイベントに引き継ぐ-->
    @childChange="parentChange"
     />

    <p>
     <!--⑧受け取ったデータを画面上に表示-->
    ・v-modelのデータ:  {{text}}
    </p>
  </v-app>
</template>

<script>
import TextField from "./TextField"

export default {
  name: "Parent",
  components:{
    TextField
  },
  data(){
    return{
      //⑦受け取ったデータをいれる変数を定義
      //子の初期値と合わせるため同じ値をいれる
      text:'初期値'
    }
  },
  methods:{
    //⑥methodsオプションで処理を定義 
    parentChange(text){
      this.text = text
      console.log("text-filedが変更されました")
    }
  }
}
</script>



実施結果

image.png



▼画面入力後

image.png
image.png

v-modelで変更した値が適用されている。
親側で定義したメソッドも発火している。

ただし、上記いずれの例も、changeイベントでmethodを発火させるため、変更を確定(Enter)しないと反映されない。

入力内容を即時反映したい場合はwatchプロパティを使う。

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

dasdasdada

alb-266929303.ap-northeast-1.elb.amazonaws.com_ (3).png
alb-266929303.ap-northeast-1.elb.amazonaws.com_ (2).png

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

絵文字APIの使い方メモ

絵文字のAPIの使い方

絵文字用のAPIの使い方がよく分からなかったので、検索してみたら、下記の記事が存在。
http://rdrgn.hatenablog.com/entry/2017/01/20/000000

まずは、emojione.min.jsとemojione.min.cssを読み込む必要がある。

実際に使ってみた

実際に動かした結果は下記の通り。emojione.min.jsはimgタグを返している。
https://qiita.com/heihei15408697/items/e830cae0a967346f6650

返却されたimgタグをうまく出力したかったけど、そこの扱いがうまくいかず、imgタグの文字列をそのまま出力して終わっている。

出力する方法をどこかで調べて、ちゃんと出るようにしたい。

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

フロントエンド(TypeScript, Three.js)のみで動く、Molecular Visualizerの開発(その2)

概要

詳しくはその1を参照ください

現状

heat.png
色が変わったくらいでほとんど見た目は前回から変わってない。

(前回との)更新内容

画面構成を部分的にVue.js化
headerにCSSでhover時の色の変化を加えた。
他細かな修正とまだ記事にしてない機能の追加

分子構造データクラスとパーサー

現状対象としている構造データは、原子が3D空間上に点在していて、任意の原子間に結合が存在している。
つまりデータ構造としては、原子のリストと、結合のリストと、それのコンテナである。
これを愚直に抽象化したインターフェースを次のように設計した。AtomにはelementType以外にFFtypeなどが必要ではないかという話は、いったんおきましょう。

Atom インターフェース(./src/systems/system.ts)

enum ElemType{
    ANY,H,He,Li,Be,B,C,N,O,F,Ne,
}

export type Position = Array<number>;
export type Name = string;

export interface IAtom{
    position: Position;
    name: Name;
    element: ElemType;
    index: number;
}

Bond インターフェース(./src/systems/system.ts)

export type BOND_TYPE= number;

export interface IBond<AtomType extends IAtom = IAtom>{
    atoma: AtomType;
    atomb: AtomType;

    vector: number[];
    distance: number;

    position: Position; 
}

System インターフェース(./src/systems/system.ts)

export interface ISystem<AtomType extends IAtom,BondType extends IBond<AtomType>>{
    /**getterでの実装を禁ズ */
    systemName: string;

    natoms: number;
    nbond: number;
    getNames(): string[];
    getPositions(): Position[];
    getElements(): ElemType[];
    getAtom(index:number): AtomType;
    atomIndexOf(name:string): number;
    getAtoms():Generator<AtomType,string,unknown>;
    getBond():Generator<BondType,void,unknown>;
}

実現クラスは設計では一つしか作らない予定であるため、インターフェースクラスを用意する意味はほとんどないが、ひとまず経由することとした。

このSystemクラスは、描画するクラスに引き渡され描画のためのオブジェクトの作成や、IAtom.nameを使ってクリックイベントの対象物の識別に使われる(予定)。

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

vue3を導入しようとして挫折した話

はじめに

実PRJでvue3を導入しようとしたけど、PRJ途中で断念した話をまとめます。
巷ではCompositionAPIやら複数の双方向データバインディングやら話題ありますが、その辺りには到達することなく断念することになりました。とはいえ少し触ったので、「お、これいけてるやん!」というものも紹介しつつ、なぜ断念したかを綴ります。

vue 3

「Vue 3」は、JavaScriptフレームワークVue.jsの次期バージョンで、2016年にリリースされた「Vue 2」以来のメジャーバージョンアップ。vue 2から記載方法が変わった点もあるが、割と互換性もある(気がした)

便利だと思った機能

v-on:clickをcomponentに指定しても動くように

vue 2までだと、componentに対してv-on:clickを指定しても動作しません

動かない
<template>
<div>
 <childComponent v-on:click="test" />
</div>
</template>

<script>
import childComponent from "@/components/chiledComponent";
export default {
 components: {
   childComponent,
 },
 methods: {
  test(): {
   alert("test")
  }
 }
}
</script>

なので、下記のように記載する必要がありました。

これは動く
<template>
<div>
 <childComponent v-on:click.native="test" />
</div>
</template>

<script>
import childComponent from "@/components/chiledComponent";
export default {
 components: {
   childComponent,
 },
 methods: {
  test(): {
   alert("test")
  }
 }
}
</script>

これがvue 3だと

vue3だと動く
<template>
<div>
 <childComponent v-on:click="test" />
</div>
</template>

<script>
import childComponent from "@/components/chiledComponent";
export default {
 components: {
   childComponent,
 },
 methods: {
  test(): {
   alert("test")
  }
 }
}
</script>

のように記述することができます。
なのでタグが純粋なhtmlタグなのかcomponentタグなのか気にせず開発を進めることができます。

build時の環境変数指定が楽に

開発時はdev環境、ユーザーテスト環境、本番環境などの環境ごとにAPIの向き先など環境変数を変更したいニーズがあると思います。vue 2では環境変数がデフォルトではconfigディレクトリ以下にそれぞれ
- dev.env.js
- prod.env.js
- test.env.js
が用意されていますが、もっと環境を分けたい場合はwebpackコンフィグファイルあたりをいじる必要がありました。
Nuxt.jsではcrossenvやdotenvをnpm iしてnuxt.config.jsファイルをいじるようなことをやってたかと思います。
vue 3ではその辺りをかなり良しなにやってくれたのが感動しました。
下記手順で環境ごとのbuildを簡単に作成できます。

1. プロジェクト直下に.env.環境変数の命名で環境変数ファイルを作成

Project
├─.env.production
├─.env.uat
└─.env.dev

2. 「.env.環境変数」のファイルにVUE_APP_XXXの命名規則で変数を定義する

変数のプレフィックスにVUE_APP_を記載するのを忘れないようにしましょう。

env.dev
VUE_APP_REST_URL=https://testapi/v1/
VUE_APP_S3BACKET_URL=http://bucket.s3-ap-northeast-1.amazonaws.com

3. package.jsonのscriptsに下記を追記

--mode 以降を.env.環境変数の環境変数と対応させる

package.json
{
 "scripts": {
  "build:dev": "vue-cli-service serve --mode dev",
  "build:uat": " vue-cli-service serve --mode uat",
  "buld:production": "vue-cli-service serve --mode producion"
 }
}

.vue内ではprocess.env.VUE_APP_REST_URLのように記載すれば環境変数を参照できる

オブジェクトや配列のバインディング改善

vue 2までの場合、配列要素の更新やオブジェクトのプロパティ追加削除を画面に反映させるにはVue.setVue.deleteで間接的に操作する必要があったが、vue 3ではそれが不要となった。

vue3
/* これで更新できる */
this.array[0] = this.inputValue

なんでvue3を諦めたの?

vue3のメジャーアップデートの恩恵にはあまり与っていないにせよ、上記だけでもなかなかメリットがあるように見られたが、下記のような流れで断念。

デザイナ「そういえばこのデザインbootstrap前提で作ってるよ」

そうなんです、2020年9月時点ではbootstrap-vueがvue3に対応していなかったのです。
でも、ここまでであれば開発チームはまだ心は折れません。所詮デザインなんてCSSで頑張ればbootstrapなんていらないよ。

デザイナ「ダイアログはこのライブラリのイメージで作ったから、ライブラリ入れたら一発だよ」

bootstrapの流れからお察しの通り、ダイアログのライブラリも残念ながらまだvue3には対応していませんでした。

ここで開発メンバーも自前でダイアログのデザイン実装は厳しいと根を上げ、PRJ途中でNuxtに移行(笑)したのでした。

じゃあどうするのが正解だったの?

ここはぶっちゃけあんまり解はないのかなと思います。プロジェクトの納期を優先するのであれば、実績やリファレンスが豊富な安定版を使うべきだし、エンジニアとして最新を追い求めたいのであれば多少困難でも最新版を積極導入すればいいと思います。技術力がとても高ければ最新入れても大抵のことは独力解決できるだろうし。

今回に関してはPRJの早い段階で見切りを付けられたので進捗にもさほど影響しなかったし、少しだけだけど最新バージョンをいろいろ調べつつ悩みつつ触ることで多少なりとも知見が増えたので、結果的には良かったのでは、と個人的には思っています。

vue 3に世の中が追いついてきたらまたPRJ導入を検討したいなと思いました。

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