【Vue】親子間値受け渡しを✅

ブシトラさんと武蔵小杉の渡来部に行ってきました。ブシ渡来部ですね。何つって。殴っていいですよ。近所っていいですね。

そしてチームが決まりました。来年の2月からチーム開発を開始していきます。楽しみですね〜〜

さて、今回は親子間で値を受け渡す千本ノックをしていきたいと思います。

親子コンポーネントの値受け渡しとは

二つのコンポーネント間でv-bindやv-onを使って値の受け渡しをすること。

一つのコンポーネントは一つの役割を持つという前提で分割を進める。そうすることでコードの冗長化を防ぐ。rubyでいうpartial。

分割されたコンポーネントを、さらに値を渡す側(親)と値を受け取る側(子)に分類。

ここでいう子は、ボタンやインプットフォーム、ヘッダーなど、一つの役割だけを持ったコンポーネントを指す。

そして親は、子を複数入れることで機能するコンポーネントを指す。

値の受け渡しにおいて重要な概念

props

配列でdataのプロパティを渡す

親⇨子にコンポーネントを渡すときはpropsを使う。

紛らわしいのが、親⇨子という流れなのに、コンポーネントをインポートしているのは親だということ。日本語的に、コンポーネントを「渡される」対象が子なら、子がインポートするのが普通なのではと思う。

#src/components/Child.vue

<template>
  <div>
    <p>{{ statement }}</p>
  </div>
</template>

<script>
export default {
  props: {
    statement: {
     type: String,
     default: 'hogehoge'
    }
  }
#propsの名前と型を子コンポーネント内で定義している。
}
</script>

補足:

propsの名前と型を子コンポーネント内で定義している。

 #src/pages/App.vue
<template>
  <div>
    <h1>Hello from App.vue</h1>
    <Child statement='choose to go to the moon.'/>
#ChildのgreetをHello with propsという値に上書きしている。
  </div>
</template>

<script>
import Child from './components/Child.vue'
#子コンポーネントを親コンポーネントがインポートしている。
export default {
  components: {
    Child
#childにコンポーネントを渡している。
  }
}
</script>

補足:

ChildのgreetをHello with propsという値に上書きしている。

コンポーネントを親コンポーネントがインポートしている。

ポイント

$emit

コンポーネントのデータを親コンポーネントへ渡し、親コンポーネントのイベントを発火させることができる。

子⇨親にコンポーネントを渡している。

src/components/Child.vue
<template>
  <div>
    <p>subordinate_num: {{ subordinate_num }}</p>
    <button @click='send'>君主に値を渡す</button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      subordinate_num: 0
    };
  },
  methods: {
    send() {
      this.$emit("my-click", this.subordinate_num);
 #親コンポーネントでは、子で定義していたsend()アクションをmy-clickという名前で実行できる。
    }
  }
};
</script>

補足:

	this.$emit("my-click", this.subordinate_num);

とすることで、親コンポーネントでは、子で定義していたsend()アクションをmy-clickという名前で実行できる。

#src/pages/App.vue
# パターン1:受け取った値をそのまま使う場合は$eventで受け取る
<template>
  <div>
    <h1>Hello from App.vue</h1>
    <p>lord_num: {{ lord_num }}</p>
    <Child @my-click='lord_num = $event'/>
#受け取った値を@に続いた形で記述。
#バインドさせた値 = $eventと書くことで、そのまま使用できる。
  </div>
</template>

<script>
import Child from './components/Child.vue'
export default {
  data() {
    return {
      lord_num: 100
    }
  },
  components: {
    Child
  }
}
</script>

補足:

受け取った値を@に続いた形で記述。 バインドさせた値 = $eventと書くことで、そのまま使用できる。

実際に使ってみる

radio button

まずは親に全てを記述する

<template>
  <v-container
    class="px-0"
    fluid
  >
    <v-radio-group v-model="radioGroup" >
  
      <v-radio
        v-for="n in 3"
        :key="n"
        :label="`Radio ${n}`"
        :value="n"
      ></v-radio>
    </v-radio-group>
  </v-container>
</template>
<script>
export default {
     data () {
      return {
        radioGroup: 1,
      }
    },
};
</script>

動きは以下の通り

Image from Gyazo

それでは親子コンポーネントに分けていく。

v-modelを使った場合

RadioParent.vue

