20200430のvue.jsに関する記事は19件です。

親コンポーネントから子コンポーネントへpropsを使って値を渡す際にハマったこと

こんな感じで、example-component(子コンポーネントに)①itemsと②newItemsの二つのjsonを渡したい

親コンポーネント

<example-component items="{{ json_encode($items) }}" newItems="{{ json_encode($newItems) }}" ></example-component>

子コンポーネント

  props: {
    items: String,
    newItems: String
  },

がこれでやると、①itemsは問題なく渡せるが、②newItemsがundifindになってしまう

なんと、htmlは大文字小文字を区別できず全て小文字になってしまうそう...。
なので↓に修正する

親コンポーネント改

<example-component items="{{ json_encode($items) }}" new-items="{{ json_encode($newItems) }}" ></example-component>

間に「-」を挟むことで、子コンポーネント側ではnew-items→newItemと認識してくれるようになる

初歩的かもしれませんが、僕はこれで1日を無駄にしてしまったので、みんな気を付けてくれ!

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

vue-composition-apiで計算機作ってみた。

vue-composition-apiで計算機作ってみた。

触ってみました。vue-composition-api。
vue3系から組み込まれる新機能だそうで。
とりあえず使ってみます。

※今回、四則演算までは実装していません。

自己紹介

28歳から独学でプログラミングを始める。もちろん未経験。
約10ヶ月の学習期間をへて事業会社のIT部門に内定をいただく。
しかし、CTOが2ヶ月で退任しコードを書く仕事ができなくなり「このままじゃ技術が伸びない!」と思い、内定から3ヶ月ほどで再度転職活動を始める。
今年の2月から新しい会社に努めており、現在はvueを用いて自社サービスを開発しています。

ぎゅっと縮めると実務でコードを触ってるのは4ヶ月ほどなのでクソコードかもしれませんが、学習の備忘録として記事にしています。
何か間違ってる点やご指摘があれば是非教えてください。

結論

実際書いてみてこのような印象を受けました。
メリット

  • 処理ごとでfunctionをまとめられるので可読性上がりそう
  • 型推論が効くため安全

デメリット

  • 従来のvueの書き方とは違うので最初は戸惑う
  • 情報が少ないためベストプラクティスが分からない
  • tsを使うならもちろんtsの学習も必要

実際どんな感じになるの?

例えば今までだと

sample.vue
<template>
  <h1>hello vue</h1>
</template>

<script>
export default {
  data() {
    return {
      hoge: 'hoge',
      foo: 'foo',
    }
  },
  computed() {},
  created() {},
  mounted() {},
  methods: {
    handleHoge() {},
    handleFoo() {},
  },
}
</script>

今はコードが少ないのであまりメリットを感じませんが、こんな感じでdataでプロパティを定義して、methodsまで離れていたりして、コードを読む時行ったり来たりを繰り返して読まなければなりませんでした。

それがこんな感じでまとめられるようになります。

sample.vue
<template>
  <h1>hello vue</h1>
</template>

<script lang="ts">
import { defineComponent, ref } from '@vue/composition-api'

function useHoge() {
  const hoge = ref('hoge')
  const handleHoge = () => {
    // なんらかの処理
    console.log(hoge.value)
  }
  return {
    hoge,
    handleHoge,
  }
}

function useFoo() {
  const foo = ref('foo')
  const handleFoo = () => {
    // なんらかの処理
    console.log(foo.value)
  }
  return {
    foo,
    handleFoo,
  }
}

export default defineComponent({
  setup() {
    const { hoge, handleHoge } = useHoge()
    const { foo, handleFoo } = useFoo()
    return {
      hoge,
      handleHoge,
      foo,
      handleFoo,
    }
  },
})
</script>

変数の定義とメソッドを同じ関数内にまとめることができるようになります。
もちろんcomputedなども使えます。

RFCのまさにこの画像のようになります。
62783026-810e6180-ba89-11e9-8774-e7771c8095d6.png

ざっくりcomposition-apiがどんなものかわかったら実際に計算機のコードをみてみます。

コード

今回は試しにatomic designで実装してみました。
ただ、本来どこでデータを持すべきなのかメソッドを定義するべきなのかということだったり、organismsとmoleculesの違いは?などいろいろ疑問点が出てきたため、これからも試していきたいと思います。

operator

コードの中で「どの二項演算子が入力されてるのか」で分岐させる場所があるためobjectにしておきました。
直接'+'や'÷'を書いて分岐させない方が良さそうな気がするので。

operator.ts
export const Operator: Record<string, string> = {
  plus: '+',
  minus: '-',
  multiplication: '×',
  division: '÷',
  equal: '=',
  allClear: 'AC'
}

pages

大元となるpagesです。
おそらく、コードを読み始める時は上位ファイルから読んでいくだろという推測で、「pagesのファイルを見ればどんなデータが使われてるかが分かる」という考えのもと計算機ボタンの配列を定義しました。
デザインはgoogleの計算機を参考にしました。
ボタンが数字なのか記号なのかで、styleを変えたいためtypeを定義し、atomsのclassバインディングでstyleを変更できるようにしました。

pagesCalc.vue
<template>
  <template-calc :operator="Operator" :number-unit-array="numberUnitArray" />
</template>

