スポンサー
Support Framework7

ルーターコンポーネント

    Router Componentは、componentまたはcomponentUrlプロパティを使用してルートコンテンツを指定したときにRouterによって読み込まれることができる、特別なタイプのコンテンツです。

    これは、アプリケーションをより良く構成し、物事を適切な場所に保持し、多くのことをより速く、より明確で快適な方法で行うのに役立ちます。

    コンポーネント機能

    コンポーネントは、propscontextを受け取り、レンダリング関数を返す関数です。

    コンポーネントのレンダリング関数は、コンポーネントのHTMLコンテンツを含むタグ付きのテンプレートリテラルを返す必要があります。

    例:

    const MyComponent = (props, context) => {
      // いくつかのコンポーネントロジック
      let value = 'foo';
    
      // レンダリング関数を返す
      return () => context.$h`
        <div class="page">
          <p>Value is ${value}</p>
        </div>
      `;
    }
    

    コンポーネントのテンプレート

    前述のとおり、コンポーネントのレンダリング関数は、コンポーネントのHTMLコンテンツを含むタグ付きテンプレートリテラルを返す必要があります。これには注意すべき点がいくつかあります。

    すべての自閉式タグは閉じなければなりません!<br>, <img src="">, <input ...> のような自己完結型のタグを閉じていない場合、コンパイラはエラーを出します。

    すべての空要素は自己閉鎖することができます

    <div class="my-div"></div>
    
    <!-- としても有効です。 -->
    <div class="my-div" />
    

    コンポーネントの小道具

    コンポーネント関数を受け取る最初の引数は props です。このオブジェクトには、navigateメソッドに渡すすべてのpropsと、すべてのルートパラメータが含まれます。

    例えば、以下のようなルートがあるとします。

    {
      path: '/blog/:id',
      component: MyComponent
    }

    そして、/blog/34/というURLでルートに移動すると、props.id'34'になります。

    また、次のようにAPIを使ってコンポーネントに移動した場合も同様です。

    router.navigate('/blog/34/', {
      props: {
        foo: 'bar'
      }
    })

    すると、props は次のようなオブジェクトになります: { id: '34', foo: 'bar' }

    また、propsにはカスタムコンポーネントに属性として渡されるプロパティが含まれます。カスタムコンポーネントがそのような属性を持っている場合

    <my-component foo="bar" id="25" user=${{name: 'John'}} number=${30}></my-component>

    とすると、$propsは次のようになります。

    {
      foo: 'bar',
      id: '25',
      user: {
        name: 'John'
      },
      number: 30
    }

    コンポーネントコンテクスト

    context オブジェクトには、たくさんの便利なヘルパーが含まれています。

    PropertyDescription
    $h

    特別な タグ付きテンプレートリテラル コンポーネントのレンダリング関数の結果と、その中のすべての HTML エントリをラップするために使用しなければなりません。

    const MyComponent = (props, { $h }) => {
      let list = ['item 1', 'item 2'];
    
      return () => $h`
        <div class="page">
          <ul>
            ${list.map((item) => $h`
              <li>${item}</li>
            `)}
          </ul>
        </div>
      `
    }
    $el

    .value` プロパティがコンポーネント HTML 要素を持つ Dom7 インスタンスを含むオブジェクトです。

    $el.value は、コンポーネントがマウントされた後 (または pageInit のような任意のページイベントで) のみ利用可能です。

    const MyComponent = (props, { $el, $onMounted }) => {
      $onMounted(() => {
        $el.value.find('p').addClass('red')
      })
      // ...
    }
    $

    Dom7ライブラリです。

    const MyComponent = (props, { $, $onMounted }) => {
      $onMounted(() => {
        $('p').text('hello world')
      })
      // ...
    }
    $f7

    Framework7 アプリのインスタンス

    $f7.dialog.alert('Hello world!')
    $store

    ストアのインスタンスです。詳細や例は Store documentation をご覧ください。

    $f7route現在のルート。ルートの query, hash, params, path, url を持つオブジェクトが含まれています。
    $f7router

    関連するルーターインスタンス

    $f7router.back(); //navigate back
    $theme

    現在のテーマを示す md, ios, aurora のブール型プロパティを持つオブジェクトです。例えば、以下のようになります。

    if ($theme.ios) { /* do something when iOS theme is active */ }
    if ($theme.md) { /* do something when MD theme is active */ }
    if ($theme.aurora) { /* do something when Aurora theme is active */ }
    $update(callback)

    このメソッドは、このコンポーネントとその子供たちが、更新された状態で再レンダリングされる必要があることを示します。

    const MyComponent = (props, { $update, $h }) => {
      // 初期状態
      let value = 'foo';
    
      const updateValue = () => {
        // ローカル状態の更新
        value = 'foo2';
        // updateメソッドの呼び出し
        $update();
      }
    
      return () => $h`
        <div class="page">
          <p>Value is ${value}</p>
          <button @click=${updateValue}>Update Value</button>
        </div>
      `;
    }
    

    DOMの変更がすぐに適用されることは保証されていないので、DOMに依存している場合(例えば、状態が変更された後にHTMLコンテンツや属性の値を取得する必要がある場合)は、callback関数を引数として渡します。

    $tick(callback)

    また、DOMに依存していて、$update()メソッドを呼び出した後に、コンポーネントの状態とDOMが更新されたことを確認する必要がある場合にも、このメソッドを使用することができます。

    渡されたコールバックは、DOMの更新時に実行されます。

    このメソッドは、DOMの更新時に解決されるPromiseを返します。

    そのため、このように使用することができます。

    $update();
    
    $tick(function () {
      console.log('DOM and state updated');
    });
    
    // あるいはPromiseとして
    $tick().then(() => {
      console.log('DOM and state updated');
    })
    
    // あるいは、非同期関数/メソッドの中で次のように使用できます。
    await $tick();
    console.log('DOM and state updated');
    $f7ready(callback)

    このメソッドは、メインアプリコンポーネントを使用して、アプリの初期化時にFramework7のAPIを確実に呼び出す場合にのみ使用する必要があります。

    const AppComponent = (props, { $f7, $f7ready }) => {
      $f7ready(() => {
        // これでFramework7のAPIを呼び出しても大丈夫です。
        $f7.dialog.alert('Hello!');
      });
    
      // ...
    }
    Events
    $on

    コンポーネントのルート要素にDOMイベントハンドラーをアタッチする機能

    const MyComponent = (props, { $on }) => {
      // pageInit'イベントハンドラをアタッチする
      $on('pageInit', (e, page) => {
        console.log(page.name)
      });
      // ...
    }

    このようなイベントハンドラは、コンポーネントが破壊されると自動的に切り離されます。

    $once

    DOMイベントハンドラーをコンポーネントのルート要素にアタッチする関数です。on`と同じですが、このようなハンドラは一度だけ実行されます。

    $emit(event, data)

    再利用可能なカスタムコンポーネントで、カスタムDOMイベントを発行する関数です。

    const MyComponent = (props, { $emit }) => {
      // カスタムイベントを発行します。
      $emit('myevent')
      // ...
    }

    そして、他の親コンポーネントでも。

    <my-component @myevent=${doSomething} />
    Lifecycle Hooks
    $onBeforeMountコンポーネントがDOMに追加される直前に呼び出されます。
    $onMounted

    コンポーネントがDOMに追加された直後に呼び出されます。

    const MyComponent = (props, { $onMounted }) => {
      // コンポーネントがマウントされたときに何かをする
      $onMounted(() => {
        console.log('component mounted')
      });
      // ...
    }
    $onBeforeUpdateVDOMの前のコンポーネントがパッチ/アップデートされた直後に呼び出される。
    $onUpdatedコンポーネントVDOMがパッチ/アップデートされた直後に呼び出される。
    $onBeforeUnmountコンポーネントがアンマウントされる(DOMから切り離される)直前にコールされる。
    $onUnmountedコンポーネントがアンマウントされ、破壊されたときに呼び出される

    つまり、ページコンポーネントを使ったルートの例は次のようになります。

    routes = [
      // ...
      {
        path: '/some-page/',
        // コンポーネント
        component: (props, { $h, $f7, $on }) => {
          const title = 'Component Page';
          const names = ['John', 'Vladimir', 'Timo'];
    
          const openAlert = () => {
            $f7.dialog.alert('Hello world!');
          }
    
          $on('pageInit', (e, page) => {
            // ページ開始時に何かをする
          });
          $on('pageAfterOut', (e, page) => {
            // ページがビューから離れた
          });
    
          return () => $h`
            <div class="page">
              <div class="navbar">
                <div class="navbar-bg"></div>
                <div class="navbar-inner">
                  <div class="title">${title}</div>
                </div>
              </div>
              <div class="page-content">
                <a @click=${openAlert} class="red-link">Open Alert</a>
                <div class="list simple-list">
                  <ul>
                    ${names.map((name) => $h`
                      <li>${name}</li>
                    `)}
                  </ul>
                </div>
              </div>
            </div>
          `;
        },
      },
      // ...
    ]
    

    コンポーネントのページイベント

    コンポーネントのページイベントハンドラは、$onコンポーネントイベントハンドラで渡すことができます。これらは通常のDOMイベントであるページイベントです。これらはDOMイベントなので、第一引数に event を、第二引数に ページデータ を受け取ります。通常のDOMイベントとの唯一の違いは、イベントハンドラ名がキャメルケース形式で指定されなければならないことです(page:init -> pageInit)。

    const MyComponent = (props, { $on }) => {
      $on('pageMounted', (e, page) => {
        console.log('page mounted');
      });
      $on('pageInit', (e, page) => {
        console.log('page init');
      });
      $on('pageBeforeIn', (e, page) => {
        console.log('page before in');
      });
      $on('pageAfterIn', (e, page) => {
        console.log('page after in');
      });
      $on('pageBeforeOut', (e, page) => {
        console.log('page before out');
      });
      $on('pageAfterOut', (e, page) => {
        console.log('page after out');
      });
      $on('pageBeforeUnmount', (e, page) => {
        console.log('page before unmount');
      });
      $on('pageBeforeRemove', (e, page) => {
        console.log('page before remove');
      });
    }
    

    DOM イベントの処理

    コンポーネントのテンプレートには、追加の @ 属性があることに注意してください。これは、指定された要素にイベントリスナーを割り当てるための、省略可能な方法です。指定されたイベントハンドラは、コンポーネントのスコープ内で検索されます。

    このイベントハンドラ属性の値は関数でなければなりません。

    const MyComponent = (props, { $h, $update }) => {
      let value = 10;
      const addValue = (number) => {
        value += number;
        $update();
      }
      const onClick = () => {
        console.log('click');
      }
    
      return () => $h`
        <div class="page">
          <!-- アトリビュートに関数を渡す -->
          <button @click=${onClick}>Button</button>
    
          <!-- も動作します。 -->
          <button @click=${() => onClick()}>Button</button>
    
          <!-- 属性値 "onClick "が単なる文字列の場合は動作しません。 -->
          <button @click="onClick">Button</button>
    
          <!-- 動的なデータを渡すと期待通りに動作します。 -->
          <button @click=${() => addValue(15)}>Button</button>
        </div>
      `
    }

    イベントハンドラは、最初のレンダリング時、またはVDOMでパッチされた要素に対してのみ処理されます。そのような要素を手動でDOMに追加しても動作しません!

    const MyComponent = (props, { $h, $on }) => {
      const onClick = () => {
        console.log('click');
      }
    
      $on('pageInit', (e, page) => {
        // これは動作しません
        page.$el.append('<a @click="onClick">Link</a>');
      });
    
      return () => $h`
        <div class="page">
        </div>
      `
    }
    

    コンポーネントのルート要素

    コンポーネントのテンプレートやレンダリング関数は、1つのHTML要素のみを返す必要があります。そしてそれは、ルーターでサポートされている要素でなければなりません。

    • ルーターコンポーネントとしてページを読み込む場合、ルーターコンポーネントはPage要素を返さなければなりません。

      <template>
        <div class="page">
          ...
        </div>
      </template>

    • モーダル(Routable Modals)をルーターコンポーネントとして読み込む場合、ルーターコンポーネントはそのモーダル要素を返さなければなりません。

      <template>
        <div class="popup">
          ...
        </div>
      </template>

    • パネル(Routable Panels)をルーターコンポーネントとしてロードした場合、ルーターコンポーネントはPanel要素を返さなければなりません。

      <template>
        <div class="panel panel-left panel-cover">
          ...
        </div>
      </template>

    • タブコンテンツ(Routable Tabs)をルーターコンポーネントとして読み込んだ場合、ルーターコンポーネントは、ルーティング可能なタブの内側に挿入されるタブの子要素を返さなければなりません。

      <template>
        <div class="some-element">
          ...
        </div>
      </template>
      

    シングルファイルコンポーネント

    すべてのコンポーネントのルートを同じルート配列で指定するのは、特にルートがたくさんある場合には、あまり快適ではありません。そこで、componentUrlを使用して、コンポーネントを1つのファイルにまとめることができます。

    routes = [
      ...
      {
        path: '/some-page/',
        componentUrl: './some-page.f7',
      },
      ..
    ];

    そして、some-page.f7に入れます。

    <!-- コンポーネントテンプレートでは、同じタグ付きテンプレートリテラルを使用しています。 -->
    <template>
      <div class="page">
        <div class="navbar">
          <div class="navbar-bg"></div>
          <div class="navbar-inner">
            <div class="title">${title}</div>
          </div>
        </div>
        <div class="page-content">
          <a @click=${openAlert}>Open Alert</a>
          <div class="list simple-list">
            <ul>
              ${names.map((name) => $h`
                <li>${name}</li>
              `)}
            </ul>
          </div>
        </div>
      </div>
    </template>
    <!-- コンポーネントのスタイル -->
    <style>
      .red-link {
        color: red;
      }
    </style>
    <!-- コンポーネントロジックの残りの部分 -->
    <script>
      // スクリプトはコンポーネント関数を返す/エクスポートしなければならない
      export default (props, { $f7, $on }) => {
        const title = 'Component Page';
        const names = ['John', 'Vladimir', 'Timo'];
    
        const openAlert = () => {
          $f7.dialog.alert('Hello world!');
        }
    
        $on('pageInit', () => {
          // ページの初期化で何かをする
        });
        $on('pageAfterOut', () => {
          // ページがビューから離れた
        });
    
        // コンポーネント関数はレンダリング関数を返さなければならない
        return $render;
      }
    </script>

    さて、これでだいぶすっきりしましたね。