Gutenberg richtext block vaildation failed
-
20-05-2021 - |
Question
I have a problem with understanding richtext elements. I am trying to create form with customizable placeholder and label, but when I save and refresh page i get this error:
blocks.min.js?ver=9ed25ffa009c799f99a4340915b6dc6a:3 Block validation: Block validation failed for `kathy/contact-form` ({name: "kathy/contact-form", icon: {…}, keywords: Array(0), attributes: {…}, providesContext: {…}, …}).
Content generated by `save` function:
<form class="wp-block-kathy-contact-form kathyContactForm"><label class="kathyContactFormEmailLabel flexCenterCenter"><span class="kathyContactFormEmailLabelText flexCenterCenter">x</span><input class="kathyContactFormEmail flexCenterCenter" placeholder=""/></label></form>
Content retrieved from post body:
<form class="wp-block-kathy-contact-form kathyContactForm"><label class="kathyContactFormEmailLabel flexCenterCenter"><span class="kathyContactFormEmailLabelText flexCenterCenter">x</span><input class="kathyContactFormEmail flexCenterCenter" placeholder="z"/></label></form>
Content is saved and correctly displayed on the site, but editor is showing errors.
My js:
registerBlockType('kathy/contact-form', {
title: 'Kathy Contact Form',
description: 'Contact form',
icon: 'format-image',
category: 'kathy',
attributes: {
emailLabel: {
type: 'string',
source: 'html',
selector: 'span.kathyContactFormEmailLabelText'
},
emailPlaceholder: {
type: 'string',
source: 'html',
selector: 'span.kathyContactFormEmail'
}
},
edit: ({attributes, setAttributes}) => {
const {emailLabel, emailPlaceholder, nameLabel, namePlaceholder, messageLabel, messagePlaceholder, submitText} = attributes;
const updateEl = (el, val) => {
switch(el) {
case "emailLabel":
setAttributes({emailLabel: val});
break;
case "emailPlaceholder":
console.log(val)
setAttributes({emailPlaceholder: val});
break;
}
}
return ([
<form className={'kathyContactForm'}>
<label>
<RichText className={'kathyContactFormEmailLabelText flexCenterCenter'} key="editable"
tagName="span" placeholder="Label text" value={emailLabel}
onChange={(val) => updateEl("emailLabel", val)} />
<RichText className={'kathyContactFormEmail flexCenterCenter'} key="editable"
tagName="span" placeholder="Placeholder text" value={emailPlaceholder}
onChange={(val) => updateEl("emailPlaceholder", val)} />
</label>
</form>
]);
},
save: ({attributes}) => {
const {emailLabel, emailPlaceholder} = attributes;
return (
<form className={'kathyContactForm'}>
<label className={"kathyContactFormEmailLabel flexCenterCenter"}>
<span className={"kathyContactFormEmailLabelText flexCenterCenter"}>{emailLabel}</span>
<input className={"kathyContactFormEmail flexCenterCenter"}
placeholder={emailPlaceholder}
/>
</form>
);
}
});
I know (or I think I know) error is showed because of span in edit and input in save, but when I replaced span to input in edit and attributes - no error is showed, but updating doesn't work. How can I fix it?
Solution
The problem in your block is due to the selector
value of your emailPlaceholder
attribute — you set the value to span.kathyContactFormEmail
, but there's actually no span element with that class in your save()
function (or the markup/HTML of the element returned by the function).
What you have though, is an input with kathyContactFormEmail
as the class, so you would need to change the emailPlaceholder
's selector to input.kathyContactFormEmail
.
And in addition to that, you should also change the source from html
to attribute
and set attribute
to placeholder
like so:
emailPlaceholder: {
type: 'string',
source: 'attribute', // change it from 'html' to 'attribute'
attribute: 'placeholder', // add add this
selector: 'input.kathyContactFormEmail'
}
Firstly, because you're using the attribute with the input's placeholder, i.e. <input placeholder="{ emailPlaceholder }" />
.
And secondly, input
elements are self-closing, i.e. <input />
and not <input></input>
, hence their innerHTML
value is empty.
So html
source won't work with inputs because the block editor will read the attribute value from the inner HTML and yet with an <input />
(or even with <input>foo</input>
which is invalid..), the editor will get an empty string.
Additional Issues/Considerations
Instead of using a
RichText
element for editing theemailPlaceholder
attribute, I would use a simple text field, e.g. usingTextControl
like so:<TextControl label="Placeholder text" value={ emailPlaceholder } onChange={ ( val ) => updateEl( 'emailPlaceholder', val ) } />
PS: Don't forget to import or load the component, e.g.
const { TextControl } = wp.components;
.And if you use that component, then you would want to use a
div
, Fragment (<>
and</>
) or something other thanlabel
to wrap theRichText
andTextControl
elements, e.g.<div><RichText .../><TextControl .../></div>
.The
RichText
reference says:RichText.Content
should be used in thesave
function of your block to correctly save rich text content.Therefore, instead of
<span className={"kathyContactFormEmailLabelText flexCenterCenter"}>{emailLabel}</span>
, you should do:<RichText.Content tagName="span" className="kathyContactFormEmailLabelText flexCenterCenter" value={ emailLabel } />
For string literals or non-dynamic attribute values, you should just use the
<attribute>="<value>"
syntax, so for example instead ofclassName={'kathyContactForm'}
, useclassName="kathyContactForm"
. That way, your code will become simpler.In your
edit()
function, I don't know why are you returning an array, i.e.return ([<form>...</form>])
, but you should set a uniquekey
prop for each outermost parent element (e.g. if you had<p><b>foo <i>bar</i></b> baz</p>
, the outermost parent isp
). So for example, you would doreturn ([<form key="your-key">...</form>])
.And make sure to use unique keys — the two
RichText
elements in youredit()
function used the same key ("editable").