0

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?

2
  • Won't the form and input tags be destroyed when you save by the post content security filters? Even if your block succeeded it'd be stripped out in PHP. There's a reason contact forms don't save form tags directly into the database, and use shortcodes, or dynamically render themselves in PHP. Also your blocks save output differs from what it expected, did you try to create an instance of that block, then changed the block to add an attribute but did not update the content?
    – Tom J Nowell
    May 2 at 0:33
  • I don't know exactly what you mean. Block saves properly, I can see on website my form with input with proper placeholder, but on edit page I see that error when I resfresh or enter edit page. May 2 at 1:00
1

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

  1. Instead of using a RichText element for editing the emailPlaceholder attribute, I would use a simple text field, e.g. using TextControl 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 than label to wrap the RichText and TextControl elements, e.g. <div><RichText .../><TextControl .../></div>.

  2. The RichText reference says:

    RichText.Content should be used in the save 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 }
    />
    
  3. For string literals or non-dynamic attribute values, you should just use the <attribute>="<value>" syntax, so for example instead of className={'kathyContactForm'}, use className="kathyContactForm". That way, your code will become simpler.

  4. In your edit() function, I don't know why are you returning an array, i.e. return ([<form>...</form>]), but you should set a unique key prop for each outermost parent element (e.g. if you had <p><b>foo <i>bar</i></b> baz</p>, the outermost parent is p). So for example, you would do return ([<form key="your-key">...</form>]).

    And make sure to use unique keys — the two RichText elements in your edit() function used the same key ("editable").

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.