Djangoroidの奮闘記

python,django,angularJS1~三十路過ぎたプログラマーの奮闘記

Django の {{ form }} をカスタマイズして使う

参考サイト

https://docs.djangoproject.com/en/1.11/topics/forms/#rendering-fields-manually https://docs.djangoproject.com/en/1.11/topics/forms/#looping-over-the-form-s-fields

手動でformをレンダリングしてカスタマイズする。

  • {{ form }}でレンダリングすると、すべての項目が垂直方向に個別に表示されてしまうので、部分的に横に並べてフォームの項目を作りたいときに役に立ちそうなテクニック。

https://docs.djangoproject.com/en/1.11/topics/forms/#rendering-fields-manually

{{ form.non_field_errors }}
<div class="fieldWrapper">
    {{ form.subject.errors }}
    <label for="{{ form.subject.id_for_label }}">Email subject:</label>
    {{ form.subject }}
</div>
<div class="fieldWrapper">
    {{ form.message.errors }}
    <label for="{{ form.message.id_for_label }}">Your message:</label>
    {{ form.message }}
</div>
<div class="fieldWrapper">
    {{ form.sender.errors }}
    <label for="{{ form.sender.id_for_label }}">Your email address:</label>
    {{ form.sender }}
</div>
<div class="fieldWrapper">
    {{ form.cc_myself.errors }}
    <label for="{{ form.cc_myself.id_for_label }}">CC yourself?</label>
    {{ form.cc_myself }}
</div>

for文でループして表示することも可能。

https://docs.djangoproject.com/en/1.11/topics/forms/#looping-over-the-form-s-fields

{% for field in form %}
    <div class="fieldWrapper">
        {{ field.errors }}
        {{ field.label_tag }} {{ field }}
        {% if field.help_text %}
        <p class="help">{{ field.help_text|safe }}</p>
        {% endif %}
    </div>
{% endfor %}

Bootstrap + Django で、themeをMaterial ぽくする。

大まかな流れ

  • ベースになるbase.htmlページを作成する。

    • base.htmlを作成する
    • urls.pyを設定して、テストしてみる。
    • css.htmlを作成する
    • javascript.htmlを作成する
    • base.htmlに読み込ませる
  • bootstrapのthemeを変える

    • チューニングされている bootstrapファイルをダウンロードする

ベースになるbase.htmlページを作成する。

base.htmlを作成する

  • base.htmlに、bootstrapのページから適当にexamplesのソースコードをコピペして、そのまま貼り付ける。

  • bootstrapのdistのzipファイルをダウンロードする。

  • zipファイルの中身を、djangoのstaticフォルダ内に配置する。ファイル種別によって、フォルダ別にまとめてあるので、そのままstaticフォルダに配置する。

  • zipファイルの中に入っていなくて、配置が必要なcssファイルなどは、bootstrapのソースコードから直接ダウンロードしてくる。

    • (例) <link href="navbar-fixed-top.css" rel="stylesheet">cssファイルは、bootstrapのexampleページのソースコードからアクセスできるので、それを直接ダウンロードする。
  • 必要なファイルをstaticファイルに配置したら、{% static "" %}で、静的ファイルを読み込むように設定する。

