Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Simple Autocomplete component for WP Gutenberg
/**
* A very simple autocomplete component
*
* This is to replace the OOTB Gutenberg Autocomplete component because it is
* currently broken as of v4.5.1.
*
* See Github issue: https://github.com/WordPress/gutenberg/issues/10542
*
* Note: The options array should be an array of objects containing labels and values; i.e.:
* [
* { value: 'first', label: 'First' },
* { value: 'second', label: 'Second' }
* ]
*/
// Load external dependency.
import { isEmpty } from 'lodash';
function MyAutocomplete( {
label,
id,
value,
onChange,
options = [],
} ) {
// Construct a unique ID for this block.
const blockId = `my-autocomplete-${ id }`;
// Function to handle the onChange event.
const onChangeValue = ( event ) => {
onChange( event.target.value );
};
// Return the block, but only if options were passed in.
return ! isEmpty( options ) && (
<div>
{ /* Label for the block. */ }
<label for={ blockId }>
{ label }
</label>
{ /* Input field. */ }
<input
list={ blockId }
value={ value }
onChange={ onChangeValue }
/>
{ /* List of all of the autocomplete options. */ }
<datalist id={ blockId }>
{ options.map( ( option, index ) =>
<option value={ option.value } label={ option.label } />
) }
</datalist>
</div>
);
};
export default MyAutocomplete;
@simonhammes

This comment has been minimized.

Copy link

commented Jul 26, 2019

