MDC-112 Web: MDC とウェブ フレームワークの統合

MDC-112 Web:
MDC とウェブ フレームワークの統合

この Codelab について

subject最終更新: 10月 11, 2020
account_circle作成者: Liz Mitchell, Abhinay Omkar

1. はじめに

logo_components_color_2x_web_96dp.png

マテリアル コンポーネント(MDC)は、デベロッパーがマテリアル デザインを実装する際に役立ちます。Google のエンジニアと UX デザイナーのチームが作成した MDC には、美しく機能的な UI コンポーネントが多数含まれており、Android、iOS、ウェブ、Flutter.material.io/develop に利用可能です。

MDC ウェブは、マテリアル デザインの原則を守りつつ、あらゆるフロントエンド フレームワークに統合できるように設計されています。次の Codelab では、MDC Web を基盤として使用する React コンポーネントの作成方法について説明します。この Codelab で学んだ原則は、あらゆる JavaScript フレームワークに適用できます。

MDC ウェブの構築

MDC ウェブの JavaScript レイヤは、コンポーネントごとに ComponentFoundationAdapter の 3 つのクラスで構成されます。このパターンにより、MDC Web はフロントエンド フレームワークと柔軟に統合できます。

Foundation にはマテリアル デザインを実装するビジネス ロジックが含まれています。本財団は、HTML 要素を参照しません。これにより、HTML インタラクション ロジックを Adapter に抽象化できます。Foundation には Adapter があります。

Adapter はインターフェースです。Adapter インターフェースは、マテリアル デザインのビジネス ロジックを実装するために Foundation から参照されます。Adapter は、Angular や React などのさまざまなフレームワークで実装できます。Adapter の実装は DOM 構造と相互作用します。

コンポーネントには基盤があり、その役割は

  1. フレームワーク以外の JavaScript を使用して Adapter を実装します。
  2. 基盤のメソッドにプロキシするパブリック メソッドを提供します。

MDC Web が提供する機能

MDC Web の各パッケージには、コンポーネント基盤アダプターが付属しています。Component をインスタンス化するには、ルート element をコンポーネントのコンストラクタ メソッドに渡す必要があります。Component は DOM 要素や HTML 要素とやり取りする Adapter を実装しています。次に、ComponentFoundation をインスタンス化し、Adapter メソッドを呼び出す。

MDC Web をフレームワークに統合するには、フレームワークの言語と構文で独自のコンポーネントを作成する必要があります。フレームワーク Component は MDC Web の Adapter を実装し、MDC Web の Foundation を使用します。

作成するアプリの概要

この Codelab では、Foundation ロジックを使用してマテリアル デザインの React コンポーネントを実現するカスタム Adapter の作成方法を示します。ここでは、フレームワークへの統合にある高度なトピックを扱います。この Codelab ではフレームワークの例として React を使用していますが、このアプローチは他のフレームワークにも適用できます。

この Codelab では、トップ アプリバーを作成し、トップ アプリバーのデモページを再作成します。デモページのレイアウトはすでに設定されているため、トップアプリバーの作業を開始できます。トップ アプリバーには次のものが含まれます。

  • ナビゲーション アイコン
  • アクション アイテム
  • ショート動画には、ショート動画、常に折りたたまれた、固定、目立つという 4 つのパターンがあります。

必要なもの:

  • Node.js の最新バージョン(JavaScript パッケージ マネージャーである npm にバンドルされています)
  • サンプルコード(次のステップでダウンロード)
  • HTML、CSS、JavaScript、React に関する基本的な知識
<ph type="x-smartling-placeholder">をご覧ください。

ウェブ開発の経験についてお答えください。

2. 開発環境を設定する

Codelab のスターター アプリをダウンロードする

スターター アプリは material-components-web-codelabs-master/mdc-112/starter ディレクトリ内にあります。

GitHub からクローンを作成する

GitHub からこの Codelab のクローンを作成するには、次のコマンドを実行します。

git clone https://github.com/material-components/material-components-web-codelabs
cd material
-components-web-codelabs/mdc-112/starter

プロジェクトの依存関係をインストールする

スターター ディレクトリ material-components-web-codelabs/mdc-112/starter から、次のコマンドを実行します。

npm install

