
開発者がコントロールのレイアウトに合わない場合があります。 たとえば、赤いボタンをカードに追加したい、またはマップ上の多くのスペースを占める標準のコルクパネルに適していない。
そのような場合、開発者は既存の要素を自分の要素に置き換える必要があります。 同時に、要素の外観を担当する部分のみを変更し、マップコントロールの論理部分は変更しないでおくことができます。
独自のコントロールレイアウトを作成するには、APIアーキテクチャの一部を理解する必要があります。 この記事では、開発者がこのタスクを完了する前に理解しなければならない概念の概要を説明し、コントロールの論理部分と視覚部分の間の相互作用の一般原則を説明します。 単純なものから複雑なものまで、レイアウトを作成する3つの例も考慮されます。
この記事は、Yandex.Map 2.0 APIの経験がある開発者を対象としています。 基本的な概念を理解するには、開発者ガイドを読むことをお勧めします 。
基本的な概念
レイアウトとは何ですか?
レイアウトは、コントロールの視覚的表現です。 実際、レイアウトは、転送されたデータに基づいてhtmlを生成できるオブジェクトです。
レイアウトは、フィールドを持つ入力オブジェクトを受け取ります。
- control-コントロールへのリンク。
- options-コントロールのオプションマネージャー。
- data-コントロールのデータマネージャー。
- state-コントロールの状態マネージャー。
オプションマネージャー(状態、データ)とは何ですか?
マネージャーは、キーごとに値を設定および取得できるリポジトリです。 オプションマネージャーは、子で見つからない場合、親要素からオプションを受け取ることもできます。
次のようにオプションまたはデータを受け取って設定できます。
smallZoomControl.options.get('layout'); // layout smallZoomControl.data.set('publicId', myId); //
オプション、データ、ステータスの違いは何ですか?
オプションは、コントロールの外観に関する推奨事項です。 たとえば、オプションを介してレイアウトのクラス( 'layout')、ボタンの最小幅( 'minWidth')などが設定されます。
オプションの重要な機能は、親から継承する機能です。 つまり、オプションは直接設定することも、親要素のいずれかを使用して設定することもできます。 親要素を介してオプションを指定する場合、通常、プレフィックスが使用されます。 たとえば、control.Buttonの 'layout'オプションは、マップを介して 'buttonLayout'( 'button' + 'layout')として設定できます。
データは、要素の情報内容を記述するフィールドのセットです。 たとえば、データはリストタイトル(「タイトル」)またはボタンコンテンツ(「コンテンツ」)である場合があります。 データは親要素から継承されず、オブジェクトに直接のみ設定されます。
状態は、コントロールの現在の状態を記述するフィールドのセットです。 ステータスフィールドは、ユーザーのアクションの結果として変更される場合があります。
ステータスフィールドの例:
- 'expanded'-ドロップダウンリストが展開されているか折りたたまれているかどうかに署名します。
- '選択'-サイン、ボタンが押されているか押されています。
状態フィールドも親要素から継承されず、コントロールのアクションの結果として独立して変更できます(たとえば、button.select()メソッドを呼び出した結果、「選択」ボタンの状態フィールドが変更されます)。
例1.データ、オプション、および制御状態に基づいてレイアウトを形成する
ほとんどの場合、レイアウトは特別なファクトリーtemplateLayoutFactoryを使用して作成されます。 ファクトリーを使用すると、エレメントのdom表現が後に形成されるテキストテンプレートを指定できます。
control.Buttonの独自のレイアウトを作成する例を考えてみましょう。 作成したレイアウトから取得するもの:
1.ボタンには何らかの種類の碑文が必要です。
2.ボタンには、押されたときと押されていないときの2つの状態があります。
碑文は、ボタンのデータフィールドの1つの値です。 標準のボタン実装では、「コンテンツ」フィールドを使用します。
myButton.data.get('content');
必要に応じて、別の任意のデータフィールドの使用を妨げるものはありません。 例:
myButton.data.set('caption', '');
この場合、標準のデータフィールドで問題ありません。
この例では、divはボタンのdomビューになります。
<div class='my-button'> </div>
そのため、「ボタンタイトル」という句の代わりに、コントロールのデータのフィールドを置き換える必要があります。 ボタンレイアウトの作成は次のようになります。
var ButtonLayout = ymaps.templateLayoutFactory.createClass("<div class='my-button'> $[data.content] </div>");
テキスト「ボタンタイトル」の代わりに、テンプレート「$ [data.content]」を挿入しました。
モックファクトリは、データ、オプション、または状態マネージャーを処理できます。 したがって、ドットを使用して「コンテンツ」フィールドにアクセスします。ファクトリは、データがデータマネージャーであることを独自に判断し、data.get(「コンテンツ」)操作を実行できます。
データマネージャーを使用してこのフィールドの値を変更すると、レイアウトが自動的に再構築されます。
最初のポイントを扱いました。 ポイント2に進みます。
通常の状態では、ボタンは次のようになります。
<div class='my-button'> </div>
押されると、次のようになります。
<div class='my-button my-button-selected'> </div>
つまり、特別なクラスがdiv要素に追加され、ボタンの外観が変更されます。
テキストテンプレートを変更します。
var ButtonLayout = ymaps.templateLayoutFactory.createClass("<div class='my-button [if state.selected]my-button-selected[endif]'>$[data.content]</div>");
そのため、データとボタンの状態に基づいて、そのレイアウトを作成できました。 ボタンレイアウトはクリックに自動的に応答し、コントロールに変換します。 したがって、この例では追加のロジックは必要ありません。
例を見る
例2.コントロールと対話するレイアウト
より複雑な例を考えてみましょう-マップのズームファクターのコントロール要素の作成、つまり「ズームコントロール」。
コントロールのHTMLテンプレートは次のようになります。
<div> <div id='zoom-in'>+</div> <div id='zoom-out'>-</div> </div>
作成されたレイアウトから次のものを取得します-プラスまたはマイナスの要素をクリックすると、マップのズームを1ずつ拡大または縮小する必要があります。
ドキュメントには、control.SmallZoomControlレイアウトがIZoomControlLayoutインターフェースを実装すると書かれています。 インターフェースの説明を読み、「zoomchange」イベントの説明を見つけます。
Zoomchange-マップの縮尺係数の変更を開始するイベント。
Eventクラスのインスタンス。 Event.getメソッドで使用可能なフィールドの名前:
•newZoom-スケーリング係数の新しい値。
•oldZoom-ズームファクターの古い値。
これは以下を意味します-レイアウトが 'zoomchange'イベントを発生させると、コントロールはそれをキャッチし、それに応じて反応します(つまり、マップのスケールファクターを変更します)。
HTMLレイアウトの形成後、リスナーは特定の要素にハングアップする必要があります。 特に、id = 'zoom-in'およびid = 'zoom-out'の要素で 'click'イベントをリッスンする必要があります。 クリックハンドラーでは、「zoomchange」イベントを生成し、フィールドに古いマップと新しいマップのスケーリング係数を送信します。
ズームコントロールのレイアウトの作成は次のようになります。
// . var MyZoomLayout = ymaps.templateLayoutFactory.createClass("<div>" + "<div id='zoom-in'>+</div>" + "<div id='zoom-out'>-</div>" + "</div>", { // , // . build: function () { // build. MyZoomLayout.superclass.build.call(this); // . $('#zoom-in').bind('click', ymaps.util.bind(this.zoomIn, this)); $('#zoom-out').bind('click', ymaps.util.bind(this.zoomOut, this)); }, clear: function () { // . $('#zoom-in').unbind('click'); $('#zoom-out').unbind('click'); // clear. MyZoomLayout.superclass.clear.call(this); }, zoomIn: function () { var map = this.getData().control.getMap(); // , // . this.events.fire('zoomchange', { oldZoom: map.getZoom(), newZoom: map.getZoom() + 1 }); }, zoomOut: function () { var map = this.getData().control.getMap(); this.events.fire('zoomchange', { oldZoom: map.getZoom(), newZoom: map.getZoom() - 1 }); } });
例を見る
control.SmallZoomControlの例を使用して、コントロールとそのレイアウトの相互作用を調べました。 スキームの知識を要約します。

