Surveyproテンプレート
内容
テンプレートとは
テンプレートは、文字列を連結することによってHTMLのブロックを直接javascript / phpに書き込むことに代わるものです。最終結果は同じですが、テンプレートにはいくつかの利点があります。
- テンプレートのコードは最終的なHTMLの外観に非常に近いため、テンプレートの最終結果を確認するのは簡単です。
- テンプレート言語は意図的に制限されているため、複雑なロジックをテンプレートに導入するのは困難です。これにより、ロジックを壊すことなく、テーマデザイナーがテンプレートをオーバーライドすることがはるかに簡単になります。
- テンプレートはJavaScriptからレンダリングできます。これにより、ajax操作でページの一部を再レンダリングすることができます。
テンプレートを作成する方法
テンプレートは「 口ひげ 」と呼ばれる言語で書かれています。口ひげは、データの表示をフォーマットするために使用される追加のタグを持つHTMLとして書かれています。口ひげタグは2つの開閉中括弧で作られています{{tag}}。これらのタグには、動作が異なるいくつかのバリエーションがあります。
{{raiden}}
これは単純な変数置換です。 "raiden"という名前の変数は現在のコンテキスト(および任意の親コンテキスト)で検索され、値が見つかるとタグ全体が変数で置き換えられます(HTMLのエスケープ)。{{{galaga}}}
これはエスケープされていない変数置換です。テンプレート内で変数を置き換える前に変数をエスケープする代わりに、変数はrawとして含まれています。これは、変数にHTMLブロックが含まれている場合などに便利です。{{#lemmings}} jump off cliff {{/lemmings}}
これらは開始および終了セクションタグです。 lemmings変数が存在し、 "not false"値と評価された場合、変数はスタックにプッシュされ、セクションの内容が解析されて結果に含まれます。変数が存在しない場合、またはfalseと評価された場合 - セクションはスキップされます。変数lemmingsが配列に評価されると、文脈上の配列の項目を使用して、配列内の各項目に対してセクションが繰り返されます。これはリストを出力する方法です。{{^lemmings}} enjoy view {{/lemmings}}
「if-not」ブロックに相当し、口ひげには「else」はありません。{{> pacman }}
これは部分的です。インクルードのように考えてください。このタグを使用して、テンプレートに他のテンプレートを含めることができます。{{$blockvar}} ... {{/blockvar}}
これはブロック変数です。それは別のテンプレートに含まれるときに上書きすることができるテンプレートのセクションを定義します。{{ < template_name } } ... { { /template_name } }
これは部分的なものを含めるのと似ていますが、含まれているテンプレート内で定義されている1つ以上のブラック変数を上書きしたいことを明確に示しています。インクルードされたテンプレートで上書きしたいブロック変数の名前と一致するブロック変数をこれらのタグ内で定義することで、黒い変数を上書きすることができます。
だから - これをすべてまとめる:
recipe.moustache
<h3 > {{recipename}} </h3 > <p > {{description}} </p > <h4 > Ingredients </h4 > <ol > {{#ingredients}} <li > {{.}} </li > {{/ingredients}} </ol > <h4 > Steps </h4 > <ol > {{#steps}} <li > {{{.}}} </li > {{/steps}} </ol > {{ > ratethisrecipe }}
このデータが与えられたとき:
{ recipename : "Cheese sandwich" , description : "Who doesn't like a good cheese sandwich?" , ingredients : [ "bread" , "cheese" , "butter" ] , steps : [ "<p>Step 1 is to spread the butter on the bread</p>" , "<p>Step 2 is to put the cheese "in" the bread (not on top, or underneath)</p>" ] }
これを与える: 😋
さらに詳しい情報 - Moustacheの Webサイトには、テンプレートに関するより明確な説明があります。スタックオーバーフローに投稿する前にそれらのページを読んでみてください:)。
ブロック(Moodle 3.0以降)
ブロックは、特別な言及に値するMoustacheの特徴です。これらは継承の形式として使用されます - そして再利用可能なテンプレートのライブラリを構築するために不可欠です。 「ブロック」を利用するには、置き換え可能なセクションを持つ親テンプレートを定義します。これらの各セクションは、次のように「blocks」タグでマークされています(blocksタグは通常のタグのように見えますが、変数名の前には$が付きます)。
<section > <h1 > {{$sectionheading}}Default heading{{/sectionheading}} </h1 > <div > {{$content}}Content for section{{/content}} </div > </section >
今すぐ - 私はこのテンプレートを再利用する必要があるときはいつでも私はそれをインクルードして同時にそれらのセクションのコンテンツを置き換えることができます。
{{ < section } } { { $sectionheading } } Latest News { { /sectionheading } } { { $content } } Nothing happened today - sorry! { { /content } } { { /section } }
テンプレートをインクルードしてブロックを使用したい場合 - includeタグは反対の方向を示しています。
{{ < section } }
ではない
{{> section}}
ブロックはループや他の多くのクールなものをサポートします - 詳細はhttps://github.com/bobthecow/mustache.php/wiki/BLOCKS-pragmaを参照してください。
テンプレートをどこに置きますか。
テンプレートは<componentdir> / templatesフォルダーにあり、ファイル拡張子は.moustacheでなければなりません。テンプレートをロードするときのテンプレート名は<componentname> / <filename>です(ファイル拡張子なし)。
そのため、 "mod_lesson / timer"はテンプレートをmod / lesson / templates / timer.mustacheにロードします。
注意: "/ templates"ディレクトリの下のサブフォルダにテンプレートを配置しないでください。これはサポートされておらず、機能しません。
javascriptからテンプレートを呼び出す方法
JavaScriptからテンプレートをレンダリングするのはかなり簡単です。あなたのためにテンプレートをロード/キャッシュしてレンダリングできる新しいAMDモジュールがあります。
// This is AMD code for loading the "core/templates" module. see [Javascript Modules]. require ( [ 'core/templates' ] , function ( templates ) { // This will be the context for our template. So {{name}} in the template will resolve to "Tweety bird". var context = { name : 'Tweety bird' , intelligence : 2 } ; // This will call the function to load and render our template. var promise = templates. render ( 'block_looneytunes/profile' , context ) ; // The promise object returned by this function means "I've considered your request and will finish it later - I PROMISE!" // How we deal with promise objects is by adding callbacks. promise. done ( function ( source , javascript ) { // Here eventually I have my compiled template, and any javascript that it generated. // I can execute the javascript (probably after adding the HTML to the DOM) like this: templates. runTemplateJS ( js ) ; } ) ; // Sometimes things fail promise. fail ( function ( ex ) { // Deal with this exception (I recommend core/notify exception function for this). } ) ; } ) ;
ボンネットの下で、これは私たちにとって多くの賢いことをしました。キャッシュされていない場合は、Ajax呼び出しによってテンプレートをロードしました。テンプレートに不足しているlang文字列が見つかった場合、それらを1つのajaxリクエストにロードしました。HTMLからJSを分割し、両方を使いやすい方法で返しました。 javascriptパラメータをうまく処理する方法について読んでください。
注意:連鎖と砂糖がうまくいくと、上記の例をかなり短くすることができます。
require ( [ 'core/templates' , 'core/notification' ] , function ( templates , notification ) { var context = { name : 'Tweety bird' , intelligence : 2 } ; templates. render ( 'block_looneytunes/profile' , context ) . done ( doneCallback ) . fail ( notification. exception ) ; } ) ;
テンプレートにJavaScriptが含まれているとどうなりますか?
時々テンプレートはそれがより多くの機能を与えるためにそれがページに追加されたときにいくらかのJSが実行されることを必要とします。テンプレートにはjavascriptのブロックを含めることができますが、慎重にjavascriptを処理するために登録された「ヘルパー」メソッドを持つ特別なsectionタグを使用する必要があります。
profile.mustacheの例
<div id = "profile" > <p > Name: {{name}} </p > <p > Intelligence: {{intelligence}} </p > </div > {{#js}} require('jquery', function($) { // Effects! Can we have "blink"? $('#profile').slideDown(); }); {{/js}}
このテンプレートがPHPによってレンダリングされる場合、JavaScriptはHTMLから切り離され、requirejsがロードされた後のページのフッターにある特別なセクションに追加されます。これにより、最適なページ読み込み速度が得られます。テンプレートがjavascriptによってレンダリングされる場合、javascriptのソースはpromiseから "done"ハンドラに渡されます。それから、 "done"ハンドラがDOMにテンプレートを追加したとき、それは以下を呼び出すことができます。
templates. runTemplateJS ( javascript ) ;
これは(新しいscriptタグを作成してそれをページヘッドに追加することによって)javascriptを実行します。
他にどのようなヘルパーを使用できますか?
{{#str}}
言語文字列をロードするための文字列ヘルパーがあります。
{{# str }} helloworld, mod_greeting {{/ str }}最初の2つのパラメータは、文字列IDとコンポーネント名です。したがって、これは事実上の口ひげの変種です。
get_string('helloworld', 'mod_greeting')。オプションの3番目のパラメータは、文字列の値を定義します。
$aプレースホルダー:
{{# str }} iscool, mod_cool, David Beckham {{/ str }}この例は効果的に
get_string('iscool', 'mod_cool', 'David Beckham')はMoodle PHPコードで行います。変数タグはの値を定義することができます
$aプレースホルダー:
{{# str }} iscool, mod_cool, {{ name }} {{/ str }}
複雑なプレースホルダーを受け入れる文字列については、次のセクションを参照してください。
{{# 見積もり }}
前のセクションで示したように、{{# str }}ヘルパーはの値として渡される複雑なデータ構造を必要とするかもしれません
$aプレースホルダー。その場合はJSONオブジェクト構文を使用できます。
{{# str }} iscool, mod_cool, { "firstname": "David", "lastname": "Beckham" } {{/ str }}
リテラル文字列の代わりにコンテキスト値を使用したい場合は、直感的に次のようなものを使用してください。
{{! DO NOT DO THIS !}} {{# str }} iscool, mod_cool, { "firstname": "{{ firstname }}", "lastname": "{{ lastname }}" } {{/ str }}しかし潜在的な問題があります。可変タグの場合
{{ firstname }}または
{{ lastname }}二重引用符を含む文字列に評価されます。これはJSON構文を壊します。変数タグに現れる可能性のある二重引用符をエスケープする必要があります。これには、
{{# quote }}ヘルパー:
{{! This is OK !}} {{# str }} iscool, mod_cool, { "firstname": {{# quote }}{{ firstname }}{{/ quote }}, "lastname": {{# quote }}{{ lastname }}{{/ quote }} } {{/ str }}
詳細はMDL-52136を参照。
{{#pix}}
ピクセルアイコンタグを生成するためのピクセルアイコンヘルパーがあります。
{{# pix }} t/edit, core, Edit David Beckham {{/ pix }}
最初の2つのパラメータは文字列IDとコンポーネント名、残りは画像の代替テキストです。
どのように私はphpからテンプレートを呼び出すのですか?
phpのテンプレートはレンダラーに添付されています。トリックを行うレンダラーメソッド "render_from_template($ templatename、$ context)"があります。
テンプレートはレンダラーとどのように連携しますか?
contextパラメータに渡されたデータがテンプレート言語に役立つことを保証するために特別な注意を払う必要があります。テンプレート言語は次のことができません。
- 呼び出し機能
- 任意のブール論理を実行する
- レンダリング可能物をレンダリングする
- 能力チェックを行う
- DBクエリを作成する
それで - 私は私のレンダリング可能なものの中に「いくらかの」データがあり、そのレンダリング可能なもののための私のrenderメソッドの中にいくつかのロジックとHTMLの生成があります。
最初に注意することは、あなたがしたくない場合はテンプレートを使用する必要がないということです。それは単にテンプレートをオーバーライドするのではなく、themersがあなたのrenderメソッドをオーバーライドしなければならないことを意味します。しかし、テンプレートを使いたいのであれば、themersを使って "cred"を獲得し、ページ全体をリロードすることなくajaxリクエストに応答してjavascriptからあなたのインターフェースの一部を再レンダリングすることができます。
テンプレートをrenderメソッドにフックするのに使う簡単なパターンがあります。あなたのレンダリング可能なものをレンダリング可能であると同時にテンプレート化可能にするならば - それは新しいメソッド "export_for_template(renderer_base $ output)"を実装する必要があるでしょう。このメソッドは、レンダリング可能オブジェクトに格納されているデータを受け取り、それを「フラット化」してテンプレートで使用できるようにします。他のレンダリング可能オブジェクトと同様に、レンダリング可能オブジェクトにネストされたデータがあり、それらがテンプレートをサポートしていない場合は、rendererパラメータを使用してフラットデータ構造に「レンダリング」できます。単純な型(int、string、bool、float、stdClass、またはこれらの型の配列)のみで構成されるプロパティを持つstdClassを返す必要があります。その後、renderメソッドを更新してデータをエクスポートし、テンプレートを使用してレンダリングすることができます。
レンダラで:
/** * Export this data so it can be used as the context for a mustache template. * * @return stdClass */ public function export_for_template ( renderer_base $output ) { $data = new stdClass ( ) ; $data -> canmanage = $this -> canmanage ; $data -> things = array ( ) ; foreach ( $this -> things as $thing ) { $data -> things [ ] = $thing -> to_record ( ) ; } $data -> navigation = array ( ) ; foreach ( $this -> navigation as $button ) { $data -> navigation [ ] = $output -> render ( $button ) ; } return $data ; }
レンダラクラスでは:
/** * Defer to template. * * @param mywidget $widget * * @return string HTML for the page */ render ( mywidget $widget ) { $data = $widget -> export_for_template ( $this ) ; return $this -> render_from_template ( 'mywidget' , $data ) ; }
テーマのテンプレートをオーバーライドする方法
テンプレートは、レンダラーを上書きするよりも少し簡単に上書きできます。まず - 変更したいテンプレートを見つけます。例えば "mod / wiki / templates / ratingui.mustache"などです。今、あなたのテーマの "templates"ディレクトリの下にあなたが上書きしているプラグインのコンポーネント名でサブフォルダを作成してください。例えば "theme / timtam / templates / mod_wiki"です。最後に、ratingui.mustacheファイルを新しく作成した "theme / timtam / templates / mod_wiki"にコピーして編集します。テーマデザイナーモードがオンの場合は、変更内容をすぐに確認できます。注:テンプレートはCSSと同じようにキャッシュされるので、テーマデザイナーモードを使用していない場合は、編集したテンプレートの最新バージョンを表示するためにすべてのキャッシュを消去する必要があります。オーバーライドしているテンプレートにドキュメントのコメントが含まれている場合(次のセクションを参照)、それを削除することをお勧めします。それでもテンプレートライブラリのドキュメントは表示されます。
テンプレートを文書化する必要がありますか?
はい!!!!テーマデザイナーは、何も壊すことなく、自分が何を変えることができるかという限界を知っておく必要があります。さらなる利点として - あなたの美しい新しいテンプレートは、Moodleに同梱されている "テンプレートライブラリ"ツールに表示することができます。テンプレートライブラリの優れたドキュメントと例を提供するために、テンプレートをドキュメント化するときにはこれらの規則に従う必要があります。
テンプレートにドキュメントのコメントを追加する
口ひげのコメントは次のようになります。
{{! I am a comment. I can span multiple lines. }}
テンプレートライブラリは、この特別なマーカーを表示するドキュメントとして含む口ひげのコメントと、コンテキスト例のソースを探します。
@template component/templatename
テンプレートのドキュメントに含めるべき便利なこと
JSに必要なクラス
これは、このテンプレートのJavaScriptによって使用されるクラスのリストです。テンプレート内の要素からクラスを削除してもJavaScriptが破損する場合は、ここにリストしてください。
JSに必要なデータ属性
これは、このテンプレートのJavaScriptによって使用されるデータ属性(data-enhance = "true"など)のリストです。テンプレート内の要素からデータ属性を削除するとJavaScriptが破損する場合は、ここにリストします。
このテンプレートに必要なコンテキスト変数
これは、テンプレートに渡されるコンテキストに含まれる可能性があるデータの説明です。明確にし、すべての属性を文書化してください。
コンテキスト例(JSON)
テンプレートライブラリは、テンプレートの「プレビュー」をテンプレートライブラリ内で直接表示できるようにするため、ドキュメントコメント内でこのデータを検索します。これは、テーマデザイナーが新しいテーマで使用可能なすべてのテンプレートをテストして、新しいテーマで見栄えがよいことを確認するのに役立ちます。また、テンプレートがさまざまな画面サイズ、言語、およびデバイスに対応していることを確認することも役立ちます。フォーマットは、このテンプレートのrenderメソッドに直接渡されるJSONエンコードオブジェクトです。
完全な例
lib / templates / pix_icon.mustache
{{! This file is part of Moodle - https://moodle.org/ Moodle is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Moodle is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Moodle. If not, see <https://www.gnu.org/licenses/>. }} {{! @template core/pix_icon Moodle pix_icon template. The purpose of this template is to render a pix_icon. Classes required for JS: * none Data attributes required for JS: * none Context variables required for this template: * attributes Array of name / value pairs. Example context (json): { "attributes": [ { "name": "src", "value": "https://moodle.com/wp-content/themes/moodle/images/logo-hat2.png" }, { "name": "class", "value": "iconsmall" } ] } }} <img {{#attributes}}{{name}}="{{value}}" {{/attributes}}/>
テンプレートのコーディングスタイル
この節では、テンプレートを書くときに従うべきコーディングスタイルのガイドラインについて説明します。これらのガイドラインの理由は、テンプレートの一貫性と相互運用性を促進するためです。
各テンプレートの上部にGPLを含める
テンプレートはコードの一種であり、他のコードと同様にそれらをライセンスすることが適切です。
各テンプレートの説明コメントを含める
例外は、あなたがテンプレートを上書きしているときで、親からのドキュメントがそれでも当てはまるならば、あなたはそれを上書きされたテンプレートにコピーする必要はありません。
JSフックにデータ属性を使用する
データ属性は、javascriptフックをテンプレートに追加するのに理想的です。
- クラスはスタイリングを目的としています - テーマデザイナーは機能を損なうことなくクラスを自由に変更できるはずです。
- IDはページ内で一意である必要がありますが、同じテンプレートをページに含める回数を制御することはできません。
- データ属性には意味のある名前を付けることができ、セレクタを使用して効率的にクエリを実行できます。
テンプレート用のカスタムCSSを避ける
これは厳密な規則ではなく、好みです。 MoodleにはすでにCSSが多すぎます - 可能な限り、新しいテンプレートをサポートするために新しいCSSを追加するのではなく、既存のCSSを試して再利用する必要があります。
コアテンプレートをできるだけ再利用する
最初に再利用可能なテンプレートのコアセットを構築する必要があります - しかしそれができたら、インターフェイスを構築するためにそれらのコアテンプレートを常に再利用しようとするべきです。これにより、Moodleはより一貫性のある魅力的でカスタマイズ可能なものになります。
テンプレート内でCSSフレームワーククラスを直接使用する
私たちはコアにブートストラップを持っています - それではそれを最大限に活用しましょう。 "base"テーマもテストされ、必要に応じてオーバーライドされたテンプレートがそこに追加される限り、コアテンプレートでブートストラップクラスを使用しても問題はありません。
スタイルやJavaScriptのIDを避ける
IDはCSS固有性が高いため、スタイル設定に決して使用しないでください。オーバーライドするのは困難です。さらに、IDはページ内で一意である必要があります。つまり、テンプレートはページ内で1回しか使用できません。同じ理由から、IDもJavaScriptには理想的ではありません(ページ内で一意である必要があります)。
IDを使用する唯一の許容できるケースは、JSとテンプレートの間に一対一の接続を作成する必要があるということです。この場合、uniqidヘルパーを使用して、ページ上の他のテンプレートと競合しないIDを生成し、それをIDの一部として使用します。
< div id = "{{uniqid}}-somethingspecific" > </ div > { { #js}} callFunction ( '{{uniqid}}-somethingspecific' ) ; { { / js } }
CSSコーディングスタイルに従う
https://docs.moodle.org/dev/CSS_coding_style
クラス名の単語区切り文字としてハイフンを使用します。小文字のクラス名を使用してください。
テンプレート名と一致するクラス名で、各テンプレートを1つのノードにラップします。
コンポーネント名とテンプレート名を組み合わせ、単語をアンダースコアで区切ってクラス名を生成します。
例えば
< div class = "core_user_header" > ... </ div >
口ひげのテンプレートでphp配列を繰り返す
言語間の互換性のため、Moustacheはハッシュと配列を異なる方法で扱います。php配列とハッシュは同じですが、Moustacheはそれらを異なる扱いをします。キー番号にギャップがあるので、手短に言えば必要です。
$datafortemplate -> mylist = array_values ( $myarraywithnonnumerickeys )
あなたも使うことができます
$datafortemplate -> mylist = new ArrayIterator ( $myarraywithnonnumerickeys ) ;
しかしこれは$ myarraywithnonnumerickeysが空であなたが使用しようとすると失敗します
{{#mylist}} with an array iterator if mylist is empty this block will not run {{/mylist}} {{^mylist}} with an array iterator mylist will not run this block either because it is not quite empty {{/mylist}}
どうやってコアテンプレートを書くの?
コアテンプレートは、理想的には、より複雑なページレイアウトを作成するために他のテンプレート内で使用できる単純な汎用コンポーネントにする必要があります。それらは、開発者や開発者がテンプレートを置き換えることなく簡単に使用できるように十分に柔軟であるべきです。テンプレートは、コンテンツを簡単にオーバーライドできるようにしながら、要素とキークラスのコア構造のカプセル化を試みる必要があります。究極的には、特にHTML要素にそれに関連するクラスがある場合は、可能な限りテンプレートからテンプレートへの重複したHTMLのコピーを避けたいと思います。
口ひげは、コンテキストデータをテンプレートに代入するために変数に依存しますが、残念ながら、コンテキストデータの名前が、テンプレートが使用される可能性があるすべての場所でテンプレートが期待するものと一致することはほとんどありません。そのため、簡単に拡張できるようにし、変数の名前を変更するためだけにテンプレートを複製する必要がないようにするために、それらをブロック変数にラップすることができます。
コアテンプレートを作成する際に留意する必要があるいくつかの重要な点があります。
- テンプレートが実際にどのように使用されるのかを検討してください。テンプレートに含まれる可能性があるいくつかの前提条件を見つけるのに役立つように、テンプレートを使用するテストページを書いてみてください。
- テンプレートで提供するサンプルコンテキストは、主にテンプレートライブラリ内のテンプレートを表示するためのものであり、実際のテンプレートの使用方法とは異なる可能性があります。テンプレートのほとんどの用途は、全体として異なるコンテキストを持ちます。
- コア構造を強制しますが、特定のコンテキストを強制しないようにしてください。コンテンツは上書き可能にする必要があります。
- ブロック変数を使用して、人々が変更したいと思う可能性があるテンプレートのセクションを示します。彼らが彼ら自身の内容で代用したいと思うであろうところで通常。
- テンプレートのHTML / CSS構造から可能な限り切り離された、テンプレートに付随するJavaScriptを保存してください。特定のHTML要素やCSSクラスの存在に頼るのではなく、一般的には任意の要素に追加できるデータ属性を利用することをお勧めします。
例:タブ
例を見て、コアテンプレートを作成する方法を説明しましょう。この例では、ブロックテンプレートとJavaScriptを使用する必要があるかなり複雑なコンポーネントなので、タブテンプレートを作成します。
最初に一般的な構造を下げるための基本的なテンプレートを作成しましょう。それをタブと呼びましょう。これは次のようになります。
<div id="{{ uniqid }}-tab-container"> <ul role="tablist" class="nav nav-tabs"> {{# tabs }} <li role="tab" data-target="{{ uniqid }}-{{ id }}" data-selected-class="active" aria-controls="{{ uniqid }}-{{ id }}" aria-selected="false" tabindex="-1"> <a href="#">{{{ name }}}</a> </li> {{/ tabs }} </ul> <div class="tab-content"> {{# tabs }} <div role="tabpanel" class="tab-pane" id="{{ uniqid }}-{{ id }}"> {{{ content }}} </div> {{/ tabs }} </div> </div> {{#js}} require(['jquery','core/tabs'], function($, tabs) { var container = $("#{{ uniqid }}-tab-container"); tabs.create(container); }); {{/js}}
テンプレートには、次のようなコンテキストが必要です。
{ "tabs": [ {"id":"tab1","name":"Tab 1","content":"This is tab 1 content <a href=\"#\">test</a>"}, {"id":"tab2","name":"Tab 2","content":"This is tab 2 content <a href=\"#\">test</a>"}, {"id":"tab3","name":"Tab 3","content":"This is tab 3 content <a href=\"#\">test</a>"} ] }
タブ要素(キーボードナビゲーション、パネルの表示/非表示など)に電力を供給するために必要なJavaScriptはAMDモジュールとして書かれており、テンプレートに含まれています。 JavaScriptはここで説明するには少し大きすぎますが、記述する際に考慮すべきいくつかの重要なポイントは次のとおりです。理想的にはHTML構造から独立している必要があります。そうすれば、同じJavaScriptを変更することなく使用できます。これを達成するためには、テンプレートの重要な構成要素を識別することが重要です。
この場合、それはタブリスト、タブ、そして内容です。これらのコンポーネントを特定する1つの方法は、DOMの構造を調べることです。例えば、タブリストを探すときに「find me the ul要素」と言ってから「find my the child li要素」と言ってタブを見つけることができます。これはうまくいきますが、それはあなたのjavascriptをHTML構造に結びつけ、後で変更するのを難しくします。別の方法としては、要素の属性を使用する方法があります。たとえば、タブリストを取得するには「ロール 'tablist'を持つ要素を検索する」、「ロール 'tab'を持つ要素を検索する」と表示します。タブこれにより、(正しい属性が設定されている限り)JavaScriptを壊すことなくHTML構造を変更できます。
この例で考慮すべきもう1つの点は、タブが選択されたときにどのクラスに適用するかです。 JavaScriptに "active"のようなものを適用するのは理にかなっていますが、それを特定のCSSフレームワークに結合するため、JavaScriptを変更せずに変更することがより困難になります。この場合、タブが選択されたときにどのクラスが設定されるかを示すために、要素にデータ属性を追加することを選択しました。つまり、JavaScriptは適切なクラスが何であるかを推測する必要はなく、単にテンプレートから取得することができます。
わかりました、それで私達は私達の基本的なテンプレートを持っています。それを使う時が来ました! 2つのタブを表示する単純なユーザープロファイルページを作成したいとしましょう。最初のタブはユーザーの名前、2番目のタブはユーザーの電子メールアドレスになります(考案された例を言い訳してください)。
ページは次のようになります。
<html> <header><title>User Profile</title></header> <body> {{< core/tabs }} </body> </html>
それはとても簡単に見えます!唯一の問題は、どうやってそこに自分のコンテンツを入れるのですか?必要なタブを表示するには、このようなコンテキストを提供する必要があります。
{ "tabs": [ {"id":"tab1","name":"Name","content":"Your name is Mr. Test User."}, {"id":"tab2","name":"Email","content":"Your email is testuser@example.com"}, ] }
ただし、このページのコンテキストは、タブテンプレートが期待しているものと一致しないと仮定しましょう(ほとんどの場合そうです)。タブテンプレートがこのコンテキストでレンダリングされているとしましょう。
{ "name":"Mr. Test User", "email":"testuser@example.com" }
残念ながら、テンプレートがレンダリングされるすべてのコンテキストを完全に制御することはほぼ確実ではありません。つまり、新しいWebサービスを作成して、使用するたびに異なる形式で同じデータを提供することを期待しています。テンプレート。それは手に負えない問題になります。
ブロックを入力してください。テンプレートのセクションを含めるときに上書きできるように定義することで、テンプレートをより柔軟にすることができます。かなりきれいです!これにより、特定のコア構造を強制することはできますが、タブを含むテンプレートにコンテキストを強制することはできません。
そのテンプレートをもう一度見てみましょう。今回はブロックが大きくなります。
<div id="{{ uniqid }}-tab-container"> {{$ tabheader }} <ul role="tablist" class="nav nav-tabs"> {{$ tablist }} {{# tabs }} <li role="tab" data-target="{{ uniqid }}-{{ id }}" data-selected-class="active" aria-controls="{{ uniqid }}-{{ id }}" aria-selected="false" tabindex="-1"> <a href="#">{{{ name }}}</a> </li> {{/ tabs }} {{/ tablist }} </ul> {{/ tabheader }} {{$ tabbody }} <div class="tab-content"> {{$ tabcontent }} {{# tabs }} <div role="tabpanel" class="tab-pane" id="{{ uniqid }}-{{ id }}"> {{{ content }}} </div> {{/ tabs }} {{/ tabcontent }} </div> {{/ tabbody }} </div> {{#js}} require(['jquery','core/tabs'], function($, tabs) { var container = $("#{{ uniqid }}-tab-container"); tabs.create(container); }); {{/js}}
変更点のまとめ
- 誰かがul要素を他のものに変更したい場合に備えて、タブリストの周りに$ tabheaderブロックを追加しました。
- タブのグループの周りに$ tablistブロックを追加して、それらをincldueで上書きできるようにしました。
- コンテンツ要素をdivから変更したい場合に備えて、コンテンツの周囲に$ tabbodyブロックを追加しました。
- コンテンツのタブ変数を囲む$ tabcontentブロックを追加し、コンテンツをinlcudeで上書きできるようにしました。
それでは、このテンプレートを使用したときのユーザープロファイルページの外観を見てみましょう。
<html> <header><title>User Profile</title></header> <body> {{> core/tabs }} {{$ tablist }} <li role="tab" data-target="{{ uniqid }}-tab1" data-selected-class="active" aria-controls="{{ uniqid }}-tab1" aria-selected="false" tabindex="-1"> <a href="#">Name</a> </li> <li role="tab" data-target="{{ uniqid }}-tab2" data-selected-class="active" aria-controls="{{ uniqid }}-tab2" aria-selected="false" tabindex="-1"> <a href="#">Email</a> </li> {{/ tablist }} {{$ tabcontent }} <div role="tabpanel" class="tab-pane" id="{{ uniqid }}-tab1"> Your name is {{ name }}. </div> <div role="tabpanel" class="tab-pane" id="{{ uniqid }}-tab2"> Your email address is {{ email }}. </div> {{/ tabcontent }} {{/ core/tabs }} </body> </html>
それは少し良く見えます!このページで利用可能なコンテキストを使用するようにテンプレートを変更するためにブロックを使用できるようになりました。「name」と「content」を持つ「tabs」配列は必要なくなりました。正しい要素属性を保持しているので、JavaScriptでも機能します。
テンプレートのデータを変更するには、上書きするときに元のテンプレートからブロックにHTMLをコピーして貼り付ける必要があります。この例ではこれで問題なく動作しますが、内部の実装の詳細が漏洩しているので、テンプレート内で望みのカプセル化が得られないことを意味します。私たちがMoodleに使用するCSSフレームワークを変更したいと思った場合(たとえばブートストラップ2からブースト3または4へ)、このタブテンプレートが使用されるコード内のすべての場所を見つけて、HTMLが正しいことを確認する必要があります。それらのブロックでオーバーライドします。
それを念頭に置いて、このテンプレートをもう1回見て、もう少し改善することができるかどうかを確認しましょう。今回は、テンプレートを3つのテンプレートに分割することにしました。
tabs.mustache:
<div id="{{ uniqid }}-tab-container"> {{$ tabheader }} <ul role="tablist" class="nav nav-tabs"> {{$ tablist }} {{# tabs }} {{< core/tab_header_item }} {{/ tabs }} {{/ tablist }} </ul> {{/ tabheader }} {{$ tabbody }} <div class="tab-content"> {{$ tabcontent }} {{# tabs }} {{< core/tab_content_item }} {{/ tabs }} {{/ tabcontent }} </div> {{/ tabbody }} </div> {{#js}} require(['jquery','core/tabs'], function($, tabs) { var container = $("#{{ uniqid }}-tab-container"); tabs.create(container); }); {{/js}}
tab_header_item.mustache
<li role="tab" data-selected-class="active" aria-selected="false" tabindex="-1"> <a href="#">{{$ tabname }}{{{ name }}}{{/ tabname }}</a> </li>
tab_content_item.mustache
<div role="tabpanel" class="tab-pane" {{$ tabpanelcontent }}{{{ content }}}{{/ tabpanelcontent }} </div>
変更の概要
- テンプレートを3つに分割して、タブをそれ自身のテンプレートに、コンテンツをそれ自身のテンプレートに移動してから、それらをタブテンプレートに含めます。
- タブとコンテンツからIDを削除しました。 JavaScriptは実行時にこれらのIDを割り当てるために更新されるため、テンプレートコンテキストの一部として提供する必要はありません。
- tab_header_itemテンプレートに$ tabnameブロックを追加して、インポート時の名前を柔軟にしました。
- tab_content_itemテンプレートに$ tabpanelcontantブロックを追加して、インポート時のコンテンツの柔軟性を高めました。
それでは、この例で次のようになっているのを見てみましょう。
<html> <header><title>User Profile</title></header> <body> {{> core/tabs }} {{$ tablist }} {{> core/tab_header_item }} {{$ tabname }}Name{{/ tabname }} {{/ core/tab_header_item }} {{> core/tab_header_item }} {{$ tabname }}Email{{/ tabname }} {{/ core/tab_header_item }} {{/ tablist }} {{$ tabcontent }} {{> core/tab_content_item }} {{$ tabpanelcontent }}Your name is {{ name }}.{{/ tabpanelcontent }} {{/ core/tab_content_item }} {{> core/tab_content_item }} {{$ tabpanelcontent }}Your email address is {{ email }}.{{/ tabpanelcontent }} {{/ core/tab_content_item }} {{/ tabcontent }} {{/ core/tabs }} </body> </html>
そしてこれで終わりです。上記の変更を加えた後は、コンテキストの変更を許可するために前の変更の利点を維持することができましたが、HTMLをどこにでもコピー&ペーストする必要もなくなりました。代わりに、コンテンツを取得するために定義されたいくつかの追加ブロックと共に子テンプレートを使用することができます。
タブのHTMLまたはCSSフレームワークを変更したい場合は、コアのタブテンプレートを変更するだけで、このページに無料でアップデートが届きます。