<template>
  <v-container class="px-0" fluid>
    <p>Parent: {{ radioGroup }}</p>
    <RadioChild v-model="radioGroup" />
  </v-container>
</template>
<script>
import RadioChild from '../components/forms/RadioChild.vue';
export default {
  components: {
    RadioChild,
  },
  data() {
    return {
      radioGroup: 1,
    };
  },
};
</script>

RadioChild.vue

 <template>
  <v-container class="px-0" fluid>
    <p>Child: {{ childRadio }}</p>
    <v-radio-group v-model="childRadio">
      <v-radio v-for="n in 3" :key="n" :label="`Radio ${n}`" :value="n"></v-radio>
    </v-radio-group>
  </v-container>
</template>
<script>
export default {
  props: {
    value: {
      type: String,
    },
  },
  data() {
    return {
      radioGroup: 1,
    };
  },
  computed: {
    childRadio: {
      get() {
        return this.value;
      },
      set(newValue) {
        this.$emit('input', newValue);
      },
    },
  },
};
</script>

動きを確認する

Image from Gyazo

select form

クリックしたら項目がずらっと出てくるようなフォームを作る。

SelectParent.vue

<template>
<div>
<p>{{selected}}</p>
  <SelectChild
    :options="options"
    :selected="selected"
    @select="$emit('select', $event)"
  />
  </div>
</template>

<script>
import SelectChild from '../components/forms/SelectChild.vue';

export default {

  components: {
   SelectChild
  },
  props: {
    selected: {
      type: Array,
      required: true
    }
  },
  data: () => ({
    options: [
      { text: 'hoge1', value: 'hoge1' },
      { text: 'hoge2', value: 'hoge2' }
    ]
  })
}
</script>

SelectChild.vue

<template>
  <select
    v-model="select"
    multiple
  >
    <option
      v-for="(option, key) in options"
      :key="key"
      :value="option.value"
    >
      {{ option.text }}
    </option>
  </select>
</template>

<script>
export default {
  props: {
    selected: {
      type: Array,
      required: true
    },
    options: {
      type: Array,
      required: true
    }
  },
  computed: {
    select: {
      get () {
        return this.selected;
      },
      set (value) {
        this.$emit('select', value);
      }
    }
  }
};
</script>

実際の動きを見てみる

Image from Gyazo

【Vue/Nuxt】multipleなselectのデータを親子間で受け渡すには?

button

クリックされたら親コンポーネントにある値を変化させるだけのボタンコンポーネントを作る。

ButtonParent.vue

<template>
  <div class="parent">
      <p>Parentnum: {{num}}</p>
    <ButtonChild @parent-event="parentEvent" />
  </div>
</template>

<script >
import ButtonChild from '../components/forms/ButtonChild.vue';
export default {
  components: {
    ButtonChild,
  },
  data(){
      return{
num: 1,
      }
  },
  methods: {
    parentEvent() {
      this.num += 1;
    }
  }
}
</script>

ButtonChild.vue

<template>
  <div class="child">
    <v-btn color="primary" @click="childEvent">クリック</v-btn>
  </div>
</template>

<script >

export default {
  methods: {
    childEvent() {
        //親にparent-eventを渡している
      this.$emit('parent-event')
    }
  }
}
</script>

実際の動き。親子間で値を渡すことができている。

Image from Gyazo

変数を渡す方法もある。

ButtonChild.vue

<template>
  <div class="child">
      <p>child_num: {{child_num}}</p>
    <v-btn color="primary" @click="childEvent">クリック</v-btn>
  </div>
</template>

<script >

export default {
    data: function() {
    return {
      child_num: 0
    };
  },
  methods: {
    childEvent() {
        //親にparent-eventを渡している
      this.$emit('parent-event', this.child_num)
    }
  }
}
</script>

ButtonParent.vue

<template>
  <div class="parent">
      <p>Parentnum: {{num}}</p>
    <ButtonChild @parent-event="parentEvent" />
  </div>
</template>

<script >
import ButtonChild from '../components/forms/ButtonChild.vue';
export default {
  components: {
    ButtonChild,
  },
  data(){
      return{
num: 5,
      }
  },
  methods: {
    parentEvent(value) {
      this.num += (value + 1);
    }
  }
}
</script>

実際の挙動

Image from Gyazo

propsと$emitでデータを引き渡す - Qiita

まとめ

後書き

明日のポートフォリオレビュー会が恐ろしい、、