{% load staticfiles %}

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
    <meta name="description" content="">
    <meta name="author" content="">
    <link rel="icon" href='{% static "img/favicon.ico" %}'>

    <title>TurnPike</title>

    <!-- Bootstrap core CSS -->
    <link href='{% static "css/bootstrap.min.css" %}' rel="stylesheet">

    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
    <link href='{% static "css/ie10-viewport-bug-workaround.css" %}' rel="stylesheet">

    <!-- Custom styles for this template -->
    <link href='{% static "css/navbar-fixed-top.css" %}' rel="stylesheet">
  </head>

  <body>

    <!-- Fixed navbar -->
    <nav class="navbar navbar-default navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
            <span class="sr-only">Toggle navigation</span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
            <span class="icon-bar"></span>
          </button>
          <a class="navbar-brand" href="#">Project name</a>
        </div>
        <div id="navbar" class="navbar-collapse collapse">
          <ul class="nav navbar-nav">
            <li class="active"><a href="#">Home</a></li>
            <li><a href="#about">About</a></li>
            <li><a href="#contact">Contact</a></li>
            <li class="dropdown">
              <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Dropdown <span class="caret"></span></a>
              <ul class="dropdown-menu">
                <li><a href="#">Action</a></li>
                <li><a href="#">Another action</a></li>
                <li><a href="#">Something else here</a></li>
                <li role="separator" class="divider"></li>
                <li class="dropdown-header">Nav header</li>
                <li><a href="#">Separated link</a></li>
                <li><a href="#">One more separated link</a></li>
              </ul>
            </li>
          </ul>
          <ul class="nav navbar-nav navbar-right">
            <li><a href="../navbar/">Default</a></li>
            <li><a href="../navbar-static-top/">Static top</a></li>
            <li class="active"><a href="./">Fixed top <span class="sr-only">(current)</span></a></li>
          </ul>
        </div><!--/.nav-collapse -->
      </div>
    </nav>

    <div class="container">

      <!-- Main component for a primary marketing message or call to action -->
      <div class="jumbotron">
        <h1>Navbar example</h1>
        <p>This example is a quick exercise to illustrate how the default, static and fixed to top navbar work. It includes the responsive CSS and HTML, so it also adapts to your viewport and device.</p>
        <p>To see the difference between static and fixed top navbars, just scroll.</p>
        <p>
          <a class="btn btn-lg btn-primary" href="../../components/#navbar" role="button">View navbar docs &raquo;</a>
        </p>
      </div>

    </div> <!-- /container -->


    <!-- Bootstrap core JavaScript
    ================================================== -->
    <!-- Placed at the end of the document so the pages load faster -->
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery.min.js"><\/script>')</script>
    <script src='{% static "js/bootstrap.min.js" %}'></script>
    <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
    <script src='{% static "js/ie10-viewport-bug-workaround.js" %}'></script>
  </body>
</html>
  • urls.pyで、適当にTemplateViewなどで、上記で作成したbase.htmlをレンダリングして表示させてみる。
from django.conf.urls import url, include
from django.contrib import admin
from django.views.generic.base import TemplateView

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^.*', TemplateView.as_view(template_name="base.html"), name='home'),
]
  • 表示されたらとりあえず第一段階完了(^^)

css.htmlを作成する。

{% load staticfiles %}
<!-- Bootstrap core CSS -->
<link href='{% static "css/bootstrap.min.css" %}' rel="stylesheet">

<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<link href='{% static "css/ie10-viewport-bug-workaround.css" %}' rel="stylesheet">

<!-- Custom styles for this template -->
<link href='{% static "css/navbar-fixed-top.css" %}' rel="stylesheet">

javascript.htmlを作成する。

{% load staticfiles %}
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="../../assets/js/vendor/jquery.min.js"><\/script>')</script>
<script src='{% static "js/bootstrap.min.js" %}'></script>
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<script src='{% static "js/ie10-viewport-bug-workaround.js" %}'></script>

base.htmlに、css.html, javascript.htmlをincludeする。

<head>
  {% include 'css.html' %}
</head>
<body>
  {% include 'javascript.html'%}
</body>

bootstrapのthemeを変える

チューニング済みの、materialUIぽい、bootstrap.min.jsをダウンロードしてくる。

  • 今回は以下のサイトから、paperというthemeをダウンロード! http://bootswatch.com/

  • ダウンロードするときに、paper_bootstrap.min.css とか今回は本家のbootstrapとちょっとファイル名を変えて保存してみた。失敗したときにすぐ戻せるように。

  • css.htmlの、bootstrap.min.cssの部分を、paper_bootstrap.min.cssに変更して、アクセスしてみる。。。。お〜かっこよくなった!(^^)

Vue.js 入門 vol.6 コンポーネント 後半

参考サイト

https://jp.vuejs.org/v2/guide/components.html#動的コンポーネント

動的コンポーネント

  • コンポーネントの切り替え: componentタグと、isによるcomponentの指定で実装可能。
var vm = new Vue({
  el: '#example',
  data: {
    currentView: 'home'
  },
  components: {
    home: {},
    posts: {},
    archive: {}
  }
})
<component v-bind:is="currentView">
  <!-- vm.currentview が変更されると、中身が変更されます! -->
