【Pug】よく利用するループ処理

同じような記述を繰り返す一覧のコーディングってめんどくさいですよね。
たくさんコーディングした後に HTML の構造を変える必要が出てくるとイラッとします。

Pug のループ処理を利用すると、こんなイライラともおさらばです。
また、HTML タグを1回記述するだけで済むので、HTML の構文エラーも少なくなります。

というわけで、よく利用するループ処理をまとめてみました！

## 基本編

### for

指定回数をループさせるには JavaScript の `for` を利用します。

:::note warn
文頭に半角ハイフン `-` をつけることで、JavaScript のコードとして実行します。
:::

```pug:Pug
ul
  - for (var i = 1; i < 4; i++)
    li リスト#{i}
```

```html:HTML（コンパイル結果）
<ul>
  <li>リスト1</li>
  <li>リスト2</li>
  <li>リスト3</li>
</ul>
```

### each in

配列要素をループさせるには `each in` を利用します。

:::note info
Pug の構文のため、文頭に半角ハイフン `-` をつけずに使用可能です。
:::

#### 配列

```pug:Pug
ul
  - const list = ['1','2','3']
  each item in list
    li リスト#{item}
```

```html:HTML（コンパイル結果）
<ul>
  <li>リスト1</li>
  <li>リスト2</li>
  <li>リスト3</li>
</ul>
```

#### 連想配列

```pug:Pug
ul
  -
    const list = [
      {
        num: 1,
        text: 'ほげほげ',
      },
      {
        num: 2,
        text: 'ふがふが',
      },
      {
        num: 3,
        text: 'ぴよぴよ',
      },
    ]
  each item in list
    li リスト#{item.num} : #{item.text}
```

```html:HTML（コンパイル結果）
<ul>
  <li>リスト1 : ほげほげ</li>
  <li>リスト2 : ふがふが</li>
  <li>リスト3 : ぴよぴよ</li>
</ul>
```

## 応用編

### 画像のリスト

#### 画像名が連番

```pug:Pug
ul
  - const altList = ['ほげほげ', 'ふがふが', 'ぴよぴよ']
  each alt, index in altList
    - const imageNum = ('00' + (index + 1)).slice(-2)
    li
      img(src=`${imageNum}.jpg` alt=`${alt}`)
```

```html:HTML（コンパイル結果）
<ul>
  <li><img src="01.jpg" alt="ほげほげ"></li>
  <li><img src="02.jpg" alt="ふがふが"></li>
  <li><img src="03.jpg" alt="ぴよぴよ"></li>
</ul>
```

#### 画像名が文字列

```pug:Pug
ul
  -
    const imageList = [
      {
        filename: 'hogehoge',
        alt: 'ほげほげ',
      },
      {
        filename: 'fugafuga',
        alt: 'ふがふが',
      },
      {
        filename: 'piyopiyo',
        alt: 'ぴよぴよ',
      },
    ]
  each image in imageList
    li
      img(src=`${image.filename}.jpg` alt=`${image.alt}`)
```

```html:HTML（コンパイル結果）
<ul>
  <li><img src="hogehoge.jpg" alt="ほげほげ"></li>
  <li><img src="fugafuga.jpg" alt="ふがふが"></li>
  <li><img src="piyopiyo.jpg" alt="ぴよぴよ"></li>
</ul>
```

### 画像とキャプションのリスト

:::note warn
画像の `alt` 属性にキャプションと同じ内容を設定すると冗長なため空にしています。
:::

#### 画像名が連番

```pug:Pug
ul
  - const captionList = ['ほげほげ', 'ふがふが', 'ぴよぴよ']
  each caption, index in captionList
    - const imageNum = ('00' + (index + 1)).slice(-2)
    li
      img(src=`${imageNum}.jpg` alt=``)
      if caption !== ''
        span #{caption}
```

```html:HTML（コンパイル結果）
<ul>
  <li>
    <img src="01.jpg" alt="">
    <span>ほげほげ</span>
  </li>
  <li>
    <img src="02.jpg" alt="">
    <span>ふがふが</span>
  </li>
  <li>
    <img src="03.jpg" alt="">
    <span>ぴよぴよ</span>
  </li>
</ul>
```

#### 画像名が文字列

```pug:Pug
ul
  -
    const imageList = [
      {
        filename: 'hogehoge',
        caption: 'ほげほげ',
      },
      {
        filename: 'fugafuga',
        caption: 'ふがふが',
      },
      {
        filename: 'piyopiyo',
        caption: 'ぴよぴよ',
      },
    ]
  each image in imageList
    li
      img(src=`${image.filename}.jpg` alt=``)
      if image.caption !== ''
        span #{image.caption}
```

