How to use Material Web Components with Contact Form 7
Tags: wordpress, php, webpack, javascript
Published: 2020-12-10 06:55:06

### Goal
Replace the default fields that Contact Form 7 (CF7) outputs with our desired Material Web Component (MWC) components.

### Requirements
A module bundler such as webpack or rollup.

### Method
In your plugin or theme, create the following folder structure:
```
mwc
├── mwc.php
├── mwc.js
├── _mwc.scss
└── modules
    ├── text.php
    └── textarea.php
```

You should copy any modules you want from `wp-content/plugins/contact-form-7/modules`. Here I've just used text and textarea.

As early as possible in the wordpress lifecycle require your `mwc/mwc.php` file. Then in the file, require all your modules like so:

```
require_once __DIR__ . '/modules/text.php';
require_once __DIR__ . '/modules/textarea.php';
```

In each of your `modules/X.php` files, where X is the module name:

1. Delete everything after the `wpcf7_X_form_tag_handler` function (we don't need to change validation or the tag generator menu)

2. 

| Step | Search for | Replace with |
| ---- | ---------- | ------------ |
| 1 | `wpcf7_add_form_tag_X` | `mwc_wpcf7_add_form_tag_X` |
| 2 | `wpcf7_X_form_tag_handler` | `mwc_wpcf7_X_form_tag_handler` |
| 3 | `$atts['placeholder'] = $value;` | `$atts['label'] = $value;` |
| 4 | `input` | `mwc-textfield` (or whatever your component is) |
| 5 | `%3$s` | `<input type="hidden" name="' . $tag->name  . '" />%3$s` |


<span>3.</span> At the top of the file add:
```remove_action('wpcf7_init', 'wpcf7_add_form_tag_X');```

At this point we should be able to make a CF7 template such as:

```
[text* full-name placeholder "Your Name"]
[email* email placeholder "Email address"]
[tel phone placeholder "Contact number (optional)"]
[textarea* message x4 placeholder "Your message"]
[submit "Submit]
```

However the fields won't be rendering because we haven't imported the web components yet. To fix that, install your desired components:

```js
npm install @material/mwc-textfield
npm install @material/mwc-textarea
```

And then in your `mwc/mwc.js` file, add:

```js
import '@material/mwc-textfield'
import '@material/mwc-textarea'

export default () => {
  const form = document.querySelector('.wpcf7-form')
  if (form) {
    form.querySelectorAll('.wpcf7-form-control-wrap').forEach(formControlWrap => {
      const formControl = formControlWrap.querySelector('.wpcf7-form-control')
      // If is web component
      if (formControl.tagName.includes('-')) {
        const input = formControlWrap.querySelector('input[type="hidden"]')
        // Make sure the hidden input value stays in sync with the mwc value
        formControl.addEventListener('change', () => {
          input.value = formControl.value
        })
      }
    })
  }
}
```

The reason we've had to add an extra hidden input and keep its value in sync with our mwc component value is because currently [there is no support for web components to submit their values in forms](https://github.com/WICG/webcomponents/issues/187). This is a very hacky solution, but does the job.

Now in your `index.js` file:
```js
import MWC from 'mwc/mwc'

document.addEventListener("DOMContentLoaded", () => {
  MWC()
})
```

And you should have working Material Web Components with Contact Form 7! :D

If you want to style your components, see the [themeing guide](https://github.com/material-components/material-components-web-components/blob/master/docs/theming.md).

Unfortunately I haven't tested this on other mwc components, so please let us know in the comments what other components you implemented and any gotchas that you had to work around.

Enjoy!