</component>
var Home = {
  template: '<p>Welcome home!</p>'
}

var vm = new Vue({
  el: '#example',
  data: {
    currentView: Home
  }
})
  • keep-alive: 切り替えられたコンポーネントの状態保持のためには、keep-aliveタグを使う。
<keep-alive>
  <component :is="currentView">
      <!-- 非活性になったコンポーネントをキャッシュします! -->
  </component>
</keep-alive>

その他

再利用可能なコンポーネントの作成

  • Vue ComponentのAPIは以下の3つ

  • v-bind, v-on の省略記法を使うと見やすい。

<my-component
  :foo="baz"
  :bar="qux"
  @event-a="doThis"
  @event-b="doThat"
>
<img slot="icon" src="...">
<p slot="main-text">Hello!</p>
</my-component>

コンポーネントの参照

<div id="parent">
  <user-profile ref="profile"></user-profile>
</div>
// 親のコンストラクタ関数を定義
var parent = new Vue({ el: '#parent' })
// 親のコンテンツで、refでidを振った子コンポーネントのインスタンスへのアクセスß
var child = parent.$refs.profile

非同期コンポーネント

  • このあたりは、普通のjavascriptの解説を見る必要があるな。さっぱりわからん。ファクトリ関数

https://jp.vuejs.org/v2/guide/components.html#非同期コンポーネント

Vue.component('async-example', function(resolve, reject) {
  // コンポーネント定義を resolve コールバックで渡す
  setTimeout(function(){
    resolve({
      template: '<div>I am async!</div>'
    })
  }, 1000)
})

まとめ

  • 基本的な文法は、javascriptに近いので、javascriptをちゃんと理解している必要はあるな。
  • あとは、作りながら勉強していく感じかな〜。

Vue.js 入門 vol.6 コンポーネント 前半

参考サイト

https://jp.vuejs.org/v2/guide/components.html

コンポーネントの使用

  • 登録
new Vue({
  el: '#some-element',
})

Vue.component('my-component',{
  //オプション
})
  • 一度登録すると、カスタム要素<my-component>を使えるようになる。親のinstanceのtemplate内で使えるようになる。
<div id="example">
  <my-component></my-component>
</div>
// 登録する
Vue.component('my-component',{
  template: '<div>A custom component!</div>'
})

// rootインスタンスを作成する

new Vue({
  el: '#example'
})
<!-- 上記の結果描画されるhtmlは以下の通り -->
<div id="example">
  <div>A custom component!</div>
</div>

ローカル登録

var Child = {
  template: '<div>A custom component!</div>'
}

new Vue({
  ...
  components: {
    'my-component': Child
  }
})
  • 今のところ、グローバル登録との違いがわからない。。。

DOM テンプレート解析の注意事項

  • 親・子 elementの関係で、うまく custom elementが読み込まれない時がある。例えば、table - trの関係とか。その場合は、isを使う
<!-- これだとうまく読み込まれない -->
<table>
  <my-row>...</my-row>
</table>

<!-- こっちが推奨される -->
<table>
  <tr is="my-row"></tr>
</table>

data は関数でなければならない

  • Vueコンストラクタ(new Vue({}))に渡すことのできるほとんどのオプションは、コンポーネントの中で利用できる。ただし、dataだけは関数でなければならない。
<div id="example-2">
  <simple-counter></simple-counter>
  <simple-counter></simple-counter>
  <simple-counter></simple-counter>
</div>
var data = { counter: 0 }
Vue.component('simple-counter', {
  template: '<button v-on:click="counter += 1">{{ counter }}</button>',
  // data は技術的には関数なので、Vue は警告を出しません。
  // しかし、各コンポーネントのインスタンスは
  // 同じオブジェクトの参照を返します。
  data: function () {
    return {
    counter: 0
    }
  }
})
new Vue({
  el: '#example-2'
})

コンポーネントの構成

  • props down, events upがキーワード!親は、 プロパティを経由して、データを子に伝え、子はイベントを経由して、親にメッセージを送ります。

プロパティ

  • プロパティによるデータの伝達: propsオプションを利用して、伝達のための変数を明示的に宣言しておく!
