MDC-112 Web: integrazione di MDC con framework web

1. Introduzione

logo_components_color_2x_web_96dp.png

I componenti Material (MDC) aiutano gli sviluppatori a implementare Material Design. Creato da un team di ingegneri e progettisti UX di Google, MDC include decine di componenti UI belli e funzionali ed è disponibile per Android, iOS, web e Flutter.material.io/develop

MDC Web è progettato per integrarsi in qualsiasi framework front-end, rispettando i principi di Material Design. Il seguente codelab ti guida nella creazione di un componente React, che utilizza MDC Web come base. I principi appresi in questo codelab possono essere applicati a qualsiasi framework JavaScript.

Come è creato MDC Web

Il livello JavaScript di MDC Web è composto da tre classi per componente: Component, Foundation e Adapter. Questo pattern offre a MDC Web la flessibilità di integrarsi con i framework frontend.

Foundation contiene la logica di business che implementa Material Design. La fondazione non fa riferimento ad alcun elemento HTML. In questo modo, possiamo astrarre la logica di interazione HTML nell'Adapter. Foundation ha un adattatore.

L'adattatore è un'interfaccia. L'interfaccia Adapter viene utilizzata da Foundation per implementare la logica di business di Material Design. Puoi implementare l'adattatore in diversi framework come Angular o React. Un'implementazione di un adattatore interagisce con la struttura DOM.

Il componente ha una base e il suo ruolo è

  1. Implementa l'adattatore utilizzando JavaScript non framework e
  2. Fornisci metodi pubblici che fungono da proxy per i metodi in Foundation.

Cosa offre MDC Web

Ogni pacchetto in MDC Web include un componente, una base e un adattatore. Per creare un'istanza di un componente, devi passare l'elemento principale al metodo costruttore del componente. Il componente implementa un adattatore, che interagisce con il DOM e gli elementi HTML. Il componente istanzia quindi la base, che chiama i metodi dell'adattatore.

Per integrare MDC Web in un framework, devi creare un componente personalizzato nel linguaggio/nella sintassi del framework. Il componente del framework implementa l'adattatore di MDC Web e utilizza la base di MDC Web.

Cosa creerai

Questo codelab mostra come creare un adattatore personalizzato per utilizzare la logica Foundation per ottenere un componente React Material Design. Tratta gli argomenti avanzati presenti in Integrazione nei framework. React viene utilizzato in questo codelab come framework di esempio, ma questo approccio può essere applicato a qualsiasi altro framework.

In questo codelab, creerai la barra dell'app in alto e ricreerai la pagina demo della barra dell'app in alto. Il layout della pagina demo è già configurato, quindi puoi iniziare a lavorare sulla barra delle app nella parte superiore della pagina. La barra dell'app in alto includerà:

  • Icona di navigazione
  • Attività
  • Sono disponibili quattro varianti: breve, sempre compressa, fissa e in evidenza.

Che cosa ti serve:

  • Una versione recente di Node.js (fornita in bundle con npm, un gestore di pacchetti JavaScript)
  • Il codice campione (da scaricare nel passaggio successivo)
  • Conoscenza di base di HTML, CSS, JavaScript e React

Come valuteresti il tuo livello di esperienza nello sviluppo web?

Principiante Intermedio Avanzato

2. Configura l'ambiente di sviluppo

Scarica l'app codelab iniziale

L'app di base si trova nella directory material-components-web-codelabs-master/mdc-112/starter.

… oppure clonalo da GitHub

Per clonare questo codelab da GitHub, esegui i seguenti comandi:

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

Installa le dipendenze del progetto

Dalla directory iniziale material-components-web-codelabs/mdc-112/starter, esegui:

npm install

Vedrai molte attività e, alla fine, il terminale dovrebbe mostrare un'installazione riuscita:

22a33efc2a687408.png

Esegui l'app di base

Nella stessa directory, esegui:

npm start

webpack-dev-server inizierà. Punta il browser su http://localhost:8080/ per visualizzare la pagina.

b55c66dd400cf34f.png

Operazione riuscita. Il codice di avvio per la pagina demo di React della barra delle app superiore dovrebbe essere in esecuzione nel browser. Dovresti vedere un muro di testo lorem ipsum, una casella Controlli (in basso a destra) e una barra dell'app in alto non completata:

4ca3cf6d216f9290.png

Dai un'occhiata al codice e al progetto

Se apri l'editor di codice, la directory del progetto dovrebbe avere un aspetto simile a questo:

e9a3270d6a67c589.png

Apri il file App.js e guarda il metodo render, che include il componente <TopAppBar>:

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>
    );
  }

Questo è il punto di contatto per TopAppBar nell'applicazione.

Apri il file TopAppBar.js, che è una classe React Component di base con un metodo render:

TopAppBar.js

import React from 'react';

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

3. Composizione del componente

In React, il metodo render restituisce l'HTML del componente. Il componente Top App Bar eseguirà il rendering di un tag <header /> e sarà composto da due sezioni principali:

  1. Icona di navigazione e sezione del titolo
  2. Sezione delle icone delle azioni

Se hai domande sugli elementi che compongono la barra delle app superiore, consulta la documentazione su GitHub.

Modifica il metodo render() in TopAppBar.js in modo che abbia il seguente aspetto:

  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>
    );
  }

Questo HTML contiene due elementi di sezione. Il primo contiene un'icona di navigazione e un titolo. La seconda contiene le icone delle azioni.

Poi, aggiungi il metodo 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>
  );
}

Uno sviluppatore importerà TopAppBar nella sua applicazione React e passerà le icone delle azioni all'elemento TopAppBar. Puoi vedere un esempio di codice che inizializza un TopAppBar in App.js.

