2

Adapting some old SEO module (which was written for TinyMCE), I have to access the current post title and post content via JavaScript. I have been avoiding Gutenberg in the past, but it seems like this will be no longer possible.

Within the old SEO module there are the following lines in the admin.js:

var title = $('#title').val().trim();
var content = $('#content').val().trim();

Those fields do not exist in Gutenberg anymore. I have tried to find the correct way to do this in the docs, but no luck so far. I have found:

wp.data.select("core/editor").getBlocks()
wp.data.select("core")

But both seem to be empty arrays in my case (although this post has content). I basically just need a way to get the textual contents of all blocks (and maybe separately from the main post title if this is possible). Who knows how to do that?

2 Answers 2

5

If we do this in the browser dev tools console:

var b = wp.data.select("core/editor");

We can then inspect the b variable to see what functions it exposes via autocomplete, or debugging tools.

Notice, I didn't call getBlocks(), the post title has never been a part of the post content, why would that change now?

A look around gave me this a.getCurrentPost(), which returns a post object with a post title, but this will be the original post title, not the current post title, and doesn't get updated as the user edits the title

A quick google however gave an identical Question with an indepth answer on stackoverflow: https://stackoverflow.com/questions/51674293/use-page-title-in-gutenberg-custom-banner-block

const title = select("core/editor").getEditedPostAttribute( 'title' );
4
  • You forgot this line: const { select } = wp.data;
    – ViliusL
    Commented Jul 9, 2021 at 10:25
  • 1
    Actually, depending on what you use to get WP Data, that would be incorrect. For example in my projects I import it instead of wp.data via import { select } from '@wordpress/data';, so it depends. So refer to the linked answer. There's also withSelect and useSelector
    – Tom J Nowell
    Commented Jul 9, 2021 at 13:21
  • And the latest is useSelect. import { useSelect } from '@wordpress/data';
    – JonShipman
    Commented Sep 3, 2021 at 21:37
  • @JonShipman useSelect is a react hook, not all uses are inside a React component, e.g. filters, so select would be more appropriate
    – Tom J Nowell
    Commented Sep 4, 2021 at 13:20
1

Just updating this answer for 2021. You'll want to be familiar with node and optionally yarn (personal preference, but you can usually sub npm run x for yarn x scripts).

Initialize the folder with yarn init it'll step you through some questions. Typically, the project name will be the same as your plugin or theme name and the version will be the same as the plugin or theme version. Afterwards you'll get a package.json. Now to add the packages.

In a command prompt/terminal we'll want to yarn add @wordpress/scripts @wordpress/data @wordpress/block-editor @wordpress/blocks. If you want access to React components, there's also @wordpress/element you can add in.

Next you'll want to create your source file that'll be the compilation start point. You can use one JS file or multiple via imports. Typically I go into it assuming multiple to avoid a refactor down the road.

For grins, let's make our entrypoint js/src/blocks.js, relative to the project's root. Inside this file, let's import registerBlockType like so:

import { registerBlockType } from '@wordpress/blocks';

Next, let's create the directory for this SEO module, I'll go with js/src/seo/. These can be anything, I'm just going with the flow.

We'll come back to that folder, but first lets finish the blocks.js.

Import that directory's exports in blocks.js.

import * as seo from './seo';

And as I mentioned earlier about supporting multiple blocks in one project, we'll build an array and run registerBlockType over each import.

[ seo ].forEach( ( block ) => {
  if ( ! block ) {
    return;
  }

  const { settings, name } = block;
  registerBlockType( name, settings );
} );

Now let's get into the actual block itself. Inside the seo folder create index.js and block.json files.

Inside the block.json file you'll want to reference https://developer.wordpress.org/block-editor/reference-guides/block-api/block-metadata/, but to get you started you need at least the following:

"apiVersion": 2,
"name": "namespace/seo",
"title": "Your SEO Block",
"category": "widgets",
"description": "SEO Classic Port",
"textdomain": "namespace"

I could go on for days just talking about the blocks.json file. But this will get you started.

Next is the index.js file. You'll want to start the file with the following imports:

import { useBlockProps } from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';
import metadata from './block.json';

Now lets use them.

const { name } = metadata;
export { name }

export const settings = {
  ...metadata,
  edit: function() {
    const { title, content } = useSelect(
        ( select ) => {
          return {
            title: select( 'core/editor' ).getEditedPostAttribute( 'title' ),
            content: select( 'core/editor' ).getEditedPostContent(),
          }
        },
        []
    );

    return (
      <div { ...useBlockProps() }>
        <div>Title: { title }</div>
        <div>Content: { content }</div>
      </div>
    );
  },
  save: () => null,
};

We're exporting the name and settings objects referred in the blocks.js above. Also, we're doing nothing right now on save. Most of the time I use these "quick blocks" to edit a piece of post meta, so returning null is completely valid.

We'll just need two more steps to round everything out. Adding our blocks.js to the package scripts and registering the script (optionally any styles as well if you import any css or sass).

First the package scripts. Add the following:

"scripts": {
  "build": "wp-scripts build ./js/src/blocks.js --output-path=./js/build",
  "start": "wp-scripts start ./js/src/blocks.js --output-path=./js/build"
}

With these scripts, running yarn start will create a development copy of your compiled JS in the src/build folder. Likewise yarn build will create a minified production ready compile.

Finally we'll need to enqueue these files. Normally we'd use the admin_enqueue_scripts hook, however, you can enqueue public js with this blocks. So I like to use init. Another reason to use init, we're going to use register_block_type_from_metadata.

Include this in your plugin file or your functions.php (for themes). I'm assuming plugin for the sake of verbosity. You'll need to change the plugin reference function for theme friendly ones.

function register_block_files() {
  $asset_file = include plugin_dir_path( __FILE__ ) . 'js/build/blocks.asset.php';

  wp_register_script(
    'your-blocks',
    plugins_url( 'js/build/blocks.js', __FILE__ ),
    $asset_file['dependencies'],
    $asset_file['version'],
    false
  );

  $folders = array(
    'seo',
  );

  foreach ( $folders as $folder ) {
    register_block_type_from_metadata(
      plugin_dir_path( __FILE__ ) . 'js/src/' . $folder . '/block.json',
      array(
        'editor_script' => 'your-blocks',
      )
    );
  }  ​
}

add_action( 'init', 'register_block_files' );

That should be all that there's to it! You'll be creating a block that loads under the widgets and will print Title: The Post Title, and Content: The post's html content. Obviously you can do what you want inside the edit function. Usually I split save and import apart into separate files and import them.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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