Bài 12: Nested Blocks: Sử dụng InnerBlocks
Vừa rồi Nam có triển khai xây dựng một nút bấm Mega menu và coi nó là thách thức không nhỏ đối với bản thân bởi dựng nó với html/css hoặc Bootstrap thì dễ, nhưng chuyển về dạng Block thì chưa có hướng triển khai.
Dưới đây là cách Nam triển khai viết một Block Megamenu như sau:
Ở phía file edit.js, cứ mặc định chúng ta triển khai xây dựng Block Plugins theo chuẩn của WordPress, file index.js thì mình không ghi ở đây, nhưng cơ bản là nó gọi tới Edit và save(Đối với static block) hoặc ở block.json có gọi tới render.php
import { __ } from '@wordpress/i18n';
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
import { Button } from '@wordpress/components';
import { useState } from '@wordpress/element';
export default function Edit() {
const blockProps = useBlockProps();
const [ isVisible, setIsVisible ] = useState( true );
const onToggleContent = () => {
setIsVisible( ! isVisible );
};
return (
<div { ...blockProps }>
<Button
onClick={ onToggleContent } className='wp-block-button__link wp-element-button mega-menu-button'
>
{ __( 'Mega Menu', 'text-domain' ) }
</Button>
{ isVisible && (
<InnerBlocks
/>
) }
</div>
);
}
Đoạn này mình có dùng Copilot để viết code cho nhanh, cơ bản nó gồm những thành phần sau
Ở save.js thì mình triển khai như sau:
import { useBlockProps, InnerBlocks } from '@wordpress/block-editor';
export default function Save() {
const blockProps = useBlockProps.save();
// Get the button and the mega menu elements
return (
<div { ...blockProps }>
<button id="button" class="wp-block-button__link wp-element-button mega-menu-button">Mega Menu</button>
<div id="mega-menu" style="display: none;">
<InnerBlocks.Content />
</div>
</div>
);
}
Đây là thành phần được sử dụng trong các static blocks nhằm lưu dữ liệu Block markup, từ đó mang tới định dạng lưu vào CSDL của WordPress. Đoạn này mình sẽ tạo ra một nút bấm Mega Menu và gọi thành phần InnerBlocks.Content
Đoạn này mình không kế thừa thư viện Toggle như ở edit.js mà tạo ra thành phần tĩnh là nút bấm và một khối chứa mega menu, chính vì vậy nội dung sẽ in ra là dạng tĩnh và không có hiệu ứng toggle như ở edit.js, mình cần đẩy script vào view.js (1 file để chứa javascript dành riêng cho block – được định nghĩa ở block.json)
Đây là file block.json, file index.js thì cơ bản nó vẫn là file dẫn tới việc đăng ký block theo block.json, nối với Edit và save
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 3,
"name": "create-block/dropdown-button",
"version": "0.1.0",
"title": "Dropdown Button",
"category": "widgets",
"icon": "smiley",
"description": "Example block scaffolded with Create Block tool.",
"example": {},
"supports": {
"html": false
},
"textdomain": "dropdown-button",
"editorScript": "file:./index.js",
"editorStyle": "file:./index.css",
"style": "file:./style-index.css",
"viewScript": "file:./view.js",
"attributes": {
"selectedBlock": {
"type": "string",
"default": ""
}
}
}
Như ta thấy file viewScript gọi tới view.js, đây là file view.js sẽ tạo ra hiệu ứng toggle ở frontend
// Get the button and the mega-menu elements
var button = document.getElementById("button");
var megaMenu = document.getElementById("mega-menu");
// Define a function to handle the button click
var toggleMenu = function () {
// Toggle the mega-menu visibility
megaMenu.style.display = megaMenu.style.display === "none" ? "block" : "none";
};
// Add a click event listener to the button
button.addEventListener("click", toggleMenu);
Block này có 1 hạn chế là chỉ gọi 1 lần thôi vì id sẽ dễ bị trùng lặp, bạn cũng có thể cải tiến bằng việc chọn các thành phần có class tương ứng, cái này thì Nam triển khai vậy thôi
Trên đây cũng là ví dụ rất rõ ràng về việc sử dụng InnerBlock, khối này sẽ được toggle toàn bộ khi ta bấm vào nút bấm “Mega menu”, giờ hãy cùng tới với lý thuyết về InnerBlock và NestedBlock
Bạn có thể tạo 1 block bao hàm nhiều Block nhỏ, trường hợp sử dụng sẽ ứng với các Block sau
Khái niệm sử dụng Repeater của ACF cũng là rất thông dụng, tuy vậy Nam thấy đội ngũ WordPress core có vẻ muốn phát triển InnerBlocks hơn
Dưới đây là một ví dụ của InnerBlocks, đây là cách viết gộp, trong thực tế bạn nên tách về function Edit và save cho rõ ràng như cấu trúc Block chuẩn
import { registerBlockType } from '@wordpress/blocks';
import { InnerBlocks, useBlockProps } from '@wordpress/block-editor';
registerBlockType( 'gutenberg-examples/example-06', {
// ...
edit: () => {
const blockProps = useBlockProps();
return (
<div { ...blockProps }>
<InnerBlocks />
</div>
);
},
save: () => {
const blockProps = useBlockProps.save();
return (
<div { ...blockProps }>
<InnerBlocks.Content />
</div>
);
},
} );
Block này sẽ gọi ở tầng edit một không gian InnerBlocks để kéo thả đủ thành phần vào, ở đầu save vào CSDL InnerBlocks.Content sẽ là thành phần được tạo ra.
Đoạn này khá rối rắm nhưng theo mình thì
Bạn có thể xem Handbook để rõ hơn về ví dụ và cách sử dụng, cá nhân Nam không quan tâm phần này lắm, vì cũng chưa gặp trường hợp khách hàng yêu cầu quá chặt chẽ về kiểu Blocks
Cách này cơ bản sẽ mang tới nhiều Block Markup của Inner Blocks hơn, giúp bạn kiểm soát được thông tin in ra một cách phù hợp và rõ ràng
import { registerBlockType } from '@wordpress/blocks';
import { useBlockProps, useInnerBlocksProps } from '@wordpress/block-editor';
registerBlockType( 'gutenberg-examples/example-06', {
// ...
edit: () => {
const blockProps = useBlockProps();
const innerBlocksProps = useInnerBlocksProps();
return (
<div { ...blockProps }>
<div {...innerBlocksProps} />
</div>
);
},
save: () => {
const blockProps = useBlockProps.save();
const innerBlocksProps = useInnerBlocksProps.save();
return (
<div { ...blockProps }>
<div {...innerBlocksProps} />
</div>
);
},
} );Hook useInnerBlocksProps giúp in ra các phần tử con nằm trong InnerBlock tương tự như việc sử dụng blockProps để rải các thuộc tính.