Il metodo getMergedStyles è mancante, ma viene utilizzato nel metodo render. Aggiungi il seguente metodo JavaScript alla classe TopAppBar:

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

this.classes manca anche dal metodo render, ma verrà trattato in una sezione successiva. Oltre al metodo getter mancante, this.classes, ci sono ancora parti di TopAppBar che devi implementare prima che la barra delle app nella parte superiore possa essere visualizzata correttamente.

I componenti di React ancora mancanti nella barra delle app in alto sono:

  • Una base inizializzata
  • Metodi dell'adattatore da passare al foundation model
  • Markup JSX
  • Gestione delle varianti (fissa, breve, sempre compressa, in evidenza)

L'approccio

  1. Implementa i metodi Adapter.
  2. Inizializza Foundation in componentDidMount.
  3. Chiama il metodo Foundation.destroy in componentWillUnmount.
  4. Stabilisci la gestione delle varianti tramite un metodo getter che combini nomi di classe appropriati.

4. Implementa i metodi dell'adattatore

Il componente JS TopAppBar non framework implementa i seguenti metodi Adapter (elencati in dettaglio qui):

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

Poiché React ha eventi sintetici e best practice e pattern di codifica diversi, i metodi dell'adattatore devono essere reimplementati.

Adapter Getter Method

Nel file TopAppBar.js aggiungi il seguente metodo JavaScript a 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,
  };
}

Le API dell'adattatore per la registrazione degli eventi di scorrimento e ridimensionamento vengono implementate in modo identico alla versione JS non framework, perché React non ha alcun evento sintetico per lo scorrimento o il ridimensionamento e rimanda al sistema di eventi DOM nativo. getViewPortScrollY deve anche rimandare al DOM nativo, poiché è una funzione dell'oggetto window, che non si trova nell'API di React. Le implementazioni dell'adattatore saranno diverse per ogni framework.

Potresti notare che manca this.setStyle, che viene chiamato dal metodo get adapter. Nel file TopAppBar.js, aggiungi il metodo JavaScript mancante alla classe TopAppBar:

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

Hai appena implementato l'adattatore. Tieni presente che a questo punto potresti visualizzare errori nella console perché l'implementazione completa non è ancora terminata. La sezione successiva ti guiderà nell'aggiunta e nella rimozione delle classi CSS.

5. Implementare i metodi del componente

Gestione di varianti e classi

React non dispone di un'API per gestire le classi. Per simulare i metodi di aggiunta/rimozione delle classi CSS di JavaScript nativo, aggiungi la variabile di stato classList. In TopAppBar sono presenti tre parti di codice che interagiscono con le classi CSS:

  1. <TopAppBar /> tramite la proprietà className.
  2. Il metodo Adapter tramite addClass o removeClass.
  3. Codificato in modo permanente nel componente React <TopAppBar />.

Innanzitutto, aggiungi la seguente importazione nella parte superiore di TopAppBar.js, sotto le importazioni esistenti:

import classnames from 'classnames';

Quindi aggiungi il seguente codice all'interno della dichiarazione di classe del componente 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,
    });
  }

  ... 
}

Se vai alla pagina http://localhost:8080, le caselle di controllo Controls ora dovrebbero attivare/disattivare i nomi delle classi del DOM.

Questo codice rende TopAppBar utilizzabile da molti sviluppatori. Gli sviluppatori possono interagire con l'API TopAppBar senza preoccuparsi dei dettagli di implementazione delle classi CSS.

Ora hai implementato correttamente l'adattatore. La sezione successiva ti guiderà nella creazione di un Foundation.

Montaggio e smontaggio del componente

L'istanza di Foundation avviene nel metodo componentDidMount.

Innanzitutto, importa le basi della barra delle app superiore MDC aggiungendo la seguente importazione dopo le importazioni esistenti in TopAppBar.js:

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

Poi, aggiungi il seguente codice JavaScript alla classe 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();
  }
 
  ... 

}

Una buona pratica di codifica React è definire propTypes e defaultProps. Aggiungi la seguente importazione dopo quelle esistenti in TopAppBar.js:

import PropTypes from 'prop-types';

Quindi aggiungi il seguente codice in fondo a TopAppBar.js (dopo la classe 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,
};

Hai implementato correttamente il componente React della barra delle app superiore. Se vai alla pagina http://localhost:8080, puoi provare la pagina dimostrativa. La pagina demo funzionerà come la pagina demo di MDC Web. La pagina demo dovrebbe avere questo aspetto:

3d983b98c2092e7a.png

6. Conclusione

In questo tutorial abbiamo spiegato come eseguire il wrapping di Foundation di MDC Web per l'utilizzo in un'applicazione React. Su GitHub e npm sono disponibili alcune librerie che eseguono il wrapping dei componenti web MDC, come descritto in Integrazione nei framework. Ti consigliamo di utilizzare l'elenco disponibile qui. Questo elenco include anche altri framework oltre a React, come Angular e Vue.

Questo tutorial illustra la nostra decisione di dividere il codice MDC Web in tre parti: Foundation, Adapter e Component. Questa architettura consente ai componenti di condividere codice comune durante l'utilizzo di tutti i framework. Grazie per aver provato Material Components React. Dai un'occhiata alla nostra nuova libreria MDC React. Ci auguriamo che questo codelab ti sia piaciuto.

Sono riuscito a completare questo codelab con un ragionevole dispendio di tempo e impegno

Totalmente d'accordo D'accordo Indifferente In disaccordo Totalmente in disaccordo

Vorrei continuare a utilizzare i componenti Material in futuro

Totalmente d'accordo D'accordo Indifferente In disaccordo Totalmente in disaccordo