<script lang="ts">
import templateCalc from '@/components/templates/templateCalc.vue'
import { defineComponent, reactive } from '@vue/composition-api'
import { Operator } from '../../common/operator'
export default defineComponent({
  components: {
    templateCalc
  },
  setup() {
    const numberUnitArray: string[][] = reactive([
      [
        { value: '(', type: 'operator' },
        { value: ')', type: 'operator' },
        { value: '%', type: 'operator' },
        { value: 'AC', type: 'operator' }
      ],
      [
        { value: '7', type: 'number' },
        { value: '8', type: 'number' },
        { value: '9', type: 'number' },
        { value: '÷', type: 'operator' }
      ],
      [
        { value: '4', type: 'number' },
        { value: '5', type: 'number' },
        { value: '6', type: 'number' },
        { value: '×', type: 'operator' }
      ],
      [
        { value: '1', type: 'number' },
        { value: '2', type: 'number' },
        { value: '3', type: 'number' },
        { value: '-', type: 'operator' }
      ],
      [
        { value: '0', type: 'number' },
        { value: '.', type: 'number' },
        { value: '=', type: 'calculation' },
        { value: '+', type: 'operator' }
      ]
    ])

    return {
      Operator,
      numberUnitArray
    }
  }
})
</script>

templates

今のところpagesからorganismsの受け渡しのファイルとなっています。

templateCalc.vue
<template>
  <div class="calculator">
    <organisms-calc :operator="operator" :number-unit-array="numberUnitArray" />
  </div>
</template>

<script>
import organismsCalc from '@/components/organisms/organismsCalc'
import { defineComponent } from '@vue/composition-api'

export default defineComponent({
  components: {
    organismsCalc
  },
  props: {
    operator: {
      type: Object,
      required: true
    },
    numberUnitArray: {
      type: Array,
      required: true
    }
  }
})
</script>

<style lang="scss" scoped>
.calculator {
  width: 100%;
  max-width: 375px;
  padding: 10px;
  margin: auto;
}
</style>

organisms

ここでmainの処理をしています。
useAddInput・・・入力された値の処理
useShowHistory・・・計算した履歴(未実装)
という風に処理ごとでfunctionを分けられるので、今までのVueの書き方と違い行ったり来たりをしなくていいので可読性は上がりそうだなと思いました。

ただ、refとreactiveの使いどころがあまり分からず、とりあえず今はfunctionごとにstateを設定し、toRefsを用いてreturnするという実装方法を用いてみました。

organismsCalc.vue
<template>
  <div class="calculator_wrapper">
    <div class="title_wrapper">
      <molecules-title />
    </div>
    <div class="input_wrapper">
      <molecules-calc-display
        :input-value="inputValue"
        :handle-show-history="handleShowHistory"
      />
    </div>
    <div class="button_wrapper">
      <molecules-calc-button
        :number-unit-array="numberUnitArray"
        :handle-add-input="handleAddInput"
      />
    </div>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive, Ref, toRefs } from '@vue/composition-api'
function useAddInput(operator: Record<string, string>) {
  const state = reactive<{ historyArray: string[]; inputValue: string }>({
    historyArray: [],
    inputValue: ''
  })
  const handleInputClear = (): void => {
    state.inputValue = ''
  }
  const handleInputBinomialOperator = (cmd: string): void => {
    state.inputValue += ' ' + cmd + ' '
  }
  const handleCalculation = (
    numArray: number[],
    operatorArray: string[],
    operator: Record<string, string>
  ): void => {
    operatorArray.map((element, index) => {
      if (index === 0) {
        switch (element) {
          case operator.plus:
            state.inputValue = String(numArray[index] + numArray[index + 1])
            break
          case operator.minus:
            state.inputValue = String(numArray[index] - numArray[index + 1])
            break
          case operator.multiplication:
            state.inputValue = String(numArray[index] * numArray[index + 1])
            break
          case operator.division:
            state.inputValue = String(numArray[index] / numArray[index + 1])
            break
        }
      } else {
        switch (element) {
          case operator.plus:
            state.inputValue = String(
              Number(state.inputValue) + numArray[index + 1]
            )
            break
          case operator.minus:
            state.inputValue = String(
              Number(state.inputValue) - numArray[index + 1]
            )
            break
          case operator.multiplication:
            state.inputValue = String(
              Number(state.inputValue) * numArray[index + 1]
            )
            break
          case operator.division:
            state.inputValue = String(
              Number(state.inputValue) / numArray[index + 1]
            )
            break
        }
      }
    })
  }
  const handleInputValueToArray = (): void => {
    const inputValueArray = state.inputValue.split(' ')
    const operatorArray: string[] = []
    const numArray: number[] = []
    inputValueArray.map(element => {
      if (
        element === operator.plus ||
        element === operator.minus ||
        element === operator.multiplication ||
        element === operator.division
      ) {
        operatorArray.push(element)
      } else {
        numArray.push(Number(element))
      }
    })
    handleCalculation(numArray, operatorArray, operator)
    state.historyArray.push(state.inputValue)
  }
  const handleAddInput = (cmd: string): void => {
    if (cmd === operator.allClear) {
      handleInputClear()
      return
    }
    if (
      cmd === operator.plus ||
      cmd === operator.minus ||
      cmd === operator.multiplication ||
      cmd === operator.division
    ) {
      handleInputBinomialOperator(cmd)
      return
    }
    if (cmd === operator.equal) {
      handleInputValueToArray()
      return
    }
    state.inputValue += cmd
  }

  return {
    ...toRefs(state),
    handleAddInput
  }
}

function useShowHistory(inputValue: Ref<string>) {
  const handleShowHistory = () => {
    console.log(inputValue)
  }

  return {
    handleShowHistory
  }
}
export default defineComponent({
  props: {
    operator: {
      type: Object,
      required: true
    },
    numberUnitArray: {
      type: Array,
      required: true
    }
  },
  setup(props) {
    const { operator } = props
    const { inputValue, handleAddInput } = useAddInput(operator)
    const { handleShowHistory } = useShowHistory(inputValue)

    return {
      inputValue,
      handleAddInput,

      handleShowHistory
    }
  }
})
</script>