多くのアクティビティが表示され、最終的に、正常にインストールされたことを示すメッセージがターミナルに表示されます。

22a33efc2a687408.png

スターター アプリを実行する

同じディレクトリで次のコマンドを実行します。

npm start

webpack-dev-server が起動します。ブラウザで http://localhost:8080/ にアクセスしてページを表示します。

b55c66dd400cf34f.png

完了しました。トップ アプリバーの React デモページのスターター コードがブラウザで実行されているはずです。lorem ipsum というテキストで囲まれた部分、[Controls] ボックス(右下)、未完成のトップ アプリバーが表示されます。

4ca3cf6d216f9290.png

コードとプロジェクトを確認します。

コードエディタを開くと、プロジェクト ディレクトリは次のようになります。

e9a3270d6a67c589.png

App.js ファイルを開き、<TopAppBar> コンポーネントを含む render メソッドを確認します。

App.js

render() {
    const {isFixed, isShort, isRtl, isProminent, isAlwaysCollapsed, shouldReinit} = this.state;

    return (
      <section
        dir={isRtl ? 'rtl' : 'ltr'}
        className='mdc-typography'>
        {
          shouldReinit ? null :
          <TopAppBar
            navIcon={this.renderNavIcon()}
            short={isShort}
            prominent={isProminent}
            fixed={isFixed}
            alwaysCollapsed={isAlwaysCollapsed}
            title='Mountain View, CA'
            actionItems={this.actionItems}
          />
        }
        <div className={classnames('mdc-top-app-bar--fixed-adjust', {
          'mdc-top-app-bar--short-fixed-adjust': isShort || isAlwaysCollapsed,
          'mdc-top-app-bar--prominent-fixed-adjust': isProminent,
        })}>
          {this.renderDemoParagraphs()}
        </div>

        {this.renderControls()}
      </section>
    );
  }

これは、アプリケーションの TopAppBar のエントリ ポイントです。

render メソッドでファイル TopAppBar.js を開きます。これはベアの React Component クラスです。

TopAppBar.js

import React from 'react';

export default class TopAppBar extends React.Component {
 
render() {
   
return (
     
<header>
       
TOP APP BAR
     
</header>
   
);
 
}
}

3. コンポーネントの構成

React では、render メソッドはコンポーネントの HTML を出力します。トップ アプリバー コンポーネントは <header /> タグをレンダリングします。このコンポーネントは次の 2 つのメイン セクションで構成されます。

  1. ナビゲーション アイコンとタイトル セクション
  2. アクション アイコンのセクション

トップ アプリバーを構成する要素についてご不明な点がある場合は、GitHub のドキュメントをご覧ください。

TopAppBar.jsrender() メソッドを次のように変更します。

  render() {
    const {
      title,
      navIcon,
    } = this.props;

    return (
      <header
        className={this.classes}
        style={this.getMergedStyles()}
        ref={this.topAppBarElement}
      >
        <div className='mdc-top-app-bar__row'>
          <section className='mdc-top-app-bar__section mdc-top-app-bar__section--align-start'>
            {navIcon ? navIcon : null}
            <span className="mdc-top-app-bar__title">
              {title}
            </span>
          </section>
          {this.renderActionItems()}
        </div>
      </header>
    );
  }

この HTML には 2 つのセクション要素があります。1 つ目はナビゲーション アイコンとタイトルを含みます。2 つ目はアクション アイコンです。

次に、renderActionItems メソッドを追加します。

renderActionItems() {
  const {actionItems} = this.props;
  if (!actionItems) {
    return;
  }

  return (
    <section className='mdc-top-app-bar__section mdc-top-app-bar__section--align-end' role='toolbar'>
      {/* need to clone element to set key */}
      {actionItems.map((item, key) => React.cloneElement(item, {key}))}
    </section>
  );
}

デベロッパーは、TopAppBar を React アプリケーションにインポートし、アクション アイコンを TopAppBar 要素に渡します。TopAppBar を初期化するサンプルコードを App.js に示します。

render メソッドで使用されている getMergedStyles メソッドがありません。次の JavaScript メソッドを TopAppBar クラスに追加してください。

getMergedStyles = () => {
  const {style} = this.props;
  const {style: internalStyle} = this.state;
  return Object.assign({}, internalStyle, style);
}