Vue.component('child',{
  props:['message'], // この場合は、messageという変数を通じて、親からデータを受け取る。
  template: '<span>{{ message }}</span>'
})
  • 上記のように設定しておくと、以下のように、親のcomponent内で、messageという変数を通じて、valueを受け取ることができる。
<child message="hello!"></child>

キャメルケース vs ケバブケース

  • htmlの属性は、大文字と小文字を区別しない。そのため、キャメルケース(大文字含む文字列)のプロパティは、ケバブケース (kebab-case: ハイフンで句切られた) に変更する必要があり。
Vue.component('child', {
  // JavaScript ではキャメルケース
  props: ['myMessage'],
  template: '<span>{{ myMessage }}</span>'
})
<!-- HTML ではケバブケース に変換する必要あり-->
<child my-message="hello!"></child>

動的なプロパティ

<div>
  <!--  v-modelで、親componentのparentMsgに、inputのvalueを渡す-->
  <input v-model="parentMsg">
  <br>
  <!-- parentMsgのvalueをv-bindを通じて、親componentの、my-message(myMessage) propsに渡す -->
  <!--  myMessage(my-message)を通じて 子componentにparentMsgのvalueが渡される-->
  <child v-bind:my-message="parentMsg"></child>
</div>

リテラル vs 動的

  • 文字列と、数字を間違えないように注意。実際に JavaScript の数を渡したい場合は、その値が JavaScript の式として評価されるよう、v-bind を使う必要があります
<!-- これは純粋な文字列"1"を渡します -->
<comp some-prop="1"></comp>

<!-- これは実際の数を渡します -->
<comp v-bind:some-prop="1"></comp>

単方向データフロー

  • すべてのプロパティは、親->子のみ。逆は無し!

  • ただし、プロパティは初期値を渡すためにのみ使われ、子コンポーネントは単にその値をローカルデータプロパティとして使用したい場合と、プロパティは変換が必要な生の値として渡されます。の場合は、プロパティを変更したくなる。(よくわからん)

// プロパティの初期値をその初期値とするようなローカルデータプロパティを定義します。
props: ['initialCounter'],
data: function () {
  return { counter: this.initialCounter }
}
// プロパティの値から計算される算出プロパティ (computed property) を定義します。

props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

プロパティ検証

  • 以下のようにvalidationできる。
Vue.component('example', {
  props: {
    // 基本な型チェック (`null` はどんな型でも受け付ける)
    propA: Number,
    // 複数の受け入れ可能な型
    propB: [String, Number],
    // 必須な文字列
    propC: {
      type: String,
      required: true
    },
    // デフォルト値
    propD: {
      type: Number,
      default: 100
    },
    // オブジェクトと配列のデフォルトはファクトリ関数から返すようにしています
    propE: {
      type: Object,
      default: function () {
        return { message: 'hello' }
      }
    },
    // カスタムバリデータ関数
    propF: {
      validator: function (value) {
        return value > 10
      }
    }
  }
})
  • typeの種類は以下の通り。
String
Number
Boolean
Function
Object
Array
Symbol
  • type はカスタムコンストラクタ関数とすることもでき、アサーションは instanceof チェックで作成できるでしょう。らしいが、意味はよくわかってない。たぶん、コンストラクタ関数の型(?)と一致しているかどうかをチェックできるということだろう。

カスタムイベント

  • カスタムイベントとの v-on の使用: イベントが起こったときに、子→親に通信する方法。以下の3つを利用できる。
    • $on(eventName)を使用してイベントを購読します。
    • $emit(eventName)を使用して自身にイベントをトリガーします。
    • コンポーネントは、子コンポーネントが使われているテンプレート内で直接 v-on を使用することで、子コンポーネントからのイベントを購読することができ
<div id="counter-event-example">
  <p>{{ total }}</p>
  <!-- incrementが実行されたときに、incrementTotalを実行する -->
  <!-- いつincrementが実行されるかというと、それは、button-counter component内にある -->
  <!-- v-on:click="increment"なので、buttonがclickされたときに、incrementが起動する -->
  <!-- incrementが起動すると、$emit('increment')により、親方向に、incrementのevent通知が伝えられる。 -->
  <button-counter v-on:increment="incrementTotal"></button-counter>
  <button-counter v-on:increment="incrementTotal"></button-counter>
