Gutenberg blocks - print attributes query (react.js) - reactjs

I'd like to display a button in my plugin that creates a custom Wordpress block. I have a problem, I cannot display an attribute from the query array:
block.json:
"hero_button_1": {
"type": "array",
"source": "query",
"selector": "a",
"query": {
"url": {
"type": "string",
"source": "attribute",
"selector": "a",
"attribute": "href"
},
"title": {
"type": "string",
"source": "attribute",
"selector": "a",
"attribute": "title"
},
"text": {
"type": "array",
"source": "children",
"selector": "a"
}
}
}
edit.js
const buttonHeroURL = ( newURL ) => {
setAttributes( {
hero_button_1: {
url: newURL
}
} )
}
<RichText
{...blockProps}
tagName="a"
onChange={buttonHeroURL}
allowedFormats={['core/bold', 'core/italic']}
value={attributes.hero_button_1.url}
placeholder={__('Adress url button')}
/>
save.js
<RichText.Content
{ ...blockProps }
tagName="a"
className={"mt-3 text-base text-gray-500 sm:mt-5 sm:text-lg sm:max-w-xl sm:mx-auto
md:mt-5 md:text-xl lg:mx-0"}
value={ attributes.hero_button_1.url }
/>
I need to create custom buttons in which it will be possible to specify the url and the button name on the backend side of Wordpress.
Thank you for your help :)

Add this to your code (i am assuming you have the base construct working):
<RichText
{...blockProps}
tagName="a"
onChange={(value) => {
setAttributes({hero_button_1: value})
}}
allowedFormats={['core/bold', 'core/italic']}
value={hero_button_1}
placeholder={__('Adress url button')}
/>
This will set the value on the hero_button_1 directly. If you want it inside as an object, you'll have to deal with creating a JSON String.
For this to work you'll also have to get your attributes and the setAttributes in your edit function:
https://developer.wordpress.org/block-editor/reference-guides/block-api/block-attributes/
Similar to this example of mine:
https://stackoverflow.com/a/73544454/7262739

Related

How do I iterate, dynamically load my form input elements and then retrieve the input values on Form Submit in React?

