Ditto で JSON を表示して jQuery で Ajax メニューを作る

最終更新日
2017.07.28

移行作業中のため一部のページが正しく表示されていない場合があります(o-ω-))

MODX の Wayfinder+Ditto+jQuery で Ajax なメニューを作成します。

MODX は Ajax との相性が非常に良く、JavaScript だけ書けば、PHP を自分で書かなくても標準のリソースやスニペットで色々なものを自由に作成する事ができます。Ditto には JSON を表示する機能があるので簡単な GET 用の API ならこれを利用して作る事が出来ます。 JSON を使用する例として簡単な Ajax メニューを作り方を紹介します。

デモ

タブメニューのサンプル

JSON 用のリソースを Ditto で作成

JSON を表示するためのリソースを作成します。Ditto の拡張機能の request を有効にして、URLパラメータ を受け取れるようにします。
適当な場所に新規リソースを作成して内容を以下のように記述し、使用するテンプレートは(blank)にします。
とりあえずエイリアスは「menu.json」にしました。メニューに表示と検索対象にするは外しておきます。

クエリーが付くのでカテゴリ毎にキャッシュされます。

[[Ditto?
  &extenders = `request`     // リクエスト取得の拡張機能を使用
  &good = `parents`          // parentsパラメータのみ許可する
  &format = `json`           // JSON形式で表示
  &orderBy = `editedon DESC` // 表示順
  &display = `20`            // 最大取得数
]]

追加パラメータの&goodで parents のみリクエストを許可します。記事の表示順や取得数などはここで設定しておきます。

http://example.com/menu.json?ditto_parents=1というような感じでアクセスして JSON が表示されればOKです。

request 拡張の追加パラメータ

request を有効にすると以下のパラメータが使用できるようになります。

good 許可するパラメータを指定する。
goodがある場合はそれのみ許可されるので付けるの推奨。
bad 許可しないパラメータを指定する
stripTags パラメータで送られた値からタグを削除する

メニューの作り方

これはおまけですが概要は親メニューを Wayfinder で表示して、内容を Ditto で表示させるようにします。
親メニュー → 小メニューというよりは、親メニュー → 記事一覧の表示向けですね。
親メニューを動的に吐かなくてもいいなら(アクティブメニューか判断しない)Wayfinder は必要ないです。

Wayfinderを記述する

親メニューを表示させたい場所に記述します。

<div class="demo1">
[[Wayfinder?
  &startId = `[ID]`
  &level = `1`
  &rowClass = `block`
  &outerTpl = `@CODE:<div class="blocks">[+wf.wrapper+]</div>`
  &rowTpl = `@CODE:
  <div data-docid="[+wf.docid+]"[+wf.classes+]>
    <div class="tab" data-docid="[+wf.docid+]"><a href="[+wf.link+]">[+wf.linktext+]</a></div>
  </div>`
]]
</div>

JavaScriptの記述

サンプルで使っているものですが、よかったら参考にしてください。

(function ($) {
  $(function () {
    var eWrapperName = '.demo1'
    var eWrapper = $(eWrapperName)
    var eBlocks = eWrapper.children('.blocks')
    var eTabs = $('<div class="tabs"></div>')
    eWrapper.prepend(eTabs)
    eBlocks.append($('<div class="log"><span class="loading">Loading...</span></div>').hide())
    eBlocks.find('.tab').prependTo(eTabs)
    eBlocks.find('.block').hide()
    eWrapper = eBlocks = eTabs = null

    $(eWrapperName).on('click', '.tab', function (e) {
      e.preventDefault()
      console.log('click')
      $(this).siblings('.tab').removeClass('is_enabled')
      var eBlocks = $(eWrapperName).children('.blocks')

      var docid = $(this).attr('data-docid')
      var eCurrent = eBlocks.children('.block[data-docid="' + docid + '"]')
      var eLog = eBlocks.children('.log')

      if (!eCurrent.children('.doclist').length) {
        eLog.show()
        $.ajax({
          type: 'GET',
          url: 'modx/tips/ditto-ajaxmenu/menu.json',
          dataType: 'json',
          data: {
            ditto_parents: parseInt(docid)
          }
        }).done(function (response) {
          eBlocks.children('.block').hide()
          var html = ''
          if ($(response.entries).length) {
            $(response.entries).each(function () {
              html += '<li><a href="' + this.link + '">' + this.title + '</a></li>'
            })
          } else {
            html += '<li>記事はありません</li>'
          }
          eCurrent.append('<ul class="doclist">' + html + '</ul>').fadeIn()
          eLog.fadeOut()
        }).fail(function () {
          eLog.replaceWith('<div class="log error">データが読み込めませんでした</div>')
        })
      } else {
        eBlocks.children('.block').hide()
        eCurrent.fadeIn()
      }
      $(this).addClass('is_enabled')
    })

    $(eWrapperName + ' .tab[data-docid="43"]').trigger('click')
  })
})(jQuery)

CSSの記述

これもサンプルで使用しているスタイルです。

@charset "UTF-8";
.demo1 {
    position: relative;
}

.demo1 .tab {
    display: table-cell;
    background: #e4e4e4;
    padding: 8px 10px;
    border-top: 1px solid #ccc;
    border-right: 1px solid #ccc;
    cursor: pointer;
}

.demo1 .tab a {
    text-decoration: none;
}

.demo1 .tab.is_enabled {
    background: #fff;
}

.demo1 .tab:first-child {
    border-left: 1px solid #ccc;
    border-radius: 3px 0 0 0;
}

.demo1 .tab:last-child {
    border-radius: 0 3px 0 0;
}

.demo1 .blocks {
    margin: 0;
    padding: 0;
    border: 1px solid #ccc;
    position: relative;
    min-height: 50px;
}

.demo1 .log {
    position: absolute;
    top: 0;
    width: calc(100% - 20px);
    height: calc(100% - 20px);
    background: rgba(220, 220, 220, .9);
    padding: 10px;
    text-align: center;
}

.demo1 .loading {
    position: absolute;
    display: block;
    top: 50%;
    width: 100%;
    height: 1em;
    margin-top: -0.5em;
}

.demo1 {
    width: 640;
    position: relative;
}

アクティブなメニューの内容をあらかじめ表示させておく

予め特定のメニューを表示しておく場合は以下のように調整します。

最後の行にtrigger('click')を追加しておけばすでにクリックされた状態になります。

$('.demo1 .tab[data-docid="43"]').trigger('click');