1

I want to develop a custom block that will let the user pick some information from autocomplete. I manage to create the autocomplete component on edit function.

A user can select an item but i don't know how to handle the attribute save.

I'm trying to save the selected item as attribute package_name. I created the onChange function on Autocomplete component but event.target.value is undefined.

Here is my code from block.js

const { __ } = wp.i18n; // Import __() from wp.i18n
const {  AlignmentToolbar,
        BlockControls,
        registerBlockType } = wp.blocks; 
const { RichText } = wp.editor;
const { Autocomplete, } =wp.components;


const MyAutocomplete = () => {
    const autocompleters = [
        {
            name: 'fruit',   
            triggerPrefix: '@',   
            options: [
                {  name: 'Apple', id: 1 },
                {  name: 'Orange', id: 2 },
                {  name: 'Grapes', id: 3 },
                {  name: 'test', id: 4 },
            ],

            getOptionLabel: option => (
                <span>
                  { option.name }
                </span>
            ),

            getOptionKeywords: option => [ option.name ],

            isOptionDisabled: option => option.name === 'Grapes',

            getOptionCompletion: option => (
                <abbr title={ option.name }>{ option.name }</abbr>
            ),
        }
    ];

    function onChangeAuto(newContent){
        console.log('autocompletexx '+newContent);
    }

    function onSelectAuto(event){
        console.log(event.target);
           console.log( event.target.value);
    }

    return (
        <div>
            <Autocomplete completers={ autocompleters }>
                { ( { isExpanded, listBoxId, activeId } ) => (
                    <div
                        contentEditable
                        suppressContentEditableWarning
                        aria-autocomplete="list"
                        aria-expanded={ isExpanded }
                        aria-owns={ listBoxId }
                        aria-activedescendant={ activeId }
                        onChange={onChangeAuto }
                        onSelect={onSelectAuto}

                    >
                    </div>
                ) }
            </Autocomplete>
            <p class="autocomplete_p">Type @ for triggering the autocomplete.</p>
        </div>
    );
};




registerBlockType( 'residence-gutenberg-block/membership-package-settings', {
    title: __( 'Residence - Membership Package Settings' ), // Block title.
    icon: 'shield', 
    category: 'common', 
    keywords: [
        __( 'membership-package-settings' ),
    ],
        attributes:{
            package_id:{
                type:'string',
                select:'p'
            },
            package_name:{
                type:'string',
            },
        },


    edit: function( props ) {

                const { attributes: {package_name}, className,setAttributes,isSelected } = props;               
        return (
            <div className={ props.className }>
                            <form>
                                <label className="wpresidence_editor_label">Current Package: {package_name}</label>
                                <MyAutocomplete></MyAutocomplete>
                             </form>

            </div>

        );
    },

    save: function( props ) {
              // Rendering in PHP
                return null;

    },
} );
Crerem
  • 1,289
  • 2
  • 18
  • 45

1 Answers1

1

Passing down onChange, onSelect to the div element won't work, because these attributes are only applicable to the form field elements (as input, select, etc.).

I checked the documentation and the source code and didn't find any details or official approaches for dealing with the case.

However, I'm seeing two possible approaches for getting the selected value:

1. Using Autocomplete onReplace prop

Looking into the Autocomplete's source code, I noticed that onSelect callback invokes onReplace prop with the selected option as array. It may not fit all the cases, but you can give it a try! It may be enough for your case! You can try to add your handler to the onReplace as follows:

<Autocomplete
  onReplace={ option => { console.log(option) } }
  completers={ autocompleters }>
  { ( { isExpanded, listBoxId, activeId } ) => (
    <div
      contentEditable
      suppressContentEditableWarning
      aria-autocomplete="list"
      aria-expanded={ isExpanded }
      aria-owns={ listBoxId }
      aria-activedescendant={ activeId }
    />
  ) }
</Autocomplete>

2. Listen for <div /> changes manually

You can add onInput, onBlur listeners to the <div />, having an uncontrolled react div component and when the div's value is changed then we can keep the changed value in your parent component's state.

Here's a great discussion, which describes these technique: React.js: onChange event for contentEditable

The good think is that there's already a plugin (based on this discussion) that can do it for you: react-contenteditable.

Firstly you have to convert your <MyAutocomplete /> component to be a statefull (not functional) and then:

import ContentEditable from 'react-contenteditable'

// Boilerplate ...

<Autocomplete completers={ autocompleters }>
  { ( { isExpanded, listBoxId, activeId } ) => (
    <ContentEditable
      html={this.state.html}
      onChange={this.handleChange}
      contentEditable
      suppressContentEditableWarning
      aria-autocomplete="list"
      aria-expanded={ isExpanded }
      aria-owns={ listBoxId }
      aria-activedescendant={ activeId }
    />
  ) }
</Autocomplete>

Conclusion

I'm surprised that in the Autocomplete's documentation there aren't any details for this case. I guess it's because of the following statement (27.08.2018):

Gutenberg is being developed on GitHub, and you can try an early beta version today from the plugin repository. Though keep in mind it’s not fully functional, feature complete, or production ready.

However, one of both mentioned approaches above will help you, until they provide a complete API to work with their components.

I would suggest you to keep wrapping the Wordpress's <Autocomplete /> with your own component - in order to easily refactor your code later, when they release the complete API.

If you have questions, feel free to comment below.

Jordan Enev
  • 16,904
  • 3
  • 42
  • 67
  • Thank you for taking the time to reply. Even if the answer didn't include the exact answer, the info and tips you shared helped me find a solution. Thanks again. – Crerem Aug 28 '18 at 08:11
  • You're welcome and glad to help you. Can you please share details here - how did you resolve your case and I will include them to the answer, in order to help other developers too. Thank you too! :) – Jordan Enev Aug 28 '18 at 08:16