I followed along with this stack overflow I got everything working except for the values saving in myTest attribute array. For some reason it's not and I've been stumped for over a week on it. Here is my complete code for reference.
import React, { useState, useEffect } from 'react';
import { __ } from '@wordpress/i18n';
import { registerBlockType } from "@wordpress/blocks"
import {
useBlockProps,
RichText,
InspectorControls,
PanelColorSettings
} from '@wordpress/block-editor';
import {
PanelBody,
PanelRow,
SelectControl
} from '@wordpress/components';
import apiFetch from '@wordpress/api-fetch';
import './editor.scss';
registerBlockType('blog-post-block/blog-post-container-block', {
// Block name. Block names must be string that contains a namespace prefix. Example: my-plugin/my-custom-block.
title: __('SfS Post block', 'sfs-block'), // Block title.
icon: 'shield', // Block icon from Dashicons → https://developer.wordpress.org/resource/dashicons/.
category: 'design', // Block category — Group blocks together based on common traits E.g. common, formatting, layout widgets, embed.
attributes: {
myTest: {
type: 'array',
source: 'query',
selector: ' div.blog-stamp > * ',
default: [],
query: {
image: {
type: 'string',
source: 'attribute',
selector: 'img',
attribute: 'src'
},
title: {
type: 'string',
selector: 'h3',
source: 'html'
},
excerpt: {
type: 'string',
selector: 'p',
source: 'html'
},
link: {
type: 'string',
source: 'attribute',
selector: 'a',
attribute: 'href'
},
}
},
backgroundColor: {
type: "string"
},
textColor: {
type: "string"
},
selectedValue: {
type: "string",
default: "vtalk"
},
myNumber: {
type: "number",
default: "3"
},
},
edit({ className, attributes, setAttributes }) {
const blockProps = useBlockProps();
const { backgroundColor, textColor, selectedValue, myNumber, myTest } = attributes;
const [teams, setTeams] = useState([]);
const catagories = [];
const newArray = [];
useEffect(() => {
apiFetch({ path: '/wp/v2/posts?_embed=wp:term' }).then((teams) => {
setTeams(teams);
});
}, []);
for (var i = 0; i < teams.length; i++) {
catagories.push({ catagory: teams[i]._embedded['wp:term']['0']['0']['name'] });
newArray.push({ value: teams[i].id, label: teams[i].title.rendered, content: teams[i].excerpt.rendered, image: teams[i].featured_image_url, link: teams[i].link, catagory: teams[i]._embedded['wp:term']['0']['0']['name'] });
}
const onChangeBackgroundColor = (BackgroundColor) => {
setAttributes({ backgroundColor: BackgroundColor })
}
const onChangeTextColor = (TextColor) => {
setAttributes({ textColor: TextColor })
}
const onChangeNumber = (number) => {
setAttributes({ myNumber: number })
}
function updateAttributes(string1, string2, string3, string4, index){
const theAttributeUpdate = [...myTest];
theAttributeUpdate[index] = { image: {string1}, title: {string2}, excerpt: {string3}, link: {string4} };
setAttributes({ myTest: theAttributeUpdate })
}
const stringArray = catagories.map(JSON.stringify);
const uniqueStringArray = new Set(stringArray);
const uniqueArray = Array.from(uniqueStringArray, JSON.parse);
const handleSelectChange = (event) => {
setAttributes({ selectedValue: event.target.value })
console.log(event.target.value);
}
return (
<div {...blockProps}>
<InspectorControls>
<PanelColorSettings
title={__('Button Color settings', 'sfs-block')}
initialOpen={false}
colorSettings={[
{
value: textColor,
onChange: onChangeTextColor,
label: __('Button Text color', 'sfs-block')
},
{
value: backgroundColor,
onChange: onChangeBackgroundColor,
label: __('Button Background color', 'sfs-block')
}
]}
/>
<PanelBody
title={__('catagory filter', 'sfs-block')}
initialOpen={true}
>
<PanelRow>
<fieldset>
<select value={selectedValue} onChange={handleSelectChange}>
{uniqueArray.map((cat) => {
return <option value={cat.catagory}>{cat.catagory}</option>
})}
</select>
</fieldset>
</PanelRow>
</PanelBody>
<SelectControl
label="How many"
value={myNumber}
options={[
{ label: 'Three in a row', value: '3' },
{ label: 'Six in a row', value: '6' },
]}
onChange={onChangeNumber}
/>
</InspectorControls>
<div class="blog-stamp-parent">
{ newArray.filter((m, idx) => m.catagory == selectedValue).map((m) => {
return (
<div class="foo blog-stamp">
<img class="stamp-image" src={m.image} width="200" height="200" />
<RichText
tagName="h3"
value={m.label}
class="foo stamp-title"
onChange={({string2}) => updateAttributes(string1, string2, string3, string4, index)}
/>
<RichText
tagName="p"
value={m.content}
class="foo stamp-excerpt"
onChange={({string3}) => updateAttributes(string1, string2, string3, string4, index)}
/>
<p class="button-parent">
<a
href={m.link}
className="foo sfs-button"
style={{ backgroundColor: backgroundColor, color: textColor }}
>
{'Read more'}
</a>
</p>
</div>)
})};
{console.log(myTest)}
</div>
</div>
)
},
save({ attributes }) {
const blockProps = useBlockProps.save();
const { backgroundColor, textColor, selectedValue, myNumber, menuItems, myTest, para, src, href } = attributes;
return (
<div{...useBlockProps.save()}>
<div class="blog-stamp-parent" >
{myTest && myTest.map(({ image, title, excerpt, link }) => {
return (
<div class="blog-stamp">
<img src={image} width="200" height="200" />
<RichText.Content className="stamp-title" tagName="h3" value={title} />
<RichText.Content className="stamp-excerpt" tagName="p" value={excerpt} />
<p class="button-parent">
<a href={link} class="sfs-button" style={{ backgroundColor: backgroundColor, color: textColor }}>Read more</a>
</p>
</div>)
})};
</div>
</div>
)
},
});
In edit, under the return statement, I map through newArray and apply the values to the block elements. Then the onchange within the block element is suppose to trigger the updateAttributes function which saves the value into an array of attributes using the myTest attribute. But, when I do it and console.log myTest, it comes back as blank with no values. Just []. Any idea why?