Wonderful! WordPress

Gutenberg Block Editor 自作その3 - form編

Page No.2

Select Block – non-JSX

そして select のブロック。
select ブロックは専用の label は持っているが親は無しで、Fragment を使った。インラインスタイルも無し。親ボックスは無しなので、エディター右サイドバーの「高度な指定」の idclassselect タグ自体に指定できるもの。故に、この select タグにおいては、nameid 属性を違うものにできるけれど、ほとんど意味はないので、これといった理由があるわけではない。

select タグにおいて、最も違うところは複数の option タグを内包するということ。
単純に指定された文字列から動的に文字列としてタグを生成して props.attributescontent としてデータの受け渡しをさせ、select タグの子要素とすれば簡単に出来そうなのではある。けれど、これだとその文字列による option タグたちは、出力の際に createElement によってエスケイプされてしまい、タグとして機能してはくれない。
これに関しては、RichText を使うことであっさりと解決する。props に保存された文字列で生成された option タグたちでも、RichText で子要素に指定してやれば、ちゃんとタグとして機能してくれるので、それならばこの方法でも良さげに思われる。
別の方法もあるかな。あらかじめ ループによる createElement で 生成した option タグたちを配列に入れておいて、子要素としてその配列を指定してやれば、期待した結果が得られた。ただ、処理の重複をさけるために、props にその生成した配列データを保存させることも可能ではあったけれど、そうすると、その createElement による配列データも props.attributes なのでブロックのデータとしてコメントデータの中に保存されてしまう。これがなかなか重ばって気になってしまう事になる。

しかしかといって、props を介さないと、たとえ更新された props.attributes に設定された値を使って生成された content だとしても、edit においては再描画されない。これはどうしたものか?と考えつつ、色々試していると結果的にこのやり方において機能してくれた。ただ、ちゃんとしたやり方というのがありそうではある。non-JSX においては情報が圧倒的に少なくてよくわからず、自分で考えるしかないのである。問題が出たならまた考えて対処するだけのことである。
ちなみにこのブロックも自的には input と同じ即時関数に textarea と共に入れている。

( function ( blocks, element, blockEditor, components ) {
	const el = element.createElement,
        Fragment = element.Fragment,
        InspectorControls = blockEditor.InspectorControls,
        TextControl = components.TextControl,
        RadioControl = components.RadioControl,
		useBlockProps = blockEditor.useBlockProps;

    // 子要素の option たちを生成して配列にいれる関数
    function update_content( tar ) {
        let cont_ary = [];

        if ( -1 !== tar.indexOf( ':' ) && -1 !== tar.indexOf( ',' ) ) {
            const tmpary = tar.split( ',' ),
                tmp_len = tmpary.length;

            for ( let i = 0; i < tmp_len; ++i ) {
                const elem = tmpary[ i ].split( ':' );

                if ( elem[1] ) {
                    // createElement にて option タグを生成
                    cont_ary.push( el( 'option', { value: elem[1], key: elem[0] }, elem[0] ) );
                }
            }
        }
        return cont_ary;
    }

    blocks.registerBlockType( 'stx-select/wrappedselect', {
        apiVersion: 2,
        title: 'STX select',
        icon: 'smiley',
        category: 'design',
        description: 'select option を追加するブロック',

        attributes: {
            iname: { type: 'string', default: '' },
            ilabel: { type: 'string', default: '' },
            isize: { type:'string', default: '' },
            imultiple: { type:'string', default: '0' },
            ioptions: { type:'string', default: '' },
            anchor: { type: 'string' },
        },

        supports: {
            anchor: true,
            // className: false, //(default:true)ブロック要素を作成した際に付く .wp-block-[ブロック名]で自動生成されるクラス名の設定。
        },
    
        edit: function( props ) {
            let blockProps = useBlockProps(),
                { iname, ilabel, isize, imultiple, ioptions } = props.attributes,
                cont_ary = [];

            // ここで update_content 関数を読んでおかないと attributes のデータを更新しても edit で再描画されない
            cont_ary = update_content( ioptions );

            return(
                el(
                    Fragment,
                    null,
                    el(
                        InspectorControls,
                        null,
                        el( 'div', { id: 'stx_control_sdbr', style: { fontSize: '1.3em' } },
                            el(
                                TextControl,
                                {
                                    label: 'label strings',
                                    help: 'label に表示する文字列で共通になる',
                                    value: ilabel,
                                    onChange: function( newValue ){ props.setAttributes( { ilabel: newValue } ); }
                                }
                            ),
                            el(
                                TextControl,
                                {
                                    label: 'name of select',
                                    help: 'select の name 指定、name属性、id属性で共通になる',
                                    value: iname,
                                    onChange: function( newValue ){ props.setAttributes( { iname: newValue } ); }
                                }
                            ),
                            el(
                                TextControl,
                                {
                                    label: 'options',
                                    help: 'optionの指定。eq. label:value,label2:value2,label2:value2 のjavascript object の書式',
                                    value: ioptions,
                                    onChange: function( newValue ){ props.setAttributes( { ioptions: newValue } );cont_ary = update_content( newValue ); }
                                }
                            ),

                            el(
                                TextControl,
                                {
                                    label: 'size属性',
                                    help: 'multiple が指定されている場合、この属性は一度に見えるべきリストの行数を指定',
                                    value: isize,
                                    onChange: function( newValue ){ props.setAttributes( { isize: newValue } ); }
                                }
                            ),
                            el(
                                RadioControl,
                                {
                                    label: 'multiple',
                                    help: 'リストの複数の選択肢を選択することができるmultiple属性の真偽',
                                    selected: imultiple,
                                    options: [ { label: 'enable', value: '1' },{ label: 'disable', value: '0' } ],
                                    onChange: function( newValue ){ props.setAttributes( { imultiple: newValue } ); }
                                }
                            ),
                        ),
                    ),
                    el(
                        Fragment,
                        null,
                        ilabel ?el( 'label', { htmlFor: iname}, ilabel + ' ' ) : null,
                        el(
                            'select',
                            Object.assign( blockProps, {
                                            className: props.className,
                                            name: iname,
                                            size: isize ? isize : null,
                                            multiple: Number( imultiple ) ? true : false
                                        } ),
                            cont_ary
                        )
                    )
                )
            );
        },
        save: function( props ) {
            let { iname, ilabel, isize, imultiple, ioptions } = props.attributes,
                blockProps = useBlockProps.save( {
                                        name: iname,
                                        size: isize ? isize : null,
                                        multiple: Number( imultiple ) ? true : false
                                    }),
                cont_ary = update_content( ioptions );

            if ( props.attributes.anchor ) {
                blockProps.id = props.attributes.anchor;
            }

            return (
                el(
                    Fragment,
                    null,
                    ilabel ?el( 'label', { htmlFor: iname}, ilabel + ' ' ) : null,
                    el( 'select', blockProps, cont_ary )
                )
            );
        },
    } );
}(
	window.wp.blocks,
	window.wp.element,
	window.wp.blockEditor,
    window.wp.components
));
JavaScript
CopyExpand