<style lang="scss" scoped>
.input_wrapper {
  margin-bottom: 20px;
}
</style>

moleculesとatomsはpropsで受け取って表示させてるだけなので省きます。

感想

楽しかった。
すごい馬鹿みたいな感想ですが、新しいものを学ぶというものは手探りではありますが、楽しいものだなと思います。

個人的には早く使ってみたいなと思います。
実務でのvueファイルがファットになってきて可読性が非常に悪いので、composition-apiで処理ごとにまとめられれば少しはよくなりそうだな〜と思っています。

あとtsもしっかり学ぶ必要があるので、しっかキャッチアップしていこうと考えています。
読んでいただきありがとうございました。
何かご指摘があれば是非教えてください。

参照ページ

Composition API RFC

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

Data Provider Component という方法

正式名称が何かわからないのですが、こちらのサイトでは Data Provider Component という名前になっていたので、そう呼びます。

使う場面があったので、備忘として残します。

何ぞや?

render関数でスロットにデータを渡すことで、UIを持たず、データやロジックを提供するコンポーネントを定義することができます。

例1

Data Provider Component

DateTimeProvider.vue
<script>
export default {
  name: "DateTimeProvider",
  data() {
    return {
      timerId: -1,
      dateTime: new Date()
    };
  },
  mounted() {
    //定期的に時刻を更新
    this.timerId = setInterval(() => {
      this.dateTime = new Date();
    }, 500);
  },
  beforeDestroy() {
    //タイマー停止
    clearInterval(this.timerId);
  },
  render() {
    //$scopedSlotsの中には各slotが含まれている、またスロットは関数になっている。
    //ここからdefaultスロットを用いる、その際slotへのパラメータを渡す事ができる。
    const defaultSlot = this.$scopedSlots.default({
      dateTime: this.dateTime
    });
    //defaultSlotは配列なのでこれをそのまま描画する。
    //こうすることで,このエレメントをdivなどでラップする必要がなくなる。
    //ただしできるのは単一のエレメントだけ
    return defaultSlot[0];
  }
};
</script>

利用する側のコード

Sample01.vue
<template>
  <DateTimeProvider v-slot="{dateTime}">
    <!-- 直下に含むことができるのは1つだけ -->
    <div>
        <div>Date:{{dateTime}}</div>
        <div>LocaleString:{{dateTime.toLocaleString()}}</div>
        <div>秒:{{dateTime.getSeconds()}}</div>
    </div>
  </DateTimeProvider>
</template>

<script>
import DateTimeProvider from "./DateTimeProvider.vue";
export default {
  components: {
    DateTimeProvider
  }
};
</script>

受け取ったdateTime を3通りの方法で表示している。

これを実行すると画像のような挙動をします。
sample01.gif

時刻が更新されていることが確認できると思います。

ポイント

  • 現在日付を提供するコンポーネントはスロットにdateTimeを渡す
  • 利用する側のコンポーネントはv-slotdateTimeを受け取って加工、表示している。

に分けることができます。 利用する側のコンポーネントには現在日付という状態を持つ必要がなくなります。

例2

コードを入力したら名称をとってくるようなパターンです。

私も今回このパターンで使いました。

Data Provider Component

ProductNameProvider.vue
<script>
//コード,名称のマップ
const products = new Map();
products.set("001", "みかん");
products.set("002", "りんご");
products.set("003", "ぶどう");

export default {
  name: "ProductNameProvider",
  props: {
    productCode: {
      type: String,
      required: true
    }
  },
  data() {
    return {
      productName: ""
    };
  },
  methods: {
    async fireProduct() {
      await this.$nextTick();
      if (this.productCode === "") {
        this.productName = "";
        return;
      }
      //今回は例なので外部apiからではなくMapから名称を取得している。
      this.productName = products.get(this.productCode) || "(Not Found)";
    }
  },
  render() {
    return this.$scopedSlots.default({
      productName: this.productName
    })[0];
  }
};
</script>

利用する側のコード

Sample02.vue
<template>
  <ProductNameProvider ref="productNameProvider" :productCode="productCode" v-slot="{productName}">
    <div>
      <input type="text" v-model="productCode" maxlength="3" @change="onChangedProductCode" />
      <span>:{{productName}}</span>
    </div>
  </ProductNameProvider>
</template>

<script>
import ProductNameProvider from "./ProductNameProvider.vue";
export default {
  components: {
    ProductNameProvider
  },
  data() {
    return {
      productCode: ""
    };
  },
  methods: {
    onChangedProductCode() {
      this.$refs.productNameProvider.fireProduct();
    }
  }
};
</script>

productCode を変更したときにProductNameProviderfireProduct 関数を実行しています。

これを実行すると画像のような挙動をします。
sample02.gif

コード変更したときに名称を取得していることが確認できます。

この例では@change で明示的に 呼び出していますが ProductNameProvider 側でwatch する方法でも可能でしょう。

ポイント

  • 名称の取得する方法をProductNameProvider.vue に隠ぺいすることができる。
  • 利用する側はproductCode だけ持てばよく productNamedata に定義する必要がない。
  • 実際に名称をどのように表示するかは 利用する側のコンポーネントで自由に決定できる。
    • この例ではすぐ横に表示しているが、下に出したり上に出したりは利用側の自由

まとめ

  • データを提供するコンポーネントを定義する。
  • 利用する側はv-slot でデータを受け取る、そのことで変数を減らすことができる。
  • 受け取ったデータを、どう加工するか?、どう表示するか?は利用する側で決定することができる。

活躍する場面はそんなにあるかわからないですが、覚えておくと便利なテクニックだと思います。

参考

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

vue.jsでセレクトボタンを使ってCSSを変更する

この記事で分かること

  • セレクトボタンを使ってのイベント処理
  • dataで指定した値を使う方法