```html:HTML（コンパイル結果）
<ul>
  <li>
    <img src="hogehoge.jpg" alt="">
    <span>ほげほげ</span>
  </li>
  <li>
    <img src="fugafuga.jpg" alt="">
    <span>ふがふが</span>
  </li>
  <li>
    <img src="piyopiyo.jpg" alt="">
    <span>ぴよぴよ</span>
  </li>
</ul>
```

### お知らせのリスト

お知らせの仕様は以下とします。

* Newアイコン
  * class属性に `is-new` を設定
* リンクに `target="_blank"` を設定
  * 外部リンク
    *　リンク文字列の先頭が `http`
  * pdfファイルへのリンク
    * リンク文字列の末尾が `.pdf`
* リンクがない場合はテキストのみ表示

```pug:Pug
-
  const newsList = [
    {
      isNew: true,
      date: '2022年4月4日',
      text: 'Newアイコンを表示',
      link: '/news.html'
    },
    {
      isNew: false,
      date: '2022年3月3日',
      text: '外部リンク',
      link: 'https://www.google.com/'
    },
    {
      isNew: false,
      date: '2022年2月2日',
      text: 'PDFリンク',
      link: '/news.pdf'
    },
    {
      isNew: false,
      date: '2022年1月1日',
      text: 'リンクなし',
      link: ''
    },
  ]
each news in newsList
  -
    const isExternalLink = /^http/.test(news.link)
    const isPdfLink = /\.pdf$/.test(news.link)
    const newClass = news.isNew ? 'is-new' : ''
    const target = isPdfLink || isExternalLink ? '_blank' : false
  mixin newsBody
    p #{news.date}
    p #{news.text}
  article(class=newClass)
    if news.link
      a(href=news.link target=target)
        +newsBody
    else
      +newsBody
```

```html:HTML（コンパイル結果）
<article class="is-new">
  <a href="/news.html">
    <p>2022年4月4日</p>
    <p>Newアイコンを表示</p>
  </a>
</article>
<article>
  <a href="https://www.google.com/" target="_blank">
    <p>2022年3月3日</p>
    <p>外部リンク</p>
  </a>
</article>
<article>
  <a href="/news.pdf" target="_blank">
    <p>2022年2月2日</p>
    <p>PDFリンク</p>
  </a>
</article>
<article>
  <p>2022年1月1日</p>
  <p>リンクなし</p>
</article>
```

#### 年ごとに見出しを追加

```pug:Pug
-
  const newsList = [
    {
      isNew: true,
      date: '2022年4月4日',
      text: 'Newアイコンを表示',
      link: '/news.html'
    },
    {
      isNew: false,
      date: '2022年3月3日',
      text: '外部リンク',
      link: 'https://www.google.com/'
    },
    {
      isNew: false,
      date: '2022年2月2日',
      text: 'PDFリンク',
      link: '/news.pdf'
    },
    {
      isNew: false,
      date: '2022年1月1日',
      text: 'リンクなし',
      link: ''
    },
    {
      isNew: false,
      date: '2021年1月1日',
      text: 'リンクなし',
      link: ''
    },
    {
      isNew: false,
      date: '2020年1月1日',
      text: 'リンクなし',
      link: ''
    },
  ]
  const yearList = []
  for (const news of newsList) {
    const year = news.date.slice(0, 4);
    if (yearList.includes(year)) continue;
    yearList.push(year)
  }
each year in yearList
  section
    h2 #{year}年
    each news in newsList
      if news.date.includes(year)
        -
          const isExternalLink = /^http/.test(news.link)
          const isPdfLink = /\.pdf$/.test(news.link)
          const newClass = news.isNew ? 'is-new' : ''
          const target = isPdfLink || isExternalLink ? '_blank' : false
        mixin newsBody
          p #{news.date}
          p #{news.text}
        article(class=newClass)
          if news.link
            a(href=news.link target=target)
              +newsBody
          else
            +newsBody
```

```html:HTML（コンパイル結果）
<section>
  <h2>2022年</h2>
  <article class="is-new">
    <a href="/news.html">
      <p>2022年4月4日</p>
      <p>Newアイコンを表示</p>
    </a>
  </article>
  <article>
    <a href="https://www.google.com/" target="_blank">
      <p>2022年3月3日</p>
      <p>外部リンク</p>
    </a>
  </article>
  <article>
    <a href="/news.pdf" target="_blank">
      <p>2022年2月2日</p>
      <p>PDFリンク</p>
    </a>
  </article>
  <article>
    <p>2022年1月1日</p>
    <p>リンクなし</p>
  </article>
</section>
<section>
  <h2>2021年</h2>
  <article>
    <p>2021年1月1日</p>
    <p>リンクなし</p>
  </article>
</section>
<section>
  <h2>2020年</h2>
  <article>
    <p>2020年1月1日</p>
    <p>リンクなし</p>
  </article>
</section>
```