Form InnerBlocks

最後は、input やら select などの form 要素をまとめる親ブロックとしての form ブロック。まぁ、なんてことはない、actionmethod などの form の属性を指定することができる InnerBlocks てことで。

ただ、form タグは他の文書的に意味があったりレイアウトとしての意味があったりするタグとはちょっと趣が異なると思う。そのせいなのか、他の innerblock の入れ子にすると、エディター内で選択できなくなってしまうよう。この点、いまだ検討中。

( function ( blocks, element, blockEditor, components ) {
	const el = element.createElement,
        InnerBlocks = blockEditor.InnerBlocks,
        Fragment = element.Fragment,
        InspectorControls = blockEditor.InspectorControls,
        TextControl = components.TextControl,
        RadioControl = components.RadioControl,
		useBlockProps = blockEditor.useBlockProps;

    blocks.registerBlockType( 'stx-form/wrappedform', {
        apiVersion: 2,
        title: 'STX form',
        icon: 'smiley',
        category: 'design',
        description: 'inputを内包するための form ブロック',

        attributes: {
            faction: { type: 'string', default: '' },
            fmethod: { type: 'string', default: 'post' },
            fenctype: { type: 'string', default: '' },
            anchor: { type: 'string' },
        },

        supports: {
            anchor: true,
            // className: false, //(default:true)ブロック要素を作成した際に付く .wp-block-[ブロック名]で自動生成されるクラス名の設定。
        },
    
        edit: function( props ) {
            let blockProps = useBlockProps(),
                { faction, fmethod, fenctype } = props.attributes;

            return(
                el(
                    Fragment,
                    null,
                    el(
                        InspectorControls,
                        null,
                        el( 'div', { id: 'stx_control_sdbr', style: { fontSize: '1.3em' } },
                            el(
                                RadioControl,
                                {
                                    label: 'データ送信形式、default : post',
                                    selected: fmethod,
                                    options: [ { label: 'post', value: 'post' },{ label: 'get', value: 'get' } ],
                                    onChange: function( newValue ){ props.setAttributes( { fmethod: newValue } ); }
                                }
                            ),
                            el(
                                TextControl,
                                {
                                    label: 'action',
                                    help: 'サブミットされた時の動作指定。通常は送信先のファイルurl',
                                    value: faction,
                                    onChange: function( newValue ){ props.setAttributes( { faction: newValue } ); }
                                }
                            ),
                            el(
                                TextControl,
                                {
                                    label: 'enctype',
                                    help: 'application/x-www-form-urlencoded, multipart/form-data, text/plain ',
                                    value: fenctype,
                                    onChange: function( newValue ){ props.setAttributes( { fenctype: newValue } ); }
                                }
                            ),
                        ),
                    ),
                    el(
                        'form',
                        Object.assign( blockProps, {
                            className: props.className,
                            action: faction,
                            method: fmethod,
                            encType: fenctype ? fenctype : null
                        }),
                        el( InnerBlocks )
                    )
                )
            );
        },
        save: function( props ) {
            let blockProps = useBlockProps.save( {
                                        action: props.attributes.faction,
                                        method: props.attributes.fmethod,
                                        encType: ( props.attributes.fenctype ? props.attributes.fenctype : null )
                                    });

            if ( props.attributes.anchor ) {
                blockProps.id = props.attributes.anchor;
            }

            return ( el( 'form', blockProps, el( InnerBlocks.Content ) ) );
        },
    } );
}(
	window.wp.blocks,
	window.wp.element,
	window.wp.blockEditor,
    window.wp.components
));
JavaScript
CopyExpand

と、いうことで以上である。
で、そのブロックたちを使っているのが下のサンプル。まぁ、なんてことはなく、出来て当然といえば当然のことではある。ふぅ~!

Sample Form

これでいいかな、と思いつつ、一つ気にかかっているものがあったのだ。
それは span タグのブロックが無いということと、label タグを単独で使えないということ。それらのタグを選択できるブロックがあればいいのだけれど・・・。と、いうことで、それは3ページ目へと続く。

Sanbanse Funabashi
2010.10.24 sunrise

Top

スクロールさせるか画像をクリックすると元に戻ります。