コントロールのレイアウトは、フィールド 'state'、 'data'または 'options'に基づいており、それらの変更を監視します。 フィールド値を変更すると、レイアウトが再構築されます。
次に、レイアウトは、インターフェイスで指定されたイベントを使用してコントロールの状態を変更できます。 コントロールは、指定された一連のイベントをリッスンし、適切なアクションを実行します(または実行しません)。
例3.グループコントロールレイアウトの作成
グループコントロールは、子要素を追加できるという点で異なります。 したがって、グループ要素のレイアウトには、子要素のhtmlレイアウトのルートとなるhtml要素が必要です。
ドロップダウンリストのレイアウトを作成します。
展開されたリストのHTMLレイアウトは次のとおりです。
<div id='my-listbox-header'> </div> <div id='my-listbox'> <br/> <br/> </div>
完成したレイアウトから得たいものを書きましょう:
1.レイアウトでリストヘッダーを置き換える必要があります。
2.リストの子は、親の指定されたdom要素に自動的に追加される必要があります。
3.リストは折りたたんだり展開したりできるはずです。
4.何らかの方法でリストアイテムの外観を設定する必要があります。
レイアウト内のリストのタイトルを、ボタンのレイアウト例との類推で置き換えます。
var MyListBoxLayout = ymaps.templateLayoutFactory.createClass( "<div id='my-listbox-header'>$[data.title]</div>” + “<div id='my-listbox'></div>" );
それでは、2番目のポイントに移りましょう。
ドロップダウンリストの親dom要素は、<div id = 'my-listbox'>要素になります。
グループコントロールのレイアウトは、 IGroupControlLayoutインターフェイスを実装する必要があります。 このインターフェイスの機能は、getChildContainerElementメソッドの存在です。 このメソッドを介して、コントロールは、子要素のHTMLレイアウトをアタッチする必要があるdom要素を受け取ります。
var MyListBoxLayout = ymaps.templateLayoutFactory.createClass( "<div id='my-listbox-header'>$[data.title]</div>” + “<div id='my-listbox' ></div>", { build: function() { MyListBoxLayout.superclass.build.call(this); this.childContainerElement = $('#my-list-box')[0]; }, getChildContainerElement: function () { return this.childContainerElement; } });
レイアウトを構築すると、目的のdom要素が見つかり、グループコントロールは親に子要素を追加できます。
レイアウトには置換「$ [data.title]」が含まれているため、再構築できます。 このフィールドが変更されると、レイアウトは、html表示を更新するために、いくつかのクリアおよびビルドメソッドを呼び出します。 この場合、子のコンテナとして機能するdom要素が変更されます(はい、前のものとまったく同じですが、物理的に異なるdom要素になります)。
コントロールはレイアウトの変更を監視せず、子コンテナが変更されたことを認識しません。 したがって、私たちは独立して彼にこれを知らせる必要があります。
再構築中に、毎回IGroupControlLayout 'childcontainerchange'インターフェイスイベントを生成します。
var MyListBoxLayout = ymaps.templateLayoutFactory.createClass( "<div id='my-listbox-header'>$[data.title]</div>” + “<div id='my-listbox'></div>", { build: function() { MyListBoxLayout.superclass.build.call(this); this.childContainerElement = $('#my-list-box')[0]; // , // . this.events.fire('childcontainerchange', { newChildContainerElement: this.childContainerElement, oldChildContainerElement: null }); }, clear: function () { // // . // , // dom- ie. this.events.fire('childcontainerchange', { newChildContainerElement: null, oldChildContainerElement: this.childContainerElement }); this.childContainerChange = null; MyListBoxLayout.superclass.clear.call(this); }, getChildContainerElement: function () { return this.childContainerElement; } });
これで、レイアウトを再構築するときに、コントロールはコンテナの変更を認識し、子要素を新しいdom要素に転送します。
リストを折りたたんだり展開したりできるようにする必要があります。
最小化された状態のコントロールの外観:
<div id='my-listbox-header'> </div>
展開された状態のコントロールの外観:
<div id='my-listbox-header'> </div> <div id='my-listbox'> <br/> <br/> </div>
この例では(コントロールの状態に応じて)、子要素を持つコンテナを表示または非表示にする必要があることがわかります。
var MyListBoxLayout = ymaps.templateLayoutFactory.createClass( "<div id='my-listbox-header'>$[data.title]</div>” + “<div id='my-listbox' style='display: [if state.expanded]block[else] none[endif];'></div>", { build: function() { MyListBoxLayout.superclass.build.call(this); this.childContainerElement = $('#my-list-box')[0]; // , // . this.events.fire('childcontainerchange', { newChildContainerElement: this.childContainerElement, oldChildContainerElement: null }); }, clear: function () { // // . // , // dom- ie. this.events.fire('childcontainerchange', { newChildContainerElement: null, oldChildContainerElement: this.childContainerElement }); this.childContainerChange = null; MyListBoxLayout.superclass.clear.call(this); }, getChildContainerElement: function () { return this.childContainerElement; } });
コントロールの状態の変化に対応するレイアウトを設定します。 次に、フィードバックを設定する必要があります-ユーザーのアクションに応じて、コントロールに「カール」または「最大化」コマンドを送信します。 幸いなことに、control.ListBoxはデフォルトでレイアウトをクリックすることで折りたたまれたり展開されたりします。 したがって、この例では、追加の手順を実行する必要はありません。 デフォルトの動作に満足できない場合、control.SmallZoomControlで行ったように、レイアウトを介してコマンドをコントロールに送信できます。
ドロップダウンリスト内のアイテムのレイアウトを作成します。 これは非常に簡単です。
ymaps.templateLayoutFactory.createClass("$[data.content]<br/>");
例を見る
カスタムレイアウトの別の例

レイアウトの機能を示すために、APIで最も人気のある6つのマップコントロール(ボタン、ドロップダウンリスト、マップ検索、シンプルマップズームコントロール、トラフィックコントロールパネル、マップタイプスイッチ)を選択し、人気のあるTwitter cssフレームワークを使用して再設計しましたブートストラップ。
例を見る
Githubコード