vue.jsのDjangoでの使い所
私がつくっているkoredeee.comというウェブアプリではDjangoにバックエンドで使っていますが、フロントエンドはページによってreactとvueを使い分けています。それぞれに長所があると思って両方使っているのですが、今回はvueの話をします。
vueはreactに比べて柔軟な使い方ができる印象です。Django(APIではなくテンプレート)が吐き出したデータを受け取って要素をつくるような書き方ができます。そしてその結果感動するほどHTMLがすっきりするのです!!
DjangoとVueを一緒に使う
DjangoにVueを組み合わせる方法については私のを含め下記の記事が参考になるでしょう。
コード
例えばフォームを書く場合、Djangoテンプレートの{% for %}構文とvueのコンポーネントでHTMLをすっきりさせられます
まずvueでinputのコンポーネントをつくります。クラス名はBootstrapを意識しています
<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です
import Vue from 'vue'
import FormGroup from './form-group.vue'
var vueApp = new Vue({
el: '#vue-app',
components: {
FormGroup
},
})
ここからがDjangoのテンプレートです。例えばログイン画面をつくるとしましょう。実際につくってみたい方は
が参考になります。
Djangoのviewから渡されたformを{% for field in form %}
でfieldごとに繰り返します。fieldはlabel要素やinput要素に必要なidやnameについての情報(field.id_for_nameやfield.html_name)を持っています。これらをpropとしてvueコンポーネントに渡しています。
{% 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がすっきり書けると思いませんか!?
もうひとつ例をあげます。たとえばkoredeee.comのソースをみてください。BootstrapのNavbarのように階層の深くなる要素が、短い記述に置き換えられています。
次のコンポーネントを用意しています
<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がつくれます!
<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 %}
でユーザーがログインしているかに応じてボタンを変えるということまでやっています。