Vue.jsのリストレンダリングを勉強する

# リストレンダリング

## v-forで配列に要素をマッピングする
配列に基づいて、アイテムのリストをレンダリングするために、**v-for**ディレクテイブを使用します。
**v-for**ディレクティブは**item in items**の形式で特別な構文を要求し、**items**は配列で、**item**は配列要素を順不動で復されているエイリアスです。

```html:html部分
<ul id="example">
  <li v-for="item in items">
    {{ item.message }}
  </li>
</ul>
```

```javascript:javascript部分
let app = new Vue({
  el: '#example',
  data: {
    items: [
      { message: 'item1' },
      { message: 'item2' }
    ]
  }
})
```

```:ブラウザ画面
・item1
・item2
```

上記の例ではitems配列の要素をブラウザ画面にレンダリングしています。

**v-for**ブロック内では、親スコープのプロパティへの完全なアクセスを持っています。
また**v-for**は現在のアイテムに対する配列のインデックスを、２つ目の引数としてサポートしてます。

```html:html部分
<ul id="example">
  <li v-for="(item, index) in items">
    [配列番号：{{ index }}] - [配列要素：{{ item.message }}]
  </li>
</ul>
```

```:ブラウザ画面
・[配列番号：0] - [配列要素：item1]
・[配列番号：1] - [配列要素：item2]
```

javascript部分は上記の例と同じです。
indexは配列の要素番号です。

