公式ホームページのIntro to Vue 3クラス11 (Forms & v-model)

# 11. Forms & v-model

>クラス https://www.vuemastery.com/courses/intro-to-vue-3/forms-and-v-model-vue3

VS Codeから該当フォルダを選択して、開きます。その後に、配下のgit部分をクリックして、ブランチをL11-startへ入れ替えてください。

> ブランチ切り替え方法 https://qiita.com/phjg81/items/e6695eb2334d9297a318

今日のポイントは利用者が製品に関するレビューを残せるようにすることです。

## v-model
v-bindは片方向データバインドです。両方向のデータバインドにはv-modelが使われます。v-modelが多めになるほど、重くなるので、v-modelでないといけないもののみをv-modelとして使います。
ユーザが操作したデータをサーバのデータベースに保存する場合に使ったら良いですね。

まずは、ReviewForm.jsをcomponentsフォルダは以下に追加してください。

ファイル作成方法
> ファイル作成方法 https://qiita.com/phjg81/items/dbff26b87a2c8e565f20

```javascript:ReviewForm.js
app.component('review-form', {
    template:
    /*html*/
    `<form class="review-form">
      <h3>Leave a review</h3>
      <label for="name">Name:</label>
      <input id="name">
  
      <label for="review">Review:</label>      
      <textarea id="review"></textarea>
  
      <label for="rating">Rating:</label>
      <select id="rating">
        <option>5</option>
        <option>4</option>
        <option>3</option>
        <option>2</option>
        <option>1</option>
      </select>
  
      <input class="button" type="submit" value="Submit">
    </form>`,
    data() {
      return {
        name: '',
        review: '',
        rating: null
      }
    }
  })
```

v-modelを追記します。

```javascript:ReviewForm.js：v-model追記
app.component('review-form', {
    template:
    /*html*/
    `...
      <input id="name" v-model="name">
  
      ...     
      <textarea id="review" v-model="review"></textarea>
  
      ...
      <select id="rating" v-model.number="rating">
        <option>5</option>
        <option>4</option>
        <option>3</option>
        <option>2</option>
        <option>1</option>
      </select>
  
      ...
    </form>`,
    data() {
      ...
    }
  })
```
ブラウザーのrefreshを防止するため、@submit.preventを利用して、転送イベントを追記します。

```javascript:ReviewForm.js：v-model追記と転送イベント追記
app.component('review-form', {
    template:
    /*html*/
    `<form class="review-form" @submit.prevent="onSubmit">
      ...     
      <input class="button" type="submit" value="Submit">
    </form>`,
    data() {
      ...
    },
    methods: {
      onSubmit() {
        let productReview = {
          name: this.name,
          review: this.review,
          rating: this.rating,
        }
        this.$emit('review-submitted', productReview)
   
        this.name = ''
        this.review = ''
        this.rating = null
      }
    }
 }
  })
```

その後にはindex.htmlに追加します。

```html:index.html：ReviewForm追記
    <!-- Import Components -->
    <script src="./components/ProductDisplay.js"></script>
    <script src="./components/ReviewForm.js"></script>

```

```javascript:ProductDisplay.js：review-form追記とaddReview追加
app.component('product-display', {
  props: {
    ...
  },
  template: 
  `<div class="product-display">
    <div class="product-container">
    ...
    </div>
    <review-form @review-submitted="addReview"></review-form>
  </div>`,
  data() {
    return {
        reviews: []
    }
  },
  methods: {
      addReview(review) {
        this.reviews.push(review)
      }
  },
  computed: {
      ...
  }
})
```
これで、レビュー追加が可能になります。
作成されたレビューを表示して見ます。componentsフォルダにReviewList.jsを追加します。

```javascript:ReviewList.js
app.component('review-list', {
  props: {
    reviews: {
      type: Array,
      required: true
    }
  },
  template:
  /*html*/
  `
  <div class="review-container">
  <h3>Reviews:</h3>
    <ul>
      <li v-for="(review, index) in reviews" :key="index">
        {{ review.name }} gave this {{ review.rating }} stars
        <br/>
        "{{ review.review }}"
        <br/>
      </li>
    </ul>
  </div>
`
})
```

```html:index.html：ReviewList追記
    <!-- Import Components -->
    <script src="./components/ProductDisplay.js"></script>
    <script src="./components/ReviewForm.js"></script>
    <script src="./components/ReviewList.js"></script>
```

レビュー数分だけ、繰り返して表示するため、「v-if="reviews.length"」も追記します。

```javascript:ProductDisplay.js：review-list追記とv-if
app.component('product-display', {
  props: {
    ...
  },
  template: 
  `<div class="product-display">
    <div class="product-container">
    ...
    </div>
    <review-list v-if="reviews.length" :reviews="reviews"></review-list>
    <review-form @review-submitted="addReview"></review-form>
  </div>`,
  data() {
    return {
        reviews: []
    }
  },
  methods: {
      addReview(review) {
        this.reviews.push(review)
      }
  },
  computed: {
      ...
  }
})
```

最後に送信時のValidationも追記します。

```javascript:ReviewForm.js：Validation追記
app.component('review-form', {
    template:
      ...,
    data() {
      ...
    },
    methods: {
      onSubmit() {
        if (this.name === '' || this.review === '' || this.rating === null) {
            alert('Review is incomplete. Please fill out every field.')
            return
        }
        ...
      }
    }
 }
  })
```

これで、簡単にvue3を学習して見ました。他にはhttps://www.vuemastery.com/courses/ を参照して追加学習をして見ても良いと思います。

今回の課題はreview-formに「Would you recommend this product?」を入れて、その内容をreview-listへ表示して見ましょう。

<details>
<summary>課題確認</summary><div>

```javascript:ReviewForm.js：recommend追記
app.component('review-form', {
    template:
    /*html*/
    `...
  
      <label for="rating">Rating:</label>
      <select id="rating" v-model.number="rating">
        <option>5</option>
        <option>4</option>
        <option>3</option>
        <option>2</option>
        <option>1</option>
      </select>

      <!-- solution -->
      <label for="recommend">Would you recommend this product?</label>
      <select id="recommend" v-model="recommend">
        <option>Yes</option>
        <option>No</option>
      </select>

      <input class="button" type="submit" value="Submit">
    </form>`,
    data() {
      return {
        ...
        // solution
        recommend: null
      }
    },
    methods: {
      onSubmit() {
        if (this.name === '' || this.review === '' || this.rating === null || this.recommend === null) {
            ...
        }
        let productReview = {
          ...
          // solution
          recommend: this.recommend
        }
        ...
   
        ...
        // solution
        this.recommend = null
      }
    }
 }
  })
```

```javascript:review-list.js：recommend追記
app.component('review-list', {
    props: {
      ...
    },
    template:
    `
      ...
          "{{ review.review }}"
          <br/>
          <!-- solution -->
          Recommended: {{ review.recommend }}
        </li>
      </ul>
    </div>
  `
  })
```

</div>
</details>

お疲れさまでした！