I am creating a sample dynamic form and I want to load my input elements based on a JSON which contains different input elements like "textbox", "text-area", "dropdown", "radio-input" and so on..
I have a JSON file created to get this as shown below:
[
{
"id": "1",
"type": "textbox",
"text": "",
"required": true,
"label": "lbl"
},
{
"id": "2",
"type": "textbox",
"text": "",
"required": true,
"label": "Specification Name"
},
{
"id": "3",
"type": "dropdown",
"text": "",
"required": true,
"label": "Specification Reviewed",
"options":["a","2"]
},
{
"id": "4",
"type": "dropdown",
"text": "",
"required": true,
"label": "Action Required",
"options":["1","2","3"]
},
{
"id": "5",
"type": "textbox",
"text": "",
"required": true,
"label": "lbl"
}
]
I have an App base component which calls another component called "Input" which has my layout and I retrieve the elements through that component. I am able to pull the text box and dropdown here but I am not able to iterate through the dropdown select. I'm not sure how to do it.
Here's my App Base solution: Here I use the map concept to fetch the data from the JSON local file and assign it to inputvalues which I then use in the return within the form tag.
I'm able to list all my input elements dynamically
But I'm not able to get the dropdown values from my JSON file
function App() {
const [inputObject, setInputObject] = React.useState(inputData)
const inputvalues = inputObject.map( input => {
return (
<Input
key={input.id}
input={input}
/>
)
})
const handleSubmit = (event) => {
event.preventDefault();
}
return (
<div className="App">
<header className="App-header">
<form>
<div>
{inputvalues}
</div>
<input type="submit" value="submit" onClick={handleSubmit} />
</form>
</header>
</div>
);
}
export default App;
And, here's my input.js component file: This basically lays out the input elements and I fetch the data using Props but I am unable to fetch the dropdown selection values because I would need to somehow iterate within each of those dropdown elements.
export default function Input(props) {
const [state, setState] = React.useState({
textBoxValue: ""
})
function handleChange(evt) {
setState({ [props.input.id] : evt.target.value });
}
if (props.onChange) {
props.onChange(state);
}
return (
<div>
<label>{props.input.type}: </label>
{props.input.type === "textbox" && <input name={props.input.type} placeholder={props.input.type} id={props.input.id} value={state.firstName} onChange={handleChange}/>}
{props.input.type === "dropdown" && <select name={props.input.type} id={props.input.id}>
<option value={props.input.options}></option></select>
}</div>)}
Please help me or guide me because I'm still learning React.
In addition to this, how would i later get all the input values upon FORM SUBMIT ? For this I tried adding a handleChange event to see if data comes through but it does not work.
Thank you so much in advance!
You may find Yup and Formik useful.
With Yup, you can include types to fields as well as things such as if the field is required.
The example linked should get you in the right direction.
Edit - (after OP comment)
So without using any external library, you could do something like this:
// Get a hook function
const {useState} = React;
const INPUTS = [
{
"id": "1",
"type": "textbox",
"value": "",
"required": true,
"label": "lbl"
},
{
"id": "2",
"type": "textbox",
"value": "",
"required": true,
"label": "Specification Name"
},
{
"id": "3",
"type": "dropdown",
"value": "",
"required": true,
"label": "Specification Reviewed",
"options":["a","2"]
},
{
"id": "4",
"type": "dropdown",
"value": "",
"required": true,
"label": "Action Required",
"options":["1","2","3"]
},
{
"id": "5",
"type": "textbox",
"value": "",
"required": true,
"label": "lbl"
}
];
const convertArrayToObject = (array, key, targetKey) => {
const initialValue = {};
return array.reduce((obj, item) => {
return {
...obj,
[item[key]]: item[targetKey],
};
}, initialValue);
};
const Form = () => {
const [formState, setFormState] = useState(
convertArrayToObject(INPUTS, "id", "value")
);
const handleChanges = (keyName) => (e) => {
const newValue = e.target.value
setFormState(prev => ({
...prev,
[keyName]: newValue
}));
}
console.log(formState);
return (
<form>
{INPUTS.map((input, inputIndex) => (
<div key={inputIndex}>
<label htmlFor={input.id}>{input.label}</label>
{input.type === "dropdown" && input.options ? (
<select onChange={handleChanges(input.id)}>
{input.options.map((option, optionIndex) => (
<option
key={optionIndex}
value={option}>{option}
</option>
))}
</select>
) : (
<input
id={input.id}
name={input.id}
required={input.required}
type={input.type}
onChange={handleChanges(input.id)}
value={formState.value}
/>
)}
</div>
))}
</form>
);
}
ReactDOM.createRoot(
document.getElementById("root")
).render(
<Form />
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.1.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.1.0/umd/react-dom.development.js"></script>
A little reasoning behind some of the code written:
React wants a key prop to be passed when mapping over objects (hence I've added it for each wrapper div and option element.
I've mapped over the INPUTS object to build the initial state, and then created an onChange handler that is curried, that way it is generic enough to be used everywhere
I'm just console.loging the formState to demonstrate the changes as you update the form.
Beyond
Consider adding Auto Complete if applicable or worthwhile
You will of course need some kind of some kind of submit button if you plan to submit the data to an API.
<button type="submit">Submit</button
But I will leave that as an exercise for you...
Hope this helps!

Creating carousel from JSON response

I am getting a JSON response from a server which looks like this:
{
"value_1": "a",
"value_2": "b",
"pagination": {
"titles": [
"Title 1",
"Title 2",
"Title 3",
"Title 4",
"Title 5"
]
},
"slides": [
{
"pagination_id": 0,
"content": {
"heading": "Welcome!",
"description": "Stuff goes here",
"image": {
"url": "<image_url>",
"alt": "alternative text"
},
"next": {
"label": "Next"
}
}
},
{
"pagination_id": 1,
"content": {
"heading": "About",
"description": "Stuff goes here",
"image": {
"url": "<image_url>",
"alt": "alternative text"
},
"next": {
"label": "Next"
}
}
},
{
"pagination_id": 2,
"content": {
"heading": "Welcome!",
"description": "Stuff goes here",
"groups": [
{
"id": 1,
"label": "Group 1"
},
{
"id": 2,
"label": "Group 2"
},
{
"id": 3,
"label": "Group 3"
}
],
"next": {
"label": "Next"
}
}
},
{
"pagination_id": 3,
"heading": "Welcome!",
"description": "Stuff goes here",
"image": {
"url": "<image_url>",
"alt": "alternative text"
},
"back": {
"label": "Back"
},
"next": {
"label": "Next"
}
},
{
"pagination_id": 4,
"heading": "Welcome!",
"description": "Stuff goes here",
"image": {
"url": "<image_url>",
"alt": "alternative text"
},
"back": {
"label": "Back"
},
"next": {
"label": ""
}
}
],
"footer": {
"legal": {
"label": "Legal",
"url": "<url>"
},
"privacy": {
"label": "Privacy",
"url": "<url>"
},
"cookies": {
"label": "Cookies",
"url": "<url>"
}
}
}
As you can probably tell this data is being used to create a carousel with the slide content shown on each slide. The problem I am having is that the slide titles are coming from the pagination part of the JSON but the actual slide content including next and back buttons are coming from the slides part.
Currently I have some problems:
I need to get the correct title for each slide.
I need to render buttons based on the next and back properties present in each slide.
When a button is clicked to go forward or back I need to keep track of the slide that should be showing.
I already know that what I need to do for part 1 is use the pagination_id of the slide to get the correct title from the pagination.titles array but I am not entirely sure about the best way to go about this.
For the second part, I think it should be possible to also use the pagination_id to keep a track of the current slide (I think), but I am not sure how I might go about this. I should mention that the buttons for each slide are going to be render based on the next and back properties of each slide.
This application is built with React and I am currently only using local state currently as I don't think that something like Redux is really worth including for such a small amount of data.
Any help with this conundrum would be much appreciated,
Thanks for your time
In your case despite the data coming from different arrays, what you can cash around is that the number of items are the same in both arrays. So you can just use the current index of array where you are looping these items. So it would go something like this:
Updated Code
return(
data.slides.map((slide, index)=>{
return <div key={index}>
<h1> {data.pagination.titles[index]} </h1> // for title
<img src={slide.content?.image?.url || slide.image.url} alt={slide.content?.image?.alt || slide.image.alt} />
//for buttons
{(slide.content?.previous || slide.previous) && <button onClick={()=> setCurrentSlideIndex((index - 1) % data.slides.length)}> {slide.content?.previous?.label || slide.previous.label} </button>}
{(slide.content?.next || slide.next) && <button onClick={()=> setCurrentSlideIndex((index + 1) % data.slides.length)}> {slide.content?.next?.label || slide.next.label} </button>}
</div>
})
)
Hope you get the idea.
Update
However if there is lot of stuff going on then you might need to make a function which gets boolean as a parameter telling whether the particular item has content object or not, and return the ui based on that conditionally. Something like this:
const renderSlide=(content, index)=>{
if(content){
return <div>
// with slide.content.xyz
<h1> data.pagination.titles[index] </h1>
<img src={data.slides[index].content.image.url}
</div>
}
else{
return <div>
// with slide.xyz
<img src={data.slides[index].image.url}
</div>
}
}
and calling it inside your function as:
return(
data.slides.map((slide, index)=>{
<>
{renderSlide(slide.content, index)}
<>
})

Logic app- how to retrieve json data from dynamic property name

Here's my json - Here I want to retrieve json content from "Property - Dynamic content". Where, dynamic content part might vary for every json request. How do I filter this by a dynamic name?
{
"Attributes":
{
"Property1": {
"Data1": {
"Value": "50"
}
},
"Property2": {
"Data2": {
"Value": "50"
}
},
"Property - Dynamic content": {
"Data3": {
"Value": "50"
},
"Data4": {
"Value": "50"
}
}
}
}
For your requirement, please refer to my logic app below:
1. I initialized a variable and store the json same with yours to simulate your situation.
2. Then use "Parse JSON".
Please notice the schema of "Parse JSON" show as:
{
"properties": {
"Attributes": {
"properties": {
"Property - Dynamic content": {
"type": [
"object",
"array"
]
},
"Property1": {
"properties": {
"Data1": {
"properties": {
"Value": {
"type": "string"
}
},
"type": "object"
}
},
"type": "object"
},
"Property2": {
"properties": {
"Data2": {
"properties": {
"Value": {
"type": "string"
}
},
"type": "object"
}
},
"type": "object"
}
},
"type": "object"
}
},
"type": "object"
}
Please pay attention to the type of Property - Dynamic content in schema above. Since the content of Property - Dynamic content is either "object" or "array", so I set both "object" and "array" as the type of Property - Dynamic content.
3. Then I initialized a variable named "result" to get the value which you want.
As we use both type "object" and "array" in the schema for Property - Dynamic content, so you may not find it in the "Dynamic content" selection. You can input its value by expression as the screenshot above. The whole expression is: body('Parse_JSON')?['Attributes']?['Property - Dynamic content']
I was able to get what I need using inline code - javascript - If anyone else is looking for the same - here it is - This will give json from Property - dynamic content element.
var data = Object.keys(workflowContext.trigger.outputs.body.Attributes);
var key = data.filter(s => s.includes('Property')).toString(); // to get element - Property - dynamic content
return workflowContext.trigger.outputs.body.Attributes[key];

react 16, render with array, with first and last element conditiona with divl in complex render

I have a render that returns array and it's working well, it's iterate over an array to show fields in a form. But now i Want to enclose in a divs to group some fields conditionnaly accord to a propery. to get somethi like
<div class='section>
<Field>
<Field>
</div>
<div class='section>
<Field>
</div>
actually i get just:
<div class='section>
<Field>
<Field>
</div>
One example of a branch in my object:
(it's when the field have "formNewSection" property to separate the fields grouped by div)
"identitydocs": {
"type": "String",
"dbtype": "Json",
"labelChildsGlobal": true,
"labelChildsShow": true,
"subfields": {
"id": {
"type": "ID",
"typeInput": "hidden"
},
"type": {
"type": "String",
"label": "id_doctype"
},
"country": {
"type": "String",
"validators": [
"required"
],
"typeInput": "selectBox",
"listSource": "countries"
},
"number": {
"type": "String",
"label": "id_docnumber"
},
"idnameisother": {
"type": "Boolean",
"typeInput": "checkbox",
"formNewSection": true
},
"lastname": {
"type": "String",
"validators": [
"required",
"alphai18n",
"minLength:3"
],
"normalize": "UpperCase"
},
"firstname": {
"type": "String",
"validators": [
"required",
"alphai18n",
"minLength:3"
]
},
"idexpiration": {
"type": "String",
"dbtype": "Date"
},
"idiauthority": {
"type": "String"
},
"ididate": {
"type": "String",
"dbtype": "Date"
},
"idaddressisother": {
"type": "Boolean",
"typeInput": "checkbox",
"formNewSection": true
},
"addressline1": {
"type": "String",
"validators": [
"required"
]
},
"addressline2": {
"type": "String",
"validators": [
"required"
]
},
"cp": {
"type": "String",
"inputSize": 7
},
"city": {
"type": "String"
},
"cityid": {
"type": "ID",
"typeInput": "hidden"
}
}
},
My code working:
return [
<Field
key={index+'-'+subindex+'-'+fieldKey}
name={`${rowValues}.${fieldKey}`}
type={subfield.typeInput ? subfield.typeInput : 'text'}
typeInput={subfield.typeInput ? subfield.typeInput : 'text'}
component={FormField}
label={field.labelChildsShow ? t(labelKey ):''}
placeHolder={!field.labelChildsShow || subfield.placeHolder ? t(labelKey) : ''}
listSource={subfield.listSource ? aSources[subfield.listSource] : ''}
index={subindex + 1}
width="270px"
icon={subfield.icon}
/>,
fields.length === 1 && subindex + 1 === Object.keys(Tables[tableCrud].fields[fieldParentKey].subfields).length ?
<div key={index+'-'+subindex+'-b'} style={ { marginTop: "10px", marginRight: "5px" } }><a href="#" onClick={() => fields.remove(index)}>
<ShowIcon size="25" color="gray" icon="removecircleblack"/>
</a></div>
: null,
];
My new codigo does not work, adding and on the top and botomm of array but conditionnaly:
return [
(subfield.formNewSection && <div className="formSubSection" >),
<Field
key={index+'-'+subindex+'-'+fieldKey}
name={`${rowValues}.${fieldKey}`}
type={subfield.typeInput ? subfield.typeInput : 'text'}
typeInput={subfield.typeInput ? subfield.typeInput : 'text'}
component={FormField}
label={field.labelChildsShow ? t(labelKey ):''}
placeHolder={!field.labelChildsShow || subfield.placeHolder ? t(labelKey) : ''}
listSource={subfield.listSource ? aSources[subfield.listSource] : ''}
index={subindex + 1}
width="270px"
icon={subfield.icon}
/>,
(fields.length === 1 && subindex + 1 === Object.keys(Tables[tableCrud].fields[fieldParentKey].subfields).length ?
<div key={index+'-'+subindex+'-b'} style={ { marginTop: "10px", marginRight: "5px" } }><a href="#" onClick={() => fields.remove(index)}>
<ShowIcon size="25" color="gray" icon="removecircleblack"/>
</a></div>
: null)
(subfield.formNewSection && </div>),
];
with this modificaiotn i get print "fields.length === 1 && subindex + 1 === ..." on the screen.
is possible to do what i'm looking for with react? I can't do it in a simple way, becase this render is inside another render do it with .map, some fields have a mark to be grouped by divs and anothers not, so i can't see for the simple solutions
Could you provide the array structure or the real data for the data I'll take a look and see If I can help you with that.
One idea is suppose you had a a data structure housing the the items with index or a length that is equivalent to the fields something like this:
{
0: [ ... , ]
... to whatever data length
} <-- this can be an array instead of object
iterate over this container/w.e and have a div surrounding it
pending if you used array or object if object you can use Object.keys(w.e)
like poopArrayContainer.map( (item,ind) => { item.map( poop => ... ) } ) ... pretty sure you get it from here hope that helps, not sure of that's the best implementation but that's a thot LuL...
Why not just add the div tag before the field one and at the end like :
<div className="formSubSection" > <Field.......
..
</a></div></div>
: null)

How can pull values from a Json in React?

I'm trying to dosomething similar to this Angular Code but using React. I'm very new to react and can't figure it out.
I have a json that is storing data fields and a field called classes. I want to be able to pull the classes in json fields to attach them to each row. This is the angular way I have done in the past successfully.
<tr ng-repeat="row in vm.widget10.table.rows">
<td ng-repeat="cell in row">
<span class="{{cell.classes}}">
{{cell.value}}
</span>
</td>
</tr>
with a json structured this way
{
"widget10": {
"title": "Table Details",
"table": {
"columns": [{
"title": "Item Name"
},
{
"title": "Some Data"
},
{
"title": "Other Data ($)"
},
{
"title": "Visual Data (%)"
},
{
"title": "Profit/Loss ($)"
},
{
"title": "Profit/Loss (%)"
}
],
"rows": [
[{
"value": "Data Field One",
"classes": "text-boxed m-0 deep-orange-bg white-fg",
"icon": ""
},
{
"value": "$14,880.00",
"classes": "text-bold",
"icon": ""
},
{
"value": "$14,000.00",
"classes": "",
"icon": ""
},
{
"value": "%94.08",
"classes": "red-fg",
"icon": "trending_down"
},
{
"value": "$880.00",
"classes": "",
"icon": ""
},
{
"value": "%5.92",
"classes": "",
"icon": ""
}
]
]
}
}
In my react component render() I have something like this:
<TableBody
displayRowCheckbox={this.state.showCheckboxes}
deselectOnClickaway={this.state.deselectOnClickaway}
showRowHover={this.state.showRowHover}>
{statsData.map( (row, index) => (
<TableRow key={index}>
<TableRowColumn><span style={{backgroundColor:"{statsData.bgColor[index]}"}}>{row.name}</span></TableRowColumn>
<TableRowColumn>{row.data}</TableRowColumn>
<TableRowColumn>{row.o_data}</TableRowColumn>
<TableRowColumn>{row.v_data}</TableRowColumn>
<TableRowColumn>{row.o_pl}</TableRowColumn>
<TableRowColumn>{row.v_pl}</TableRowColumn>
</TableRow>
))}
</TableBody>
and a json this way (in the component)
const statsData = [
{
name: "Data Field One",
bgColor: "red",
data: "$14,880.00",
o_data: "$14,000.00",
v_data: "%94.08",
o_pl: "$880.00",
v_pl: "%5.92",
},
{
name: "Data Field Two",
bgColor: "blue",
data: "$14,880.00",
o_data: "$14,000.00",
v_data: "%94.08",
o_pl: "$880.00",
v_pl: "%5.92",
},
{
name: "Data Field Three",
bgColor: "yellow",
data: "$14,880.00",
o_data: "$14,000.00",
v_data: "%94.08",
o_pl: "$880.00",
v_pl: "%5.92",
}
];
So far the data comes through fine, but I can't figure out how to pull the bgColor as either a backgroundColor style or as a class.
Any help is appreciated.
Thanks
Remove the quotes from around the value for backgroundColor and read from the row iterator variable (based on the JSON you pasted):
<span style={{backgroundColor: row.bgColor}}>{row.name}</span>

Resources