</div>
Vue.component('button-counter', {
  template: '<button v-on:click="increment">{{ counter }}</button>',
  data: function () {
    return {
      counter: 0
    }
  },
  methods: {
    increment: function () {
      this.counter += 1
      this.$emit('increment') //$emit('')で、自分を含む親方向へのイベント通知
    }
  },
})
new Vue({
  el: '#counter-event-example',
  data: {
    total: 0
  },
  methods: {
    incrementTotal: function () {
      this.total += 1
    }
  }
})
  • 上記のコードでは、子->親へイベント通知をしているだけで、データの受け渡しなどはされていない。

  • ネイティブイベントとコンポーネントバインディング: コンポーネントの root 要素でのネイティブイベントを購読したいときがあるかもしれません。このような場合、v-on に .native 修飾子を使用することができます。…よくわからん。とにかく例は以下の通りらしい。

<my-component v-on:click.native="doTheThing"></my-component>

.sync 修飾子

  • .syncは双方向データバインディングができるんやな。結構中核になりそうな機能の1つやな。
<comp :foo.sync="bar"></comp>
<!-- ↑は、下のコードに展開される -->
<comp :foo='bar' @update:foo="val => bar = val"></comp>
<!-- 省略記法をなくすと以下の通り -->
<comp v-bind:foo='bar' v-on:update:foo="val => bar = val"></comp>

<!-- v-onで、まずupdateを検知する -->
<!-- それをきっかけに、fooのvalを、barに代入する -->
<!-- v-bindで、barのvalueを、親componentのfooに代入する -->
  • お〜〜〜、これはめっちゃ便利そうだし、使い所多そうだな。

  • 上記のコードで、子コンポーネントが、fooの値を更新するために、プロパティの変更の代わりに、明示的にイベントを発信する必要がある。

this.$emit('update:foo', newValue)
// fooがupdateされました!という情報を親コンポーネントに伝える!

カスタムイベントを使用したフォーム入力コンポーネント

  • カスタムイベントは、v-modelとともに、動くカスタムフォーム入力を作成されるためにも利用される。
<input v-model="something">

<!-- 上記のコードは以下のように展開されている -->
<input
  v-bind:value="something"
  v-on:input="something = $event.target.value">
<!-- inputがされたときに、something に、envetのvalueが代入される -->
<!--  somethingが、valueに代入される -->
<custom-input
  :value="something"
  @input="value => { something = value }"
>
</custom-input>
  • つまり、v-modelと一緒に、componentを動かすには以下が必要。

    • value プロパティを受け入れる
    • 新しい値とともに、inputイベントを発信する
  • 通過入力を例は以下の通り。

<currency-input v-model="price"></currency-input>
Vue.component('currency-input',{
  template: `
    <span>
      $
      <input
        ref="input"
        v-bind:value="value"
        v-on:input="updateValue($event.target.value)">
    </span>
  `,
  props: ['value'],
  methods: {
    // 値を直接的に更新する代わりに、このメソッドを使用して
    // inputの値の整形と値に対する制約が行われr。
    updateValue: function(value){
      var formattedValue = value
        //両端のスペースを削除
        .trim()
        // 小数点2桁以下まで短縮
        .slice(
          0,
          value.indexOf('.') === -1
          ? value.length
          : value.indexOf('.') + 3
        )
        // 値がすでに正規化されていないならば
        // 手動で適合するように上書き
        if (formattedValue !== value ){
          this.$refs.input.value = formattedValue
        }
        // input イベントを通して数値を発行する
        this.$emit('input', Number(formattedValue))
    }
  }
  })

コンポーネントの v-model のカスタマイズ

  • v-modelをカスタマイズする。これはdjangoのget_contextなどを上書きするのに似てるな。

  • デフォルトだと、v-modelは、valueをプロパティとして、inputをイベントとして利用する。ただ、チェックボックスラジオボタンなどの入力タイプを利用する場合は、valueプロパティをその他の目的で利用することができる。

Vue.component('my-checkbox',{
  model: {
    prop: 'checked',
    event: 'change'
  },
  props: {
    // これによって、valueプロパティを別の目的で利用することを許可する
    value: String
  }
})
<my-checkbox v-model="foo" value="some value"></my-checkbox>