Thank you very much for uploading this gist! The issue with the ootb autocomplete functionality seems to persist. Unfortunately, I am unable to integrate your code into my own project. Could you upload a working example or explain how this snippet works with (let's say) a TextControl field?

@thatdevgirl

This comment has been minimized.

Copy link
Owner Author

commented Jul 26, 2019

Sure! So, this gist is registering a new component. It was built to work on its own, not inside a TextControl field or the like. Here is an example:

import MyAutocomplete from 'simple-autocomplete.js'; // adjust if you saved this file somewhere else.

( function() {
  const options = [ 
    { 'value': 1, 'label': 'First option' }
    { 'value': 2, 'label': 'Second option' }
  ];

  registerBlockType( 'my/custom_block', {
    title: 'My custom block',
    // Other block registration code goes here.

    edit: ( props ) {
      const { isSelected } = props;
      const { myAttribute } = props.attributes;

      return (
        { isSelected && (
          <InspectorControls>
            <PanelBody title='Things'>
            
              <MyAutocomplete
                label='Attribute label'
                value={ myAttribute }
                onChange={ onChangeMyAttribute }
                options={ options }
              />

            </PanelBody>
          </InspectorControls>
        ) }
      );
    }
  }
} )();
@mohsinrafiq

This comment has been minimized.

Copy link

commented Aug 13, 2019

Hi @thatdevgirl,

First of all thanks for sharing wonderful Autocomplete extension.

I am not able to import your extension in my JS file and got error 'Unrecognized Block' as I am new to Gutenberg. My js file format is in ES5 and following below is my JS code. Any help to import your extension in my below structure would be much appreciated.

import MyAutocomplete from './simple-autocomplete.js'; // adjust if you saved this file somewhere else.

( function( i18n, blocks, element, editor, components, data ) {
	
	var el = element.createElement,
		__ = i18n.__,
		ServerSideRender = components.ServerSideRender,
		PanelBody = components.PanelBody,
		SelectControl = components.SelectControl,
		InspectorControls = editor.InspectorControls;
		
		
	blocks.registerBlockType( 'mydomain-blocks/mag-stories', {
		
		title: __( 'Mag Stories', 'my-domain-slug' ),
		category: 'common',
		
		attributes:  {
			tag : {
				type: 'string',
				default: ''
			}
		},
		
		html: false,

		edit: function( props ) {
			var attributes =  props.attributes;
			var setAttributes =  props.setAttributes;
			
			return [
				el('div', { className: props.className }, [
					el( ServerSideRender, {
						block: 'mydomain-blocks/mag-stories',
						attributes: attributes
					}),
					
					el( InspectorControls, {},
						
						el( PanelBody, {
								title: __( 'Filters', 'my-domain-slug' ),
								initialOpen: true
							},
							
							// Select Post Tags
							el( SelectControl, {
									label: __( 'Tags', 'my-domain-slug' ),
									value: attributes.tag,
									options: mag_stories.tags,
									onChange: ( newValue ) => { setAttributes( { cat: newValue } ); }
								}
							),
						),
					),
				])
			];
		},

		save: function() {
			// Rendering in PHP
			return null;
		},
	} );
} )(
	window.wp.i18n,
	window.wp.blocks,
	window.wp.element,
	window.wp.editor,
	window.wp.components,
	window.wp.data
);

Thanks in Advance

@simonhammes

This comment has been minimized.

Copy link

commented Aug 13, 2019

Just a quick comment: Did you register the block in PHP? It would be helpful if you would post your PHP code too.

@mohsinrafiq

This comment has been minimized.

Copy link

commented Aug 13, 2019

Hi @simonhammes,

Yes I did, Please find the PHP code below.

/**
 * Prefix_Core_Mag_Stories class
 * 
 * This is used to define Mag Stories.
 * 
 * @link        https://abc.com
 * @since       1.0.0
 * @package     Prefix_Core
 * @subpackage  Prefix_Core/includes/blocks
 * @author      ABC <contact@abc.com>
 */

class Prefix_Core_Mag_Stories {
    /**
     * Initialize the class and set its properties.
     *
     * @since   1.0.0
     */
    public function __construct() {
		
        // Register Block - Mag Stories.
		add_action( 'init', array ( $this, 'prefix_block_mag_stories' ) );
    }
	
	public function prefix_block_mag_stories() {
		
		// Skip block registration if Gutenberg is not enabled/merged.
		if ( !function_exists('register_block_type') ) {
			return;
		}
		
		$dir = dirname(__FILE__);
		$index_js = 'mag-stories.js';
		
		wp_register_script(
			'prefix-mag-stories-js',
			plugins_url( $index_js, __FILE__ ),
			array(
				'wp-blocks',
				'wp-element',
				'wp-i18n'
			),
			filemtime( "$dir/$index_js" ),
			true
		);
		
		// Collect Post Tags Data
		$tags[] = array(
			'label' => esc_html__( 'All Tags', 'prefix-core' ),
			'value' => '',
		);
		
		$jumbo_stories_tags = get_tags();
		foreach ( $jumbo_stories_tags as $jumbo_stories_tag ) {
			$tags[] = array(
				'label' => $jumbo_stories_tag->name,
				'value' => $jumbo_stories_tag->term_id,
			);
		}
		
		
		wp_localize_script(
			'prefix-mag-stories-js',
			'mag_stories',
			array(
				'tags' => $tags,
			)
		);
		
		register_block_type( 'mydomain-blocks/mag-stories', array(
			'editor_script' => 'prefix-mag-stories-js',
			'render_callback' => [ $this, 'prefix_block_mag_stories_handler' ],
			'attributes' => apply_filters(
				'jumbo_stories_attributes', [
					'className' => array( 'type' => 'string', 'default' => null ),
					'tag' => array( 'type' => 'string', 'default' => '' )
				]
			)
		));
	}
	
	/**
	 * Handler for Mag Stories block
	 * 
	 * @access public
     * @param array $atts - [$tag] attributes.
	 *
	 * @return string
	 */
	public function prefix_block_mag_stories_handler( $atts = [] ) {
		
		// normalize attribute keys, lowercase.
        $atts = array_change_key_case( (array)$atts, CASE_LOWER );
		
		return $this->prefix_mag_stories( $atts );
	}
	
	/**
	 * Output the Mag Stories 1
	 *
	 *
	 * @return string
	 */
	public function prefix_mag_stories( $atts ) {
		
		$args = array(
			'post_type' => 'post',
			'posts_per_page' => 6
		);
		
		if ( ! empty( $atts['tag'] ) ) {
			$args['tag__in'] = $atts['tag'];
		}
		
		$mag_story_posts = new WP_Query( $args );
		
		// Start output.
		ob_start();
		
		if ( $mag_story_posts ->have_posts() ) :
                       return $mag_story_posts;
		endif;
		
		$html = ob_get_clean();
			
		// Return output.
        return $html;
	}
}
new Prefix_Core_Mag_Stories();
@thatdevgirl

This comment has been minimized.

Copy link
Owner Author

commented Aug 20, 2019

Hi @mohsinrafiq -

The example I gave is in ES6. For ES5, you actually need to use the node require() function; import is an ES6 feature.

var MyAutocomplete = require( './MyAutocomplete' );

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.