こちらの記事ではセレクトボタンを押した時にイベント処理を行い、cssを変更する方法を紹介します。

完成物のイメージとしてはセレクトボタンで指定した色にテキストの文字色が変わるというものです。

検索してもピンポイントで記事がすぐにヒットしなかったので、備忘録を兼ねて作成しました。
処理の詳しい理由などについては他の記事を参考にして頂ければ幸いです。

テンプレート

html
<select v-model="select" v-on:change="colorchange">
  <option value="black"></option>
  <option value="green">緑</option>
  <option value="blue"></option>
  <option value="red">赤</option>
</select>

今回使うディレクティブはv-modelとv-on:changeの2つです。

vueインスタンス

main.js
var app = new Vue({
  el: "#app1",
  data: {
    select: ""
  },
  methods: {
    colorchange: function () {
      switch (this.select) {
        case "red":
          app1.style.color = "red";
          break;

        case "green":
          app1.style.color = "green";
          break;

        case "black":
          app1.style.color = "black";
          break;

        case "blue":
          app1.style.color = "blue";
          break;
      }
    }
  }
})

解説

処理の全体的な流れはまず、v-modelを使ってセレクトボタンで指定されたvalue属性をdata(オプションオブジェクト)に追加します。
それから、v-on:changeを使ってセレクトボタの選択に応じた処理を行うメソッドを作成していきます。

v-model

main.js
<select v-model="select">

v-modelで指定するプロパティ名はdataで指定するものと同じにします。
今回はselectとしました。