<!-- 上記は以下と同じ -->
<my-checkbox
  :checked="foo"
  @change="val => { foo = val }"
  value="some value">
</my-checkbox>

親子間以外の通信

スロットによるコンテンツ配信

  • 複数のコンポーネントを使用するときの注意事項。例えば以下のような例。appは独自のtemplateをもっており、app-headerなどもそれぞれ独自のtemplateをもっている。そのため、appがどのコンテンツにどのように作用するかがわかりづらい構造になっている。
<app>
  <app-header></app-header>
  <app-footer></app-footer>
</app>
  • コンポーネントのコンテンツと、コンポーネント自身のテンプレートをうまく織り交ぜる方法が必要。これは、コンテンツ配信と呼ばれるプロセスらしい。

  • Vue.jsでは、要素を利用して、配信APIを実装するらしい。

コンパイルスコープ

<child-component>
  {{ message }}
</child-component>
  • 親テンプレート内の全てのものは親のスコープでコンパイルされ、子テンプレート内の全てものは子のスコープでコンパイルされる が鉄則。
<!--someChildProperty が子コンポーネントのプロパティとすると動作しません -->
<!-- なぜなら, このコンテンツは、親コンポーネントに束縛されているから -->
<child-component v-show="someChildProperty"></child-component>
  • 上記の例をchild-componentで機能させたい場合は、以下のように、子component内のtemplateに記載する。
Vue.componet('child-component',{
  template: `<div v-show="someChildProperty>Chile</div>"`,
  data: function(){
    return {
      someChildProperty:true
    }
  }
})
  • 同様に、配信コンテンツは、親スコープでコンパイルされる。配信コンテンツというのは、レンダリングされた後の、templateのことかな。

単一スロット

  • my-component というcomponetのtemplate
<!-- my-component というcomponetのtemplate -->
<div>
  <h2>I'm the child title</h2>
  <slot>
    This will only be displayed if there is no content
    to be distributed.
  </slot>
</div>
<div>
  <h1>I'm the parent title</h1>
  <my-component>
    <p>This is some original content</p>
    <p>This is some more original content</p>
  </my-component>
</div>
  • 描画された結果は以下の通り。
<div>
  <h1>I'm the parent title</h1>
  <div>
    <!-- slot タグの部分は削除されてる。 -->
    <h2>I'm the child title</h2>
    <p>This is some original content</p>
    <p>This is some more original content</p>
  </div>
</div>
  • 結論的には、子コンポーネントのelement内に、何も値が無い場合は、は破棄されない。何かある場合は、slotは破棄されて表示されない。

名前付きスロット

  • 要素は特別な属性 name を持ち、コンテンツを配信する方法をカスタマイズするために使用できます。

  • app-layout 以下のようなtemplateをもつコンポーネントがあると仮定する。

<!-- app-layoutのcomponetのtemplate -->
<div class="container">
  <header>
    <slot name="header"></slot>
  </header>
  <main>
    <slot></slot>
  </main>
  <footer>
    <slot name="footer"></slot>
  </footer>
</div>
<app-layout>
  <h1 slot="header">Here might be a page title</h1>
  <p>A paragraph for the main content.</p>
  <p>And another one.</p>
  <p slot="footer">Here's some contact info</p>
</app-layout>
  • 描画された結果は以下の通り。
<div class="container">
  <header>
    <h1>Here might be a page title</h1>
  </header>
  <main>
    <p>A paragraph for the main content.</p>
    <p>And another one.</p>
  </main>
  <footer>
    <p>Here's some contact info</p>
  </footer>
</div>
  • slotに名前をつけると属性ごとに、slotの活用方法をカスタマイズできる。

スコープ付きスロット

<div class="child">
  <slot text="hello from child"></slot>
</div>
  • 親のtemplate。template tag内の、scopeを使って、propsに、子コンポーネントのslot内で設定したtextを渡す。
<div class="parent">
  <child>
    <template scope="props">
      <span>hello from parent</span>
      <span>{{ props.text }}</span>
    </template>
  </child>
</div>
  • 出力は以下の通り。
<div class="parent">
  <div class="child">
    <span>hello from parent</span>
    <span>hello from child</span>
  </div>