this.classesrender メソッドにはありませんが、後のセクションで説明します。トップ アプリバーを正しくレンダリングするには、欠落しているゲッター メソッド this.classes 以外にも、TopAppBar を実装する必要があります。

React Component がトップ アプリバーにまだ表示されていない部分は次のとおりです。

  • 初期化された基盤
  • 基盤に渡すアダプター メソッド
  • JSX マークアップ
  • パターン管理(固定、短い、常に閉じている、目立つ)

手法

  1. Adapter メソッドを実装します。
  2. componentDidMountFoundation を初期化します。
  3. componentWillUnmountFoundation.destroy メソッドを呼び出します。
  4. 適切なクラス名を組み合わせたゲッター メソッドでバリアント管理を確立します。

4. Adapter メソッドを実装する

フレームワーク以外の JS TopAppBar コンポーネントは、次の Adapter メソッドを実装します(詳細についてはこちらをご覧ください)。

  • hasClass()
  • addClass()
  • removeClass()
  • registerNavigationIconInteractionHandler()
  • deregisterNavigationIconInteractionHandler()
  • notifyNavigationIconClicked()
  • setStyle()
  • getTopAppBarHeight()
  • registerScrollHandler()
  • deregisterScrollHandler()
  • registerResizeHandler()
  • deregisterResizeHandler()
  • getViewportScrollY()
  • getTotalActionItems()

React には合成イベントがあり、さまざまなベスト コーディング プラクティスやパターンがあるため、Adapter メソッドを再実装する必要があります。

アダプタ ゲッター メソッド

TopAppBar.js ファイルで、次の JavaScript メソッドを TopAppBar に追加します。

get adapter() {
  const {actionItems} = this.props;

  return {
    hasClass: (className) => this.classes.split(' ').includes(className),
    addClass: (className) => this.setState({classList: this.state.classList.add(className)}),
    removeClass: (className) => {
      const {classList} = this.state;
      classList.delete(className);
      this.setState({classList});
    },
    setStyle: this.setStyle,
    getTopAppBarHeight: () => this.topAppBarElement.current.clientHeight,
    registerScrollHandler: (handler) => window.addEventListener('scroll', handler),
    deregisterScrollHandler: (handler) => window.removeEventListener('scroll', handler),
    registerResizeHandler: (handler) => window.addEventListener('resize', handler),
    deregisterResizeHandler: (handler) => window.removeEventListener('resize', handler),
    getViewportScrollY: () => window.pageYOffset,
    getTotalActionItems: () => actionItems && actionItems.length,
  };
}

React にはスクロールやサイズ変更の合成イベントがなく、ネイティブの DOM イベント システムに従うため、スクロールとサイズ変更のイベント登録用のアダプタ API は、フレームワーク以外の JS バージョンと同じように実装されます。また、getViewPortScrollYwindow オブジェクトの関数ですが、React の API には存在しません。アダプタの実装はフレームワークごとに異なります。

get adapter メソッドによって呼び出される this.setStyle がないことがわかります。TopAppBar.js ファイルで、不足している JavaScript メソッドを TopAppBar クラスに追加します。

setStyle = (varName, value) => {
  const updatedStyle = Object.assign({}, this.state.style);
  updatedStyle[varName] = value;
  this.setState({style: updatedStyle});
}

Adapter が実装されました。完全な実装はまだ完了していないため、この時点でコンソールにエラーが表示される場合があります。次のセクションでは、CSS クラスを追加、削除する方法を説明します。

5. コンポーネント メソッドを実装する

バリアントとクラスの管理

React には、クラスを管理するための API はありません。ネイティブ JavaScript の CSS クラスの追加と削除を模倣するには、classList 状態変数を追加します。TopAppBar には、CSS クラスとやり取りする次の 3 つのコードがあります。

  1. className プロパティを介した <TopAppBar /> コンポーネント。
  2. addClass または removeClass による Adapter メソッド。
  3. <TopAppBar /> React コンポーネント内にハードコードする。

まず、TopAppBar.js の先頭の既存のインポートの下に次のインポートを追加します。

import classnames from 'classnames';

次に、TopAppBar コンポーネントのクラス宣言内に次のコードを追加します。