main.js
var app = new Vue({
  el: "#app1",
  data: {
    select: ""
  }

このselectプロパティをdataにも記述します。

v-on:change

<select v-model="select" v-on:change="colorchange">

v-on:changeを使って、メソッド名を記述します。
今回はcolorchangeとしました。

main.js
  methods: {
    colorchange: function () {
      switch (this.select) {
        case "red":
          app1.style.color = "red";
          break;

        case "green":
          app1.style.color = "green";
          break;

        case "black":
          app1.style.color = "black";
          break;

        case "blue":
          app1.style.color = "blue";
          break;
      }
    }
  }

methodsの方でもcolorchangeメソッドを記述していきます。
switch文の中身については割愛させていただきますが、ここで1つポイントがあります。

それは、switch文で使う条件式についてです。
セレクトボタンで指定されたvalue属性に応じて処理を分けていきたいのですが、value属性の値は先ほど、v-modelで指定したselectに代入されています。
なので、条件式にはthis.selectと記述することでdataに代入されたvalue属性に応じて処理を分けていくことができます。
つまり、thisを使うことによってdataのプロパティを使えます。

分かりにくい言い回しなどあったかと思いますが、最後までお読み頂きありがとうございました。

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

#Vue 初心者 - data の配列の値を表示する、v-for で展開する

NOTE

要素の順番を直接指定して値を利用できる

例: {{ posts[0].id }} (配列の1番目の要素)

v-for であればすべての要素を展開できる

https://vuejs.org/v2/guide/list.html

v-for の :key="post.id" は特にこの例では必要ないと思うが、書かないと Lint に怒られるので書いている

Code

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <h1>{{ posts[0].id }} {{ posts[0].title }}</h1>
    <ul>
      <li v-for="post in posts" :key="post.id">
        {{ post.title }}
      </li>
    </ul>
  </div>
</template>

<script>

export default {
  name: 'App',
  data () {
    return {
      posts: [
        { id: 1, title: 'My journey with Vue' },
        { id: 2, title: 'Blogging with Vue' },
        { id: 3, title: 'Why Vue is so fun' }
      ]
    }
  }
}
</script>


View

image

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3102

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

Vue の v-for で bind しなさいと Lint に怒られるのだが何がいけないのか... (Elements in iteration expect to have 'v-bind:key' directives) ( #Vue )

Lintのエラーで 怒られるが、よく後ろを見ると、表示自体はちゃんと出来ているのが分かる

image

data

dataがこういう時

  data () {
    return {
      posts: [
        { id: 1, title: 'My journey with Vue' },
        { id: 2, title: 'Blogging with Vue' },
        { id: 3, title: 'Why Vue is so fun' }
      ]
    }

NG

<li v-for="post in posts">
  {{ post.title }}
</li>

OK

<li v-for="post in posts" :key="post.id">
  {{ post.title }}
</li>

OK ...?

bindの値は 特に v-for の中で表示に使われるわけではないらしい
存在しないキーを指定しても動作して、lintでは怒られなくなった ( null 的な扱いになっているかも )

<li v-for="post in posts" :key="post.xxx">
  {{ post.title }}
</li>

何故か

表示だけの場合はまず bind 指定なしでも問題ないが、削除したりする時に、要素を一意に特定するためのもの、とかだろうか

【徹底解説】これを見ればわかるvue.jsのv-forのkeyの動作 | アールエフェクト

Code

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <ul>
      <li v-for="post in posts">
        {{ post.id }} {{ post.title }}
      </li>
    </ul>
  </div>
</template>

<script>

export default {
  name: 'App',
  data () {
    return {
      posts: [
        { id: 1, title: 'My journey with Vue' },
        { id: 2, title: 'Blogging with Vue' },
        { id: 3, title: 'Why Vue is so fun' }
      ]
    }
  }
}
</script>

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3101

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

#Vue 初心者向け – component の props にデフォルト値を設定する

vue cli で作成したプロジェクト前提

css記述 は省略

src/components/Foo.vue

呼び出される component

  • props でデフォルトの値を設定する
  • component の呼び出しで何も渡さない時にデフォルト値を利用したいので、 required は false にしておく

ちなみに型を定義するだけならこんな書き方だが

  props: {
    msg: String
  }

他にも属性を指定したい場合は、一段ネストして書く

  props: {
    msg: {
      type: String,
      default: 'Ya!',
      required: false
    }
  }
<template>
  <div>
    <h1>{{ msg }}</h1>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: {
      type: String,
      default: 'Ya!',
      required: false
    }
  }
}
</script>

src/App.vue

component を呼び出すメインのっ処理
component を prpps の値の指定あり、値の指定なし、でそれぞれ呼び出す

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <Foo/>
    <Foo msg="Wow"/>
    <Foo/>
  </div>
</template>

<script>
import Foo from './components/Foo.vue'

export default {
  name: 'App',
  components: {
    Foo
  }
}
</script>

表示例

  • props に値を与えた component ではその文字列が表示される
  • props に値を与えなかった component ではデフォルトの文字列が表示される

image

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3099

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

Vue の export default が死ぬほど分からないなら vue cli でプロジェクト作成すれば良いじゃない ( #Vue )

課題と解決

  • 出来上がったプロジェクトコードでは何をしているか分からない
  • HTML + js だけの vue では import / export が使えない (ような気がする)
  • Vue CLI なら コマンド数回だけで vue プロジェクトが作成できるので、その雛形を見るのがわかりやすい
yarn global add @vue/cli
vue create my-project && cd my-project
yarn serve

vueってそもそもHTML+jsだけで動くの?サーバー環境が必要なの?って思ってたレベルの自分としては、vue cli での環境構築にたどり着くのさえ、さまよいながら、骨は折れたものだよ

src/App.vue

メインの App.vue で component に props の値 ( msg ) の string を渡しているのが分かる

<HelloWorld msg="Welcome to Your Vue.js App"/>

<template>
  <div id="app">
    <img alt="Vue logo" src="./assets/logo.png">
    <HelloWorld msg="Welcome to Your Vue.js App"/>
  </div>
</template>

<script>
import HelloWorld from './components/HelloWorld.vue'

export default {
  name: 'App',
  components: {
    HelloWorld
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

src/components/HelloWorld.vue

component では props の型 ( msg ) を string で定義して、入れ物だけ作っているのが分かる

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <p>
      For a guide and recipes on how to configure / customize this project,<br>
      check out the
      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
    </p>
    <h3>Installed CLI Plugins</h3>
    <ul>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a></li>
      <li><a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a></li>
    </ul>
    <h3>Essential Links</h3>
    <ul>
      <li><a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a></li>
      <li><a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a></li>
      <li><a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a></li>
      <li><a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a></li>
      <li><a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a></li>
    </ul>
    <h3>Ecosystem</h3>
    <ul>
      <li><a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a></li>
      <li><a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a></li>
      <li><a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a></li>
      <li><a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a></li>
      <li><a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a></li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  props: {
    msg: String
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
  margin: 40px 0 0;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>

なぜ分かりづらいと思ったか?

プログラム言語であれば関数に対して引数を「与える」わけだけど、
vueの場合は component が export される側として props を持つ、的な表現の逆な感じが、どうにもつっかえて、頭に入ってこなかったのかもしれない

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3098

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

yarn + vue cli で環境構築 (プロジェクト作成)してlocalhost でサーバーを起動する手順 ( #vue )

コマンド

yarn global add @vue/cli
vue create my-project && cd my-project
yarn serve

vue create の時にyes no の質問をされたらてきとうに答えたりエンターを押して進む

http://localhost:8080/ にアクセスする

image

参考

Vue.js を vue-cli を使ってシンプルにはじめてみる - Qiita

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3097

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

#Vue CLI で vue create <project> が使えない ( You may want to run the following to upgrade to Vue CLI 3: )

$ vue create vue-project
  vue create is a Vue CLI 3 only command and you are using Vue CLI 2.9.6.
  You may want to run the following to upgrade to Vue CLI 3:

  npm uninstall -g vue-cli
  npm install -g @vue/cli

このとおり

  npm uninstall -g vue-cli
  npm install -g @vue/cli

を実行してもバージョンが変わらない

解決

yarn の vue が入っていたっぽい
yarn で install し直すとバージョンが上がった

yarnを使う場合 (例)

yarn global add @vue/cli

which vue
/usr/local/bin/vue

vue --version
@vue/cli 4.3.1

npm を使う場合 (例)

yarn global remove @vue/cli

npm install -g @vue/cli

which vue
/Users/yumainaura/.nodebrew/current/bin/vue

参考

Vue.js を vue-cli を使ってシンプルにはじめてみる - Qiita

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3096

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

Chart.jsとAPIによるコロナウイルスのデータのグラフ化~Vueを添えて~

はい、皆さんこんにちは!
ある日ネットサーフィンをしていたら、数値を基にグラフを作成してくれる「Chart.js」と世界のコロナウイルスに関するデータを提供してくれるAPIを見つけたので、今日はこれらとVue.jsを組み合わせて世界のコロナウイルスのデータを表示するグラフを作っていきたいと思います。

今回作るもの

APIから国別のコロナウイルスについてのデータを取得し、そのデータをChar.jsを使って棒グラフとしてブラウザ(今回はGoogle Chrome)へ映すプログラム。また、今回扱うAPIはコロナウイルスについて数種類のデータを提供しているため、グラフの利用者がほしいデータの種類を利用者自身がブラウザ上でを選べるようにする。

本日の材料はこちら

  • Chart.jsファイル(ダウンロードはこちら
  • API(ダウンロードはこちら)
  • Vue.js(グラフの値の操作等で使うだけなので、面倒くさければjavascriptをそのまま使っても大丈夫。ちなみにダウンロードはこちら)

では、実際に作っていく!

今回の工程
1. Chart.js使い、ブラウザ上にグラフを作成する。
2. APIからデータを取得する
3. ブラウザ上からグラフを操作できる仕組みを作る

1. Chart.js使い、ブラウザ上にグラフを作成する。

Chart.jsの扱い方はとても簡単です。Chart.jsを使うためには、CDNを利用するかファイル自体をダウンロードし、ブラウザへ表示したいHTMLファイルにscriptタグで埋め込むだけ!
あとはChart.jsの公式ホームページこの記事に書いてあるコードをコピペすればすぐに使えます。
あとはコピペしたコードのChartクラス内のdataプロパティなどをいじっていれば扱い方はすぐにわかると思います。

2. APIからデータを取得する

APIからデータを取り出すために、今回はfetch()を使ってみました。コードはこんな感じ

js
 const infoArray = await fetch('https://api.covid19api.com/summary') //awaitはasyncがついている関数内でしか使えないので注意!
    .then(function(response) {
      return response.json();
    }).then(function (data) {
        return data.Countries;
    });

データは変数(今回はinfoArray)にあるので、これを少し加工してChartクラス内のdataプロパティにセットすれば、APIからの情報をグラフで表せます!

3. ブラウザ上からグラフを操作できる仕組みを作る

ブラウザだけではデータベースもなく、jsだとcookieを使ったユーザーからのデータの保持が手軽にできる方法がない。(自分が探していないだけでもしかしたらあるかも)ではどうすれば良いか?そこで今回使用するのはsessionStorage。こちらは配列でブラウザでのユーザーの入力データを保持できます。

js
sessionStorage['kaneko'] = 'kinironoyatu'; 
sessionStorage['Orotimaru'] = 'Yu-sho-'; 

console.log(sessionStorage.getItem('Kaneko')); //consoleにkinironoyatuと表示される
console.log(sessionStorage.getItem('Orotimaru')); //consoleにYu-sho-と表示される

localStorage.tokenでデータを保持しようと初めは考えていましたが、複数のデータを保持させることができなかったので、sessionStorageを使用しました。もしsessionStorageについてもう少し知りたい場合は下の記事を見てみてください。
Qiita sessionStorageをつかってみる

このsessionStorageを使用して作成した見た目(HTML)とその機能(Vue.js)がこちら

body
<p>Select a category you want to know.</p>
    <div id="vue">
        <form style="display: flex;">
            <div v-for="category in categories">
                <input type="radio" name="category" :value=category v-model="select">{{category}} //見たいデータのカテゴリーの選択しの表示
            </div>
            <button @click="selectCategory" style="margin:0 20px;">check!</button> //カテゴリーの切り替え
            <button @click="back" style="margin:0 10px;">back</button> //次の27か国のデータを表示
            <button @click="next" style="margin:0 10px;">next</button> //前の27か国のデータを表示
        </form>
    </div>
    <canvas id="myChart"></canvas> //グラフの表示
Vue.js
const categories = ['NewConfirmed', 'TotalConfirmed', 'NewDeaths', 'TotalDeaths', 'NewRecovered', 'TotalRecovered',]
const getEntry = sessionStorage.getItem('entry') 

new Vue({
    el:'#vue',
    data: {
        categories: categories,
        select: '',
    },
    created: function () {
        if (!getEntry) {
            sessionStorage['entry'] = '0'   
        }
    },
    methods: {
        selectCategory: function () {
            sessionStorage['select'] = this.select
        },
        next: function () {
            switch (getEntry) {
                case '0':
                    sessionStorage['entry'] = '27'
                    break
                case '27':
                    sessionStorage['entry'] = '54'
                    break
                case '54':
                    sessionStorage['entry'] = '81'
                    break
                case '81':
                    sessionStorage['entry'] = '108'
                    break
                case '108':
                    sessionStorage['entry'] = '135'
                    break
                case '135':
                    sessionStorage['entry'] = '162'
                    break
                case '162':
                    sessionStorage['entry'] = '189'
                    break
                default:
                    sessionStorage['entry'] = '216'
                    break
            }
        },
        back: function () {
            switch (getEntry) {
                case `216`:
                    sessionStorage['entry'] = '189'
                    break
                case '189':
                    sessionStorage['entry'] = '162'
                    break
                case '162':
                    sessionStorage['entry'] = '135'
                    break
                case '135':
                    sessionStorage['entry'] = '108'
                    break
                case '108':
                    sessionStorage['entry'] = '81'
                    break
                case '81':
                    sessionStorage['entry'] = '54'
                    break
                case '54':
                    sessionStorage['entry'] = '27'
                    break
                default:
                    sessionStorage['entry'] = '0'
                    break
            }
        }
    }
})

簡単に機能を説明しますと、HTML部分の<button>を押すとVue.jsの部分でそれに応じてsessionStorageを書き換える感じになっています。
また、もともとのAPIの総データ数があまりにも多く、ブラウザで一度にすべての国の情報を載せられなかったため表示できる国の数を制限しました。next、backを押すことでブラウザに表示されていない国のデータを表示させることができま。)あと悔しいことにsessionStorageは文字列データしか扱えないため、こんなに不格好なプログラムになってしまいました。

完成したものがこちら!

GitHubで公開してます

git cloneなどでコードを詳しく見たい方はこちら
すぐに動かせるので、ぜひ動かしてみてください!

今回の記事はこれで以上です。ではまた!

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

Vue.jsについて 簡易まとめメモ

Vue.jsとは

JavaScriptフレームワーク
MVVMパターン(Model-View-ViewModel)
ユーザーインターフェイスを構築するためのプログレッシブフレームワーク

MVVMパターンとは

  • Model:データ管理 ViewModelの担当外全てを担当
  • View:ユーザインターフェース
  • ViewModel:Model→Viewへの出力データを渡し、View→Modelへの入力データを渡す

プログレッシブフレームワークとは

どんなとき、どんな規模にも段階的に柔軟に使える思想
・コンポーネント設計・ライブラリの活用・小規模から大規模まで可能になる

コンポーネント設計とは

コンポーネント(component) 部品、成分、構成要素などの意味を持つ
GUIのパーツをモジュール化したもの
簡単に言うと部品を作成して組み立てようという事

アトミックデザイン(Atomic Design)とは

  • atoms:原子
    • ボタン、テキスト、ボックスなど
  • Molecule:分子
    • Atomsの組み合わせ ログインフォームなど
  • Organisms:有機体
    • MoleculesやAtomを組み合わせ ヘッダーなど
  • Template:テンプレート
    • Organismsを組み合わせ ワイヤーフレーム
  • Pages:ページ 
    • 文言、画像、データなどの最終調整

ボタンなどの小さなコンポーネント(atoms)を作り
ログインフォームなどの中くらいのコンポーネント(Molecule)を作り
ヘッダーやページ全体(Organisms、Template、Pages)を作っていく考え方

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

#Vue 初心者 – 1ファイル内で component を使うごく簡単な例 ( HTML )

  • component の中に何を書けば良いかドキュメントから分からない
  • export とか import の例とかに進むとさらによく分からない
  • ごく簡単な例から確認してみよう

  • <li>foo</li><li>bar</li> を component にして <ul> の中で使ってみる
  • script 部分で component を作っておくと、 HTML的なところでタグとして利用することができる
<!-- https://vuejs.org/v2/guide/components-registration.html -->

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
<body>
  <div id="example">
    <ul>
      <list-item-foo></list-item-foo>
      <list-item-foo></list-item-foo>
      <list-item-foo></list-item-foo>
      <list-item-bar></list-item-bar>
      <list-item-bar></list-item-bar>
    </ul>
  </div>

  <script>

    Vue.component('list-item-foo', {
      template: '<li>foo</li>'
    })

    Vue.component('list-item-bar', {
      template: '<li>bar</li>'
    })

   new Vue({
      el: '#example',
    })
</script>
</body>

参考

Vue入門

表示の例

image

一歩進む

Component を生成する時に変数名を付けておいて、それに別の名前をつけて Vue のインスタンスに与えるということが出来るみたいだ

<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.js"></script>
<body>
  <div id="example">
    <ul>
      <list-item-foo></list-item-foo>
      <list-item-foo></list-item-foo>
      <list-item-foo></list-item-foo>
      <list-item-bar></list-item-bar>
      <list-item-bar></list-item-bar>
    </ul>
  </div>

  <script>
    var ComponentFoo = Vue.component('list-item-foo', {
      template: '<li>foo</li>'
    })
    var ComponentBar = Vue.component('list-item-bar', {
      template: '<li>bar</li>'
    })

    new Vue({
      el: '#example',
      components: {
        'list-item-foo': ComponentFoo,
        'list-item-bar': ComponentBar
      }
    })
</script>
</body>

別ファイルから import するには?

script から import できるわけではないのだろうか...

まだよく分かっていない

  <script>
    import ComponentFoo from './ComponentA'

エラー

component.html:16 Uncaught SyntaxError: Cannot use import statement outside a module

HTMLファイルや他のファイルだけでは動かなさそうなことが書いてある気もする

image

参考

Vue.jsで単一ファイルコンポーネントを使いたい場合はWebpack等のモジュールバンドラでビルドするのが本来の方法です。しかし、学習コストや環境構築の手間がそれなりにかかります。

【Vue.js】HTML importsを用いた擬似単一ファイルコンポーネント - Qiita

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3095

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

#Vue で配列の要素を v-for に直接書いて展開する ( elements write directly as v-for array )

<!-- https://vuejs.org/v2/guide/list.html -->

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<body>
  <ul id="example">
    <!-- 配列要素を直接書く -->
    <li v-for="(value) in [1,2,3]">
      {{ value }}
    </li>
  </ul>

  <!-- 何も持たない Vue インスタンス -->
  <script>
    new Vue({
      el: '#example',
    })
  </script>
</body>

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3094

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

#Vue の v-for で form select option の 簡単なHTMLフォーム作る ( key value の配列を展開する )

<!-- https://vuejs.org/v2/guide/list.html -->

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<body>

  <div id="example">
    <form action="./">
      <select name="param">
        <option v-for="(value, key) in selectableItems" :value="value">
          {{ key }}
        </option>
        <input type="submit">
      </select>
    </form>
  </div>

  <script>
    new Vue({
      el: '#example',
      data: {
        selectableItems: {
          foo: "foo-value",
          bar: "bar-value"
        }
      }
    })
  </script>
</body>

image

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3093

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

#Vue で data に配列を二個持って展開する簡単な例 ( foreach two kind array simple case )

  • data は複数のデータを、それぞれ別の名前で持てる。好きな名前をつけられるので、それを v-for で使えば良い。
<!-- https://vuejs.org/v2/guide/list.html -->

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<body>
  <div id="example">
    <ul>
      <li v-for="(value) in fooItems">
        {{ value }}
      </li>
    </ul>

    <ul>
      <li v-for="(value) in barItems">
        {{ value }}
      </li>
    </ul>
  </div>

  <script>
    new Vue({
      el: '#example',
      data: {
        fooItems: [1,2,3],
        barItems: ["X", "Y", "Z"],
      }
    })
  </script>
</body>

結果

image

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3092

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

#Vue で key value のハッシュを v-for で展開する HTML の例 ( foreach key value array )

<!-- https://vuejs.org/v2/guide/list.html -->

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>

<body>
  <ul id="example">
    <li v-for="(key, value) in someItems">
      {{ key }} - {{ value }}
    </li>
  </ul>

  <script>
    new Vue({
      el: '#example',
      data: {
        someItems: {
          message1: 'Foo',
          message2: 'Bar',
        }
      }
    })
  </script>
</body>

結果

image

Original by Github issue

https://github.com/YumaInaura/YumaInaura/issues/3091

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

Vue.js + NestJSでデスクトップアプリを実装する

Excel帳票を出力するツールを作る必要があったため、この機会にJavaScript (TypeScript) のみで実装できないか検討した。

ネイティブアプリを作るのであれば定番はElectronだが、将来的にWebアプリ化することも考えて、Vue.js(フロントエンド)+ NestJS(バックエンド)という構成とし、nexeでパッケージ化している。

システム構成.png

構成の検証目的のたま、実装している機能はExcelファイルのエクスポート機能のみですが、GitHubにソースコードを載せている。

https://github.com/unhurried/vue-nest-desktop-app

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

【Vue】記法の違いまとめ

はじめに

Vue.jsを勉強するにあたり、この書き方ってどう違うの?と思ったものをまとめました。
少しJavaScriptも混ざっていますが、お気になさらず。

Q. function()を使う書き方とそうでない書き方は何が違うの?

export default {
  methods: {
    // A
    method: function(arg) {
      console.log("メソッドAです")
    },

    // B
    method(arg) {
      console.log("メソッドBです")
    },
  },
};

A. method: function()を短縮したものがmethod()です。

method()という記法はES2015(ES6)で導入された短縮形です。ES5では対応していません。
参考:メソッド定義 - JavaScript | MDN

Q. dataの表記がいろいろあるんだけど?

export default {
  // A
  data: {
    name: "太郎",
    age: 10
  },

  // B
  data: function() {
    return {
      name: "太郎",
      age: 10
    };
  },

  // C
  data() {
    return {
      name: "太郎",
      age: 10
    };
  },
};

A. コンポーネントで使う場合は関数にしなければなりません。

Aのパターンでは、以下参考サイトのようにコンポーネントでデータが独立しなくなってしまいます。そのため、少なくともコンポーネントではdataは関数(BかCの形)にする必要があります。BとCに違いについては前述のとおりです。

コンポーネントのdataオプションは関数でなければなりません。各インスタンスが返されるデータオブジェクトの独立したコピーを保持できるためです:
コンポーネントの基本 — Vue.js

区別するのもややこしいので、基本的にすべて関数で書くようにすればいいと思います。
関数で書くデメリットはない気がします……。

Q. v-forin, ofって使い分けとかあるの?

<div v-for="item of list">
<div v-for="item in list">

A. ないです。

意味は全く同じなので、同じプロジェクト内で統一して使うようにすればいいと思います。

区切り文字としてinの代わりにofを使用することができます。これは JavaScript のイテレータ構文に近いものです
リストレンダリング — Vue.js

イテレータ構文は以下のような書き方をします。

for (variable of iterable) {
  statement
}

引用:for...of - JavaScript | MDN

Q. propsの書き方は何が違うの?

export default {
  // A
  props: ["name", "user"]

  // B
  props: {
    name: String,
    user: Object,
    },

  // c
  props: {
    name: {
      type: String,
      default: ""
    },
    user: {
      type: Object,
      default: () => {},
    },
  },
};

A. 型を指定しているかどうかです。

  • Aのパターン
    • 型を指定していないため、渡されたデータの型が何であろうと代入する。警告も出ない。
  • Bのパターン
    • 型を指定しているため、違う型のデータが渡された場合は警告を表示する。
  • Cのパターン
    • 型を指定し、かつデフォルト値を設定する。
    • Array, Objectのデフォルト値は関数で返す必要がある(いろんな人の書き方を見た感じではアロー関数の形が多かった)。

コンポーネントはプロパティに対して、上で見たように型などの要件を指定することができます。もし指定した要件が満たされない場合、 Vue はブラウザの JavaScript コンソールにて警告します。
プロパティ — Vue.js

Object型を指定したuserに"テキスト"というString型を渡すと、以下のような警告が出ます。
(ただし一応表示される)

[Vue warn]: Imvalid prop: type check failed for prop "user". Expected Object, got String with "テキスト".

Q. this.namenameって違うの?

A. <template>内では同じものを示します。

テンプレート内では同一として扱われます。

<template>
  <div>
    {{ this.name }}
    {{ name }}
  </div>
</template>

ただし、メソッドについては以下のような挙動になります。

<template>
  <div>
    <button @click="showDialog"> <!-- 動く -->
    <button @click="showDialog()"> <!-- 動く -->
    <button @click="this.showDialog"> <!-- 動く -->
    <button @click="this.showDialog()"> <!-- 動かない -->
  </div>
</template>

一番下の書き方だとエラーが出ます。

[Vue warn]: Error in v-on handler: "TypeError: Cannot read property 'showDialog' of null"

でも、普通thisは省略します。

テンプレート内ではthis.nameではなくnameを使います。
v-onで使うメソッドはshowDialogにし、引数がある場合のみかっこを付けます。

参考:vue/this-in-template
   vue/v-on-function-call

A. <script>内では違うものを示します。

thisが付いていないものは変数として認識します。当然ですね。

export default {
  data() {
    return {
      name: "花子",
    }
  },
  methods: {
    showName() {
      const name = "太郎"
      console.log(this.name)  // "花子"
      console.log(name)   // "太郎"
    }
  }
}

おわりに

ほとんどの内容が公式ページに書かれているので、公式は神です。
ご指摘、実際にはこんな風によく書くよ、などのご意見がございましたら、コメントをいただければ幸いです。

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