</div>
<!-- リストコンポーネントの子のテンプレート -->
<ul>
  <slot name="item"
    v-for="item in items"
    :text="item.text">
    <!-- フォールバックコンテンツはここへ -->
  </slot>
</ul>
  • 親のtemplateは以下の通り。
<!-- itemsをitemsに -->
<my-awesome-list :items="items">
  <!-- scoped slot can be named too -->
  <template slot="item" scope="props">
    <li class="my-fancy-item">{{ props.text }}</li>
  </template>
</my-awesome-list>
  • フォールバックコンテンツってのは要するに、親コンポーネントに何も要素がなかったときに、表示されるコンテンツのことらしい。

Vue.js 入門 vol.5 フォーム入力バインディング

参考サイト

https://jp.vuejs.org/v2/guide/forms.html

フォーム入力バインディング

  • formのinput要素と、textarea要素で、two-way binding を作成するには、v-modelを使うことができる。(初期値は無視する。)

  • テキストの例

<input v-model="message" placeholder="edit me">
<p>Message is : {{ message }}</p>
  • 複数行テキスト
<span>Multilien message is:</span>
<p style="white-space: pre">{{ message }}</p>
<br>
<textarea v-model="message" placeholder="add mutiple lines"></textarea>
<input type="checkbox" id="checkbox" v-model="checked">
<label for="checkbox">{{ checked }}</label>
<input type="checkbox" id="jack" value="Jack" v-model="checkedNames">
<label for="jack">Jack</label>
<input type="checkbox" id="john" value="John" v-model="checkedNames">
<label for="john">John</label>
<input type="checkbox" id="mike" value="Mike" v-model="checkedNames">
<label for="mike">Mike</label>
<br>
<span>Checked names: {{ checkedNames }}</span>
new Vue({
  el: '...',
  data: {
    checkedNames:[]
  }
})
<input type="radio" id="one" value="One" v-model="picked">
<label for="one">One</label>
<br>
<input type="radio" id="two" value="Two" v-model="picked">
<label for="two">Two</label>
<br>
<span>Picked: {{ picked }}</span>
  • 選択
<select v-model="selected">
  <option disabled value="">Please select one</option>
  <option>A</option>
  <option>B</option>
  <option>C</option>
</select>
<span>Selected: {{ selected }}</span>
new Vue({
  el: '...',
  data: {
    selected: ''
  }
})
  • 複数の選択(配列に束縛)
<select v-model="selected" multiple>
  <option>A</option>
  <option>B</option>
  <option>C</option>  
</select>
<br>
<span>Selected: {{ selected }}</span>
  • 動的オプションは、v-forで描画できる。
<select v-model="selected">
  <option v-for="option in options" v-bind:value="option.value">
    {{ option.text }}
  </option>
</select>
<span>Selected: {{ selected }}</span>
new Vue({
  el: '...',
  data: {
    selected: 'A',
    options: [
      { text: 'One', value: 'A' },
      { text: 'Two', value: 'B' },
      { text: 'Three', value: 'C'}
    ]
  }
})

値のバインディング

  • radio, checkbox, selectは、valueを返す。
<!-- チェックされたとき、`picked` は文字列"a"になります -->
<input type="radio" v-model="picked" value="a">
<!-- `toggle` は true かまたは false のどちらかです -->
<input type="checkbox" v-model="toggle">
<!-- 選択されたとき、`selected` は文字列"abc"です -->
<select v-model="selected">
  <option value="abc">ABC</option>
</select>
  • チェックボックスで、データを動的に変更したいときは、v-bindを使う。(true-value, false-valueがそれぞれのチェックがついたときに返すvalueの値。文字列a/bを代入している。)
<input
  type="checkbox"
  v-model="toggle"
  v-bind:true-value="a"
  v-bind:false-value="b"
>
// チェックされたとき:
vm.toggle === vm.a
// チェックがはずれされたとき:
vm.toggle === vm.b
<input type="radio" v-model="pick" v-bind:value="a">
vm.pick === vm.a
  • 選択オプション
<select v-model="selected">
  <option v-bind:value="{ number: 123}">123</option>
</select>
//選択したとき
typeof vm.selected // -> 'object'({number:123 }
vm.selected.number // -> 123

修飾子

  • .lazy (inputイベント時ではなく、changeイベント後に実行される(同期される))
<input v-model.lazy="msg">
  • .number (数値として型変換したいときに使う): これは結構重要で、基本的に、input tagに、type=numberと書いていても、文字列を返すから。
<input v-model.number="age" type="number">
  • .trim 自動的にトリムする時に使える。
<input v-model.trim="msg">

まとめ

  • v-modelは、input, textareaで基本的に使う。

Vue.js 入門 vol.4 イベントハンドラ

参考サイト

https://jp.vuejs.org/v2/guide/events.html

イベントの購読(subscribe)

  • v-on ディレクティブ
<div id="example-1">
  <button v-on:click="counter += 1">Add 1</button>
  <p>The button above has been clicked {{ counter }} times.</p>
</div>
var example1 = new Vue({
  el: '#example-1',
  data: {
    counter: 0
  }
})

メソッドイベントハンドラ

  • v-onには、メソッド名の指定も可能。
<div id="example-2">
  <!-- `greet` は、あらかじめ定義したメソッドの名前 -->
  <button v-on:click="greet">Greet</button>
</div>
var example2 = new Vue({
  el: '#example-2',
  data: {
    name: 'Vue.js'
  },
  methods: {
    greet: function(event){
      alert('Hello ' + this.name + '!')
      if (event){ // eventは、ネイティブDOMイベント
        alert(event.target.tagName)
      }
    }
  }
})

example2.greet()

インラインメソッドハンドラ

  • inline javascript方式で、メソッドを指定可能。
<div id="example-3">
  <button v-on:click="say('hi')">Say hi</button>
  <button v-on:click="say('what')">Say what</button>
</div>
new Vue({
  el: '#example-3',
  methods: {
    say: function (message) {
      alert(message)
    }
  }
})
  • オリジナルのDOMイベントを参照するときは、$eventを使うことで、参照できる。
<button v-on:click="warn('Form cannot be submitted yet.', $event)">
  Submit
</button>
methods: {
  warn: function (message, event) {
    // ネイティブイベントを参照しています
    if (event) event.preventDefault()
    alert(message)
  }
}

イベント修飾子

  • イベント修飾子の種類は以下の通り。
<!-- クリックイベントの伝搬が止まります -->
<a v-on:click.stop="doThis"></a>
<!-- submit イベントによってページがリロードされません -->
<form v-on:submit.prevent="onSubmit"></form>
<!-- 修飾子は繋げることができます -->
<a v-on:click.stop.prevent="doThat"></a>
<!-- 値を指定せず、修飾子だけ利用することもできます -->
<form v-on:submit.prevent></form>
<!-- イベントリスナーを追加するときにキャプチャモードで使います -->
<!-- 言い換えれば、内部要素を対象とするイベントは、その要素によって処理される前にここで処理されます -->
<div v-on:click.capture="doThis">...</div>
<!-- event.target が要素自身のときだけ、ハンドラが呼び出されます -->
<!-- 言い換えると子要素のときは呼び出されません -->
<div v-on:click.self="doThat">...</div>

<!-- 最大1回、クリックイベントはトリガーされます -->
<a v-on:click.once="doThis"></a>

キー修飾子

  • キーボードイベントのsubscribe
<!-- keyCode が13のときだけ、vm.submit() が呼ばれます  -->
<input v-on:keyup.13="submit">
<!-- 上記と同じです -->
<input v-on:keyup.enter="submit">
<!-- 省略記法も同様に動作します -->
<input @keyup.enter="submit">

.enter
.tab
.delete (“Delete” と “Backspace” キー両方をキャプチャします)
.esc
.space
.up
.down
.left
.right
  • カスタムキーも可能。
// v-on:keyup.f1 を可能にします
Vue.config.keyCodes.f1 = 112

修飾子キー

  • キーボードの修飾子
.ctrl
.alt
.shift
.meta

<!-- Alt + C -->
<input @keyup.alt.67="clear">
<!-- Ctrl + Click -->
<div @click.ctrl="doSomething">Do something</div>
  • マウスボタンの修飾子
.left
.right
.middle

まとめ

  • v-onでclick, キーボード操作、マウス操作などのeventをかなりカバーできるぞ。