mtapp.columnGroup に任意のフィールドやテキストを表示するだけのフィールドを入れる方法
目次
Movable Type のコンテンツデータ編集画面で mtapp.columnGroup を使うと、フィールドをグループ化してカード形式やアコーディオン形式で段組レイアウトにできます。とても便利な機能なのですが、ちょっとしたハマりポイントがあります。
テキスト表示フィールドや、自分で作った任意のフィールドが columnGroup の中に入ってくれない――という問題です。
今回はこの原因と回避テクニックをご紹介します。
なぜテキスト表示フィールドは columnGroup に入らないのか
mtapp.columnGroup は内部で window.mtappVars.contentDataFields というオブジェクトを参照して、各フィールドの DOM 要素を取得しています。
const contentField = window.mtappVars.contentDataFields['contentField' + field.id];
この contentDataFields は、MTAppjQuery の初期化時に getContentDataFields() という関数で自動的に構築されます。具体的には、#content_data > div の中に name="content-field-XX" や id="asset-field-XX" といった特定のパターンを持つ要素があるかどうかでフィールドを識別しています。
ところが、テキスト表示フィールドの HTML はこうなっています。
<div class="mb-3">
<div class="first-child">テキスト表示エリアのテキストです。</div>
<small class="form-text text-muted last-child"></small>
</div>
通常のフィールドと違って id がなく、 data-mtapp-field-* 属性も付かず、内部に name="content-field-XX" のような要素もありません。そのため getContentDataFields() の検出対象外となり、contentDataFields に登録されません。
登録されていないフィールドは columnGroup から見えないので、グループの中に入れようとしても無視されてしまうわけです。
解決策: columnGroup 実行前に手動で登録する
columnGroup を呼び出す前に、任意のフィールドを contentDataFields へ手動登録するという方法で対処できます。
やることは 3 つです。
mtapp.makeFieldでフィールドの HTML を作って DOM に挿入する- 挿入した要素に
contentFieldXXX形式のidを付与する window.mtappVars.contentDataFieldsへ登録する
この 3 ステップをまとめたヘルパー関数を作っておくと、1 つでも複数でも同じ書き方で追加できて便利です。
ヘルパー関数を用意する
/**
* 任意のフィールドを作成して contentDataFields に登録する
* @param {Object} params
* @param {number} params.id - 他と衝突しない任意の数値 ID
* @param {string} params.label - フィールドのラベル
* @param {string} params.content - フィールド本文の HTML
* @param {string} [params.hint] - 補足ヒント(省略可)
* @param {string} params.anchorSelector - 挿入基準となる既存フィールドのセレクタ
*/
function registerCustomField({ id, label, content, hint = '', anchorSelector }) {
// mtapp.makeField(type: 'cd') で HTML 文字列を生成
const html = mtapp.makeField({
basename: `contentField${id}`,
label,
content,
hint,
type: 'cd'
});
const anchor = document.querySelector(anchorSelector);
const selector = `#contentField${id}`;
// 重複挿入を防止しつつ DOM に追加
if (anchor && !document.querySelector(selector)) {
anchor.insertAdjacentHTML('afterend', html);
// mtapp.makeField(type: 'cd') はルート要素に id を付けないため手動で付与
const inserted = anchor.nextElementSibling;
if (inserted) {
inserted.id = `contentField${id}`;
}
}
// columnGroup が参照する辞書へ登録
const element = document.querySelector(selector);
if (element) {
window.mtappVars.contentDataFields[`contentField${id}`] = element;
}
}
ポイントは 2 つあります。
mtapp.makeField({ type: 'cd' })はルート要素にidを付与しないため、挿入後にinsertedElement.idで手動セットしていますcolumnGroupはcontentDataFields['contentFieldXXX']でフィールドを引くので、この辞書への登録が必須です
使い方
1 つだけ追加する場合
const anchorSelector = '#contentField' + mtapp.getContentFieldIdByLabel('基準にするフィールド名');
registerCustomField({
id: 999001,
label: '補足説明テキスト',
content: '<div class="first-child">ここに表示したいテキストを書きます。</div>',
hint: '補足のヒントテキストです。',
anchorSelector
});
mtapp.columnGroup({
group: [
{
label: 'フィールドグループ',
key: 'my-group',
fields: [
{ label: 'テキスト', column: 'col-lg-6' },
{ id: 999001, label: '補足説明テキスト', column: 'col-lg-6' }
]
}
],
selector: anchorSelector,
type: 'card',
method: 'after'
});
複数追加する場合
registerCustomField を追加したいフィールドの数だけ呼ぶだけです。
const anchorSelector = '#contentField' + mtapp.getContentFieldIdByLabel('基準にするフィールド名');
registerCustomField({
id: 999001,
label: '注意事項',
content: '<div class="first-child">この商品は受注生産です。</div>',
anchorSelector
});
registerCustomField({
id: 999002,
label: '配送について',
content: '<div class="first-child">通常 3〜5 営業日で発送します。</div>',
anchorSelector
});
mtapp.columnGroup({
group: [
{
label: '補足情報',
key: 'supplementary',
fields: [
{ label: 'テキスト', column: 'col-lg-6' },
{ id: 999001, label: '注意事項', column: 'col-lg-6' },
{ id: 999002, label: '配送について', column: 'col-lg-6' }
]
}
],
selector: anchorSelector,
type: 'card',
method: 'after'
});
通常のフィールドは label だけで指定できますが、手動登録したフィールドは contentType.fields に存在しないため label からの ID 逆引きができません。id を明示的に渡す必要がある点だけ注意してください。
ちなみに、columnGroup の内部では field.id が渡されている場合、field.label は一切参照されません。label から ID を逆引きするのは id が未指定のときだけです。なので、id を指定する場合の label は省略しても動作しますが、コードの可読性のために書いておくのがおすすめです。
この方法の良いところ
user.jsだけで完結します- ヘルパー関数を 1 つ用意しておけば、追加したいフィールドの数だけ呼ぶだけで済みます
- テキスト表示フィールドに限らず、任意の HTML をフィールドとして
columnGroupに組み込めます
まとめ
mtapp.columnGroup にテキスト表示フィールドや自作フィールドが入らない原因は、window.mtappVars.contentDataFields に登録されていないことでした。
columnGroup 実行前に以下の 3 つを行えば解決します。
mtapp.makeFieldで HTML を生成して DOM に挿入- 挿入した要素に
contentFieldXXX形式のidを付与 window.mtappVars.contentDataFieldsへ手動登録
この 3 ステップをヘルパー関数にまとめておけば、1 つでも複数でも同じ書き方で追加できます。コンテンツデータ編集画面のレイアウトを細かくカスタマイズしたいときに、ぜひ試してみてください。