export default class TopAppBar extends React.Component {
  constructor(props) {
    super(props);
    this.topAppBarElement = React.createRef();
  }

  state = {
    classList: new Set(),
    style: {},
  };

  get classes() {
    const {classList} = this.state;
    const {
      alwaysCollapsed,
      className,
      short,
      fixed,
      prominent,
    } = this.props;

    return classnames('mdc-top-app-bar', Array.from(classList), className, {
      'mdc-top-app-bar--fixed': fixed,
      'mdc-top-app-bar--short': short,
      'mdc-top-app-bar--short-collapsed': alwaysCollapsed,
      'mdc-top-app-bar--prominent': prominent,
    });
  }

  ...
}

http://localhost:8080 にアクセスすると、[コントロール] チェックボックスで DOM のクラス名のオン/オフが切り替わるはずです。

このコードにより、多くのデベロッパーが TopAppBar を使用できるようになります。デベロッパーは、CSS クラスの実装の詳細を気にせずに、TopAppBar API を操作できます。

これで、Adapter が正常に実装されました。次のセクションでは、Foundation をインスタンス化する手順について説明します。

コンポーネントのマウントとマウント解除

Foundation のインスタンス化は componentDidMount メソッドで行われます。

まず、TopAppBar.js の既存のインポートの後に次のインポートを追加して、MDC トップ アプリバー基盤をインポートします。

import {MDCTopAppBarFoundation, MDCFixedTopAppBarFoundation, MDCShortTopAppBarFoundation} from '@material/top-app-bar';

次に、次の JavaScript コードを TopAppBar クラスに追加します。

export default class TopAppBar extends React.Component {
 
  ...

  foundation_ = null;

  componentDidMount() {
    this.initializeFoundation();
  }

  componentWillUnmount() {
    this.foundation_.destroy();
  }

  initializeFoundation = () => {
    if (this.props.short) {
      this.foundation_ = new MDCShortTopAppBarFoundation(this.adapter);
    } else if (this.props.fixed) {
      this.foundation_ = new MDCFixedTopAppBarFoundation(this.adapter);
    } else {
      this.foundation_ = new MDCTopAppBarFoundation(this.adapter);
    }

    this.foundation_.init();
  }
 
  ...

}

React のコーディング プラクティスとしては、propTypes と defaultProps を定義することをおすすめします。TopAppBar.js の既存のインポートの後に、次のインポートを追加します。

import PropTypes from 'prop-types';

次に、TopAppBar.js の一番下(Component クラスの後)に次のコードを追加します。

import PropTypes from 'prop-types';

TopAppBar.propTypes = {
 
alwaysCollapsed: PropTypes.bool,
 
short: PropTypes.bool,
 
fixed: PropTypes.bool,
 
prominent: PropTypes.bool,
 
title: PropTypes.string,
 
actionItems: PropTypes.arrayOf(PropTypes.element),
 
navIcon: PropTypes.element,
};

TopAppBar.defaultProps = {
 
alwaysCollapsed: false,
 
short: false,
 
fixed: false,
 
prominent: false,
 
title: '',
 
actionItems: null,
 
navIcon: null,
};

これで、トップ アプリバー React コンポーネントの実装が完了しました。http://localhost:8080 に移動したら、デモページで操作できます。デモページは、MDC ウェブのデモページと同じように機能します。デモページは次のようになります。

3d983b98c2092e7a.png

6. まとめ

このチュートリアルでは、React アプリケーションで使用するために MDC Web の Foundation をラップする方法について説明しました。フレームワークへの統合で説明されているように、GitHub と npm には MDC Web Components をラップするライブラリがいくつかあります。こちらのリストをご使用になることをおすすめします。このリストには、Angular や Vue など、React 以外のフレームワークも含まれています。

このチュートリアルでは、MDC ウェブコードを基盤アダプタコンポーネントの 3 つに分割することにしました。このアーキテクチャにより、コンポーネントはすべてのフレームワークで動作しながら共通のコードを共有できます。Material Components React をお試しいただきありがとうございます。新しいライブラリ MDC React もぜひご利用ください。この Codelab がお役に立ちましたら幸いです。

<ph type="x-smartling-placeholder">をご覧ください。

この Codelab を完了するためにそれなりの時間と労力を必要とした

今後もマテリアル コンポーネントを使用したい