vue.jsのDjangoでの使い所

# vue.jsのDjangoでの使い所

私がつくっているあるアプリではDjangoにバックエンドで使っていますが、フロントエンドはページによってreactとvueを使い分けています。それぞれに長所があると思って両方使っているのですが、今回は**vue**の話をします。

**vue**はreactに比べて柔軟な使い方ができる印象です。Django(APIではなくテンプレート)が吐き出したデータを受け取って要素をつくるような書き方ができます。そしてその結果***感動するほどHTMLがすっきりするのです!!***

## DjangoとVueを一緒に使う

DjangoにVueを組み合わせる方法については私のを含め下記の記事が参考になるでしょう。

- [Django + Webpack を使ってVueを動かす](https://qiita.com/yonaka15/items/a81825aeb67c32232fb5)
- [Django REST framework with Vue.js](https://qiita.com/yuuki_1204_/items/d892aa0ba935a1ac7594)
- [Django + Vue.js な開発環境を作ろう](https://qiita.com/kawaMk4/items/89b18c608dc7dd2b946b)

## コード

例えばフォームを書く場合、**Djangoテンプレートの{% for %}構文**と**vueのコンポーネント**でHTMLをすっきりさせられます

まずvueでinputのコンポーネントをつくります。クラス名は[Bootstrap](https://getbootstrap.com/docs/4.0/components/forms/)を意識しています

```html:form-group.vue
<template>
  <div class="form-group">
    <label
       :for="id">
      {{ label }}
    </label>
    <input
       :type="type"
       class="form-control"
       :placeholder="label"
       :name="name"
       :id="id"
       :autofocus="!!autofocus"
       :required="!!required">
  </div>
</template>

<script>
export default {
  props: ['type', 'id', 'name', 'label', 'autofocus', 'required'],
}
</script>
```

エントリからコンポーネントを呼び出します。ここまでは普通のvueです

```js:index.js
import Vue from 'vue'
import FormGroup from './form-group.vue'
var vueApp = new Vue({
  el: '#vue-app',
  components: {
    FormGroup
  },
})
```

ここからがDjangoのテンプレートです。例えばログイン画面をつくるとしましょう。実際につくってみたい方は

- [Djangoで手短にログイン処理をつくる](https://qiita.com/yonaka15/items/045e578d1d2edae181ad)
- [Djangoでログイン処理を作る](https://qiita.com/maueki/items/d28fd2a170d42e745376)
- [Using the Django authentication system](https://docs.djangoproject.com/en/2.0/topics/auth/default/#all-authentication-views)

が参考になります。

Djangoのviewから渡されたformを`{% for field in form %}`でfieldごとに繰り返します。fieldはlabel要素やinput要素に必要なidやnameについての情報(field.id_for_nameやfield.html_name)を持っています。これらをpropとしてvueコンポーネントに渡しています。

```html:registration/login.html
{% load render_bundle from webpack_loader %}
<!doctype html>
<html lang="ja">
<meta charset="utf-8">
</head>
<body>

<div id="vue-app">
<form method="post" action="{% url 'accounts:login' %}">
 {% csrf_token %}
 {% for field in form %}
 <form-group
   type="{{ field.field.widget.input_type }}"
   id="{{ field.id_for_label }}"
   name="{{ field.html_name }}"
   label="{{ field.label }}"
   :autofocus="{% if forloop.first %}true{% else %}false{% endif %}"
   :required="{{ field.field.required|lower }}"
  ></form-group>
  {% endfor %}
  <button class="btn btn-primary" type="submit">{% trans 'Login' %}</button>
  <input type="hidden" name="next" value="{{ next }}" />
</form>
</div>

{% render_bundle 'main' %}
</body>
</html>
```

HTMLがすっきり書けると思いませんか！？

---

もうひとつ例をあげます。たとえば[BootstrapのNavbar](https://getbootstrap.com/docs/4.0/components/navbar/)のように階層の深くなる要素が、短い記述に置き換えられています。

次のコンポーネントを用意しています

```html:navbar.vue
<template>
  <nav class="navbar navbar-expand-lg navbar-light bg-light">
    <a class="navbar-brand" :href="logo_url">
      <img :src="logo" width="30" height="30" alt="">
    </a>
    <button v-if="showToggleButton" class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto">
        <li class="nav-item" v-for="item in items">
          <a class="nav-link" :href="item.url">{{ item.text }}</a>
        </li>
      </ul>
    </div>
  </nav>
</template>

<script>
export default {
  props: {
    logo: {
      type: String,
    },
    items: {
      type: Array,
      default: function() {
        return []
      }
    },
    logo_url: {
      type: String,
      default: '/'
    },
    showToggleButton: {
      type: Boolean,
      default: true,
    }
  }
}
</script>

<style lang="scss">
$white : #ffffff;
.bg-light {
  background-color: $white !important;
}
</style>
```

おかげでhtmlには下記だけでNavbarがつくれます！

```html
<Navbar
logo="{% static 'images/logo.png' %}"
:items="[
{% if not user %}{text:'{% trans "ログイン" %}', url:'{% url "accounts:login" %}?next={% url "todos:home" %}'},{% endif %}
{% if user %}{text:'{% trans "ログアウト" %}', url:'{% url "accounts:logout" %}?next={% url "home:home" %}'},{% endif %}
]"
></Navbar>
```

Djangoから得られる`{% if user %}`でユーザーがログインしているかに応じてボタンを変えるということまでやっています。