また、区切り文字として**in**の他に**of**を使用することができます。
これはjavascriptの[イテレータ構文](https://qiita.com/kura07/items/cf168a7ea20e8c2554c6)に近いものです。

## オブジェクトのv-for
オブジェクトのプロパティに対して、**v-for**を使って反復処理する事ができます。

```html:html部分
<ul id="example">
  <li v-for="value in object">
    {{ value }}
  </li>
</ul>
```

```javascript:javascript部分
let app = new Vue({
  el: '#example',
  data: {
    object: {
      title: '公式リファレンスをトレスしながら勉強',
      author: 'Sthudent Camilo',
      publishedAt: '2019-07-09'
    }
  }
})
```

```:ブラウザ画面
・公式リファレンスをトレスしながら勉強
・Sthudent Camilo
・2019-05-21
```

オブジェクトでは２つ目の引数にプロパティ名（key）もサポートされています。

```html:html部分
<ul id="example">
  <li v-for="(value, name) in object">
    {{ name }} ： {{ value }}
  </li>
</ul>
```

```:ブラウザ画面
・title ： 公式リファレンスをトレスしながら勉強
・author ： Sthudent Camilo
・publishedAt ： 2019-07-09
```

javascriptは上記の例と同じ

オブジェクトはindexもサポートされています。

```html:html部分
<ul id="example">
  <li v-for="(value, name, index) in object">
    {{ index }} . {{ name }} ： {{ value }}
  </li>
</ul>
```

```:ブラウザ画面
0 . title ： 公式リファレンスをトレスしながら勉強
1 . author ： Sthudent Camilo
2 . publishedAt ： 2019-07-09
```

### <font color="red">【注意】</font>
オブジェクトを反復処理するとき、順序は**Object.keys()** の列挙順のキーに基いており、全てのjavascriptエンジンの実装で一貫性で保証されていません。

## 状態の維持
※正直この題目はピンときていので理解が深まり次第追記します。

Vue.jsが**v-for**で描画された要素のリストを更新する際、標準では”その場でパッチを適応する”方法が用いられます。
データのアイテムの順序が変更された場合、アイテムの順序に合わせてDOM要素を移動する代わりに、Vueは各要素にその場でパッチを適応して、その特定のインデックスに何を描画するべきかを確実に反映します。

```html:html部分
<ul id="example">
  <li v-for="value in object" v-bind:key="value.id">
    {{ value }}
  </li>
</ul>
```

繰り返されるDOMの内容が単純な場合や、性能向上のために標準の動作に意図的に頼る場合を除いて、可能なときはいつでも**v-for**に**key**属性を与えることがベストプラクティスです。

詳しく書かれている記事
[Vue.js: v-forで項目インデックスをkey属性にしていいのか](https://qiita.com/FumioNonaka/items/d1d9c9335116426a8316)

### <font color="red">【注意】</font>
オブジェクトや配列のような非[プリミティブ](https://developer.mozilla.org/ja/docs/Glossary/Primitive)値を**v-for**のキーとして使わないでください。代わりに、プリミティブ値である文字列や数値を使ってください。

## 配列の変化を検出
### 変更メソッド
dataプロパティで宣言した配列に変更を加える配列メソッドは以下の通りです。

```html:html部分
<ul id="example">
  <li v-for="item in items" v-bind:key="item.id">
    {{ item.message }}
  </li>
</ul>
```

```javascript:javascript部分
let app = new Vue({
  el: '#example',
  data: {
    items: [
      { message: 'item1' },
      { message: 'item2' }
    ]
  }
})
```

#### push()
配列の末尾に要素を追加します

```javascript:開発者モードのConsole
app.items.push({ message: 'item3' })
// 戻り値　push後のオブジェクトのlength
```

#### pop()
配列から最後の要素を取り除
配列の長さを変化させます

```javascript:開発者モードのConsole
app.items.pop()
// 戻り値　popで取り除いた要素
```

#### shift()
配列から最初の要素を取り除
配列の長さを変化させます

```javascript:開発者モードのConsole
app.items.shift()
// 戻り値　shiftで取り除いた要素
```

#### unshift()
配列の最初に１つ以上の要素を追加する

```javascript:開発者モードのConsole
app.items.unshift({ message: 'item3' },{ message: 'item4' })
// 戻り値　unshift後のオブジェクトのlength
```

#### sort()
配列の要素を in placeでソートします。

```javascript:開発者モードのConsole
app.items.sort()
```

#### reverse()
配列の要素を in placeで反転させます
最初の要素は最後に最後の要素は最初になります  

```javascript:開発者モードのConsole
app.items.reverse()
```

###配列の置き換え
変更メソッドは元の配列を変更します
しかし、変更を行わないメソッドもあります

#### filter()
引数として与えられたテスト関数を書く配列び対して実行し、それに合格した全ての配列要素からなる新しい配列を生成します。

```html:html部分
<ul id="example">
  <p>filter前</p>
  <li v-for="item in items" v-bind:key="item.id">
    {{ item.message }}
  </li>
  <p>filter後</p>
  <li v-for="item in passed" v-bind:key="item.id">
    {{ item.message }}
  </li>
</ul>
```

```javascript:javascript部分
let app = new Vue({
  el: '#example',
  data: {
    items: [
      { message: 'test' },
      { message: 'test' }
      { message: 'tess' }
    ],

    passed: []
  }
})
```

```:ブラウザ画面
filter前
・test
・test
・tess
filter後
```

```javascript:開発者モードのConsole
app.passed = app.items.filter(
  pass => pass.message == 'test'
)
```

```:ブラウザ画面
filter前
・test
・test
・tess
filter後
・test
・test
```

#### concat()
配列に他の配列や値を繋いでできた新しい配列を作る

```html:html部分
<ul id="example">
  <p>concat前</p>
  <li v-for="item in items" v-bind:key="item.id">
    {{ item.message }}
  </li>
  <p>concat後</p>
  <li v-for="item in items3" v-bind:key="item.id">
    {{ item.message }}
  </li>
</ul>
```

```javascript:javascript部分
let app = new Vue({
  el: '#example',
  data: {
    items: [
      { message: 'item1' },
      { message: 'item2' },
      { message: 'item3' }
    ],
    items2: [
      { message: 'item4' },
      { message: 'item5' },
      { message: 'item6' }
    ],
    items3: []
  }
})
```

```:ブラウザ画面
concat前
・item1
・item2
・item3
concat後

```

```javascript:開発者モードのConsole
app.items3 = app.items.concat(app.items2)
```

```:ブラウザ画面
concat前
・item1
・item2
・item3
concat後
・item1
・item2
・item3
・item4
・item5
・item6
```

#### slice()
指定された配列の一部をシャローコピーして、新しい配列オブジェクトを返します。
指定するさいに始めの配列番号と終わりの配列番号を指定したら、終わりの配列番号の要素は含まれません。

```html:html部分
<ul id="example">
  <p>slice前</p>
  <li v-for="(item, index) in items" v-bind:key="item.id">
    {{ index }} ： {{ item.message }}
  </li>
  <p>slice後</p>
  <li v-for="(item, index) in items2" v-bind:key="item.id">
    {{ index }} ： {{ item.message }}
  </li>
</ul>
```

```javascript:javascript部分
let app = new Vue({
  el: '#example',
  data: {
    items: [5
      { message: 'item1' },
      { message: 'item2' },
      { message: 'item3' },
      { message: 'item4' }
    ],
    items2: []
  }
})
```

```:ブラウザ画面
slice前
・0 ： item1
・1 ： item2
・2 ： item3
・3 ： item4
slice後

```

```javascript:開発者モードのConsole
app.items2 = app.items.slice(1,3)
```

```:ブラウザ画面
slice前
・0 ： item1
・1 ： item2
・2 ： item3
・3 ： item4
slice後
・0 ： item2
・1 ： item3
```

### 注意事項
javaScriptの制限のため、Vue.jsは配列下記２つの変更を検出することができません。  

+ インデックスで要素を直接設定するとき。
+ 配列の長さを変更するとき


```html:html部分
<ul id="example">
  <li v-for="item in items">
    {{ item }}
  </li>
</ul>
```

```javascript:javascript部分
let app = new Vue({
  el: '#example',
  data: {
    items: [
      'item1',
      'item2'
    ]
  }
})
```

下記の例はインデックスで要素を直接設定したアンチパターンです。

```javascript:開発者モードのConsole
app.items[1] = "item3"
```

自分で実行してみたところ、配列の中にあるオブジェクトのkeyを指定して変更する場合は問題なく実行されました。  

下記の例は配列の長さを変更するアンチパターンです。

```javascript:開発者モードのConsole
app.items.length = "item3"
```

上記の例に対する解決は存在します。
上記と同じ動作になりますが、リアクティブなシステム内で状況の更新を検出することができます。

#### インスタンスメソッド $set
インデックスで要素を直接設定する時にはインスタンスメソッド **$set**を使う事で解決できます。

```javascript:開発者モードのConsole
app.$set(app.items, 1, 'item3')
// app.$set(変更を加える配列, 配列番号, 変更したい値 )
```

#### vm.splice()
配列の長さを変更するときは変更メソッドの**splice**を使います。

```javascript:開発者モードのConsole
app.items.splice(app.items.length,0,'item3')
// app.items.splice(追加する配列番号,削除する要素の数,追加する要素)
```

## オブジェクトの変更検出の注意
javaScriptの制限のため、Vue.jsはプロパティの追加や削除を検出することはできません

```html:html部分
<div id="example">
  {{ message }}
  {{ message2 }}
</div>
```

```javascript:javascript部分
let app = new Vue({
  el: '#example',
  data: {
    message: 'オブジェクト'
  }
})
```

```javascript:開発者モードのConsole
app.message2 = 'オブジェクト２'
```

Vue.jsは既に作成されたインスタンスに新しいルートイベントのリアクティブプロパティを動的に追加することはできない。しかし、インスタンスメソッド **$set**を使いネストされたオブジェクトにリアクティブなプロパティを追加することは可能です。

```html:html部分
<ul id="example">
  <li v-for="value in object">
    {{ value }}
  </li>
</ul>
```

```javascript:javascript部分
let app = new Vue({
  el: '#example',
  data: {
    object: {
      title: '公式リファレンスをトレスしながら勉強',
      author: 'Sthudent Camilo',
      publishedAt: '2019-07-09'
    }
  }
})
```

```javascript:開発者モードのConsole
app.$set(app.object,'message' ,'こんにちわ')
// app.$set(変更を加えるオブジェクト, 追加するkey名, 追加する値)
```

### Object.assign()
すべての列挙可能なプロパティの値を、１つ以上のコピー元オブジェクトからコピー先オブジェクトにコピーするために使用されます。  

```html:html部分
<div id="example">
  <ul>
    <p>object1</p>
    <li v-for="value in object">
      {{ value }}
    </li>
    <p>object3</p>
    <li v-for="value in object3">
      {{ value }}
    </li>
  </ul>
</div>
```

```javascript:javascript部分
let app = new Vue({
  el: '#example',
  data: {
    object: {
      title: '公式リファレンスをトレスしながら勉強',
      author: 'Sthudent Camilo',
      publishedAt: '2019-07-09'
    },
    object2: {
      subtitle: 'リストレンダリング難しい…',
      author: '社畜　カミロ',
      publishedAt: '2019-07-11'
    },
    object3:{}
  }
})
```

```:ブラウザ画面
object1

公式リファレンスをトレスしながら勉強
Sthudent Camilo
2019-07-09

object3
```

```javascript:開発者モードのConsole
app.object3 = Object.assign(app.object, app.object2)
// app.object3 = Object.assign(コピー先オブジェクト, コピー元オブジェクト)
```

```:ブラウザ画面
object1

公式リファレンスをトレスしながら勉強
社畜　カミロ
2019-07-11
リストレンダリング難しい…

object3

公式リファレンスをトレスしながら勉強
社畜　カミロ
2019-07-11
リストレンダリング難しい…
```

コピー先オブジェクトのプロパティは、コピー元に同じ名前のプロパティがあると上書きされます。
コピー先オブジェクトにコピー元オブジェクトが上書きされます。

コピー先オブジェクトにコピー元オブジェクトが上書き対策として下記方法があります。
両方のオブジェクトのプロパティを使用して新しいオブジェクト作成する方法です。

```javascript:開発者モードのConsole
app.object3 = Object.assign({}, app.object, app.object2)
```

```:ブラウザ画面
object1

公式リファレンスをトレスしながら勉強
Sthudent Camilo
2019-07-09

object3

公式リファレンスをトレスしながら勉強
社畜　カミロ
2019-07-11
リストレンダリング難しい…
```

## フィルタ/ソートされた結果の表示

元のデータを実際に変更またはリセットすることなしに、フィルタリングやソートされたバージョンの配列を表示したいことがあります。
フィルタリングやソートされた配列を返す算出プロパティを作ることができます。

```html:html部分
<div id="example">
  <ul>
    <p>配列　フィルタ/ソートされた結果の表示</p>
    <li v-for="value in evenNumbers">
      {{ value }}
    </li>
  </ul>
</div>
```

```javascript:javascript部分
let app = new Vue({
  el: '#example',
  data: {
    numbers:[1,2,3,4,5]
  },
  computed: {
    evenNumbers: function () {
      return this.numbers.filter(function (number) {
        return number % 2 === 0
      })
    }
  }
})
```

算出プロパティが使えない状況ではメソッドを使う事ができる。

```html:html部分
<div id="example">
  <ul>
    <p>配列　フィルタ/ソートされた結果の表示</p>
    <li v-for="value in even(numbers)">
      {{ value }}
    </li>
  </ul>
</div>
```

```javascript:javascript部分
let app = new Vue({
  el: '#example',
  data: {
    numbers:[1,2,3,4,5]
  },
  methods:{
    even: function (numbers) {
      return numbers.filter(function (number) {
        return number % 2 === 0
      })
    }
  }
})
```

## 範囲付き v-for
**v-for**は整数値を取ることもできる。

```html:htm部分
<div id="example">
  <ul>
    <li v-for="value in 10">
      item{{ value }}
    </li>
  </ul>
</div>
```

上記の例では、指定された数だけ< li >要素が繰り返されます。

## コンテンツテンプレートでの**v-for**
**v-if**同様に、複数の要素ブロックをレンダリングするために、**v-for**で < template >タグを使うこともできます。

```html:html部分
<ul id="example">
  <template v-for="value in object">
    <li>{{ value }}</li>
  </template>
</ul>
```

```javascript:javascript部分
let app = new Vue({
  el: '#example',
  data: {
    object: {
      title: '公式リファレンスをトレスしながら勉強',
      author: 'Sthudent Camilo',
      publishedAt: '2019-07-09'
    }
  }
})
```

## あとがき
v-ifとv-forの題目はv-ifとv-forを同時に利用する事は推奨されておらずバッドプラクティスですということです。

## 参考資料
javascriptのループについての記事ですが、javascriptのイテレータ構文に近いのでVue.jsの**v-for**バインディングにも役に立ちます  
[JavaScriptで配列やオブジェクトをループする良い方法を真剣に検討してみた](https://qiita.com/endam/items/808a084859e3a101ab8f)