Wordpress Custom Widget Save Multiple Attributes? - react-hooks

I am using the #wordpress/create-block package to build a simple widget. I do not understand how to make the save.js save more than one attribute.
I have 2 attributes defined in the block.json: theNotes and theContact.
"attributes": {
"theNotes": {
"type": "string",
"source": "text",
"selector": "div",
"default": ""
},
"theContact": {
"type": "string",
"source": "text",
"selector": "div",
"default": ""
}
}
My edit.js looks like this:
export default function Edit( { attributes, setAttributes } ) {
return (
<div { ...useBlockProps() }>
<div>
<TextControl
label={ __( 'The Notes', 'editor-notes' ) }
value={ attributes.theNotes }
onChange={ ( val ) => setAttributes( { theNotes: val } ) }
/>
</div>
<div>
<TextControl
label={ __( 'The Contact', 'editor-notes' ) }
value={ attributes.theContact }
onChange={ ( val ) => setAttributes( { theContact: val } ) }
/>
</div>
</div>
);}
For the save.js file, I cannot find instructions on how to save both of those attributes using this default scaffolding. I thought something like this would work, but I get a block validation error. It says that both the attributes were saved twice to both attribute values.
export default function save( { attributes } ) {
const blockProps = useBlockProps.save();
return (
<div { ...blockProps }>
<div>{ attributes.theNotes }</div>
<div>{ attributes.theContact }</div>
</div>
); }
The error says:
Content generated by `save` function:
<div class="wp-block-create-block-editor-notes"><div>notesTestcontactTest</div><div>notesTestcontactTest</div></div>
Content retrieved from post body:
<div class="wp-block-create-block-editor-notes"><div>notesTest</div><div>contactTest</div></div>
The Getting Started Guide shows how to save one attribute called "message" like this. Apparently, I do not know what to do when I have multiple attributes to update:
import { useBlockProps } from '#wordpress/block-editor';
export default function save( { attributes } ) {
const blockProps = useBlockProps.save();
return <div { ...blockProps }>{ attributes.message }</div>;}

The error indicates an issue with the save() function; however this is a little misleading. The content mismatch between save and post body is actually caused by how the attribute selector in block.json is defined as "div" for both theNotes and theContact.
Given the attributes selector defined is not a unique element (<div>...</div> appears 3 times in the saved content), the method used to extract data from the post content gets multiple matches so both attributes save the text content of <div> and <div> again. This results in the "notesTestcontactTest" text seen in the error message.
block.json (current)
"attributes": {
"theNotes": {
"type": "string",
"source": "text", // the value saved for theNotes is the text
"selector": "div", // within this matching selector
"default": ""
},
"theContact": {
"type": "string",
"source": "text", // the value save for theContact is the text
"selector": "div", // with the same matching selector as theNotes
"default": ""
}
}
By adding a unique class name to div selector of both attributes, the values will save and update correctly, eg:
block.json (updated)
"attributes": {
"theNotes": {
"type": "string",
"source": "text",
"selector": "div.the-notes", // a div with the className "the-notes"
"default": ""
},
"theContact": {
"type": "string",
"source": "text",
"selector": "div.the-contact", // a div with the className "the-contact"
"default": ""
}
}
The RichText component would be more suitable than using TextControl for editing content to be saved as text of block-level elements like <div>, eg:
edit.js
import { useBlockProps, RichText } from '#wordpress/block-editor';
export default function Edit( { attributes, setAttributes } ) {
return (
<div { ...useBlockProps() }>
<RichText
tagName="div" // Output as a <div>, no extra div needed
value={ attributes.theNotes }
onChange={ ( content ) => setAttributes( { theNotes: content } ) }
placeholder="Enter Notes.." // Optional
/>
<RichText
tagName="div" // Output as a <div>, no extra div needed
value={ attributes.theContact }
onChange={ ( content ) => setAttributes( { theContact: content } ) }
placeholder="Enter Contact.." // Optional
/>
</div>
)
}
The save() function would then also need updating to ensure the attributes selector finds the right match to extract the props.attributes values. RichText.Content can be used in save to render the tags markup and content:
save.js
import { useBlockProps, RichText } from '#wordpress/block-editor';
export default function save( { attributes } ) {
const blockProps = useBlockProps.save();
return (
<div { ...blockProps }>
<RichText.Content
tagName="div"
className="the-notes" // Used for the selector
value={ attributes.theNotes }
/>
<RichText.Content
tagName="div"
className="the-contact" // Used for the selector
value={ attributes.theContact }
/>
</div>
)
}
The RichText tagName could also be another block-level element like "h2" or "p" as long as the attributes selector matches. When retesting these changes to your block, remove the previous block from the Editor, save, refresh/clear the cache then insert your block again. To confirm the attributes save corrently, in the Editor use the Code view to see the markup that will be saved, it should be something like:
<!-- wp:create-block/editor-notes -->
<div class="wp-block-create-block-editor-notes"><div class="the-notes">Notes Test</div><div class="the-contact">Contact Test</div></div>
<!-- /wp:create-block/editor-notes -->

Related

Missing analytics attribute on result

apollo: {
analytics: gql `
query getAnalyticsViews {
viewer {
zones(filter: { zoneTag: "tag" }) {
httpRequestsAdaptiveGroups(
filter: {
clientRequestHTTPHost: "domain.co.uk"
date_geq: "2023-01-01"
date_leq: "2023-01-24"
requestSource: "eyeball"
}
limit: 1
) {
sum {
visits
edgeResponseBytes
}
}
}
}
}
`
}
The above is my Apollo GraphQL query, which works fine in Altair and returns the below:
{
"data": {
"viewer": {
"zones": [
{
"httpRequestsAdaptiveGroups": [
{
"sum": {
"edgeResponseBytes": 4542764000,
"visits": 1200
}
}
]
}
]
}
},
"errors": null
}
This is my views component where I am trying to access the sum, visits:
<div v-if="analytics">
{{ analytics.viewer.zones[0].httpRequestsAdaptiveGroups[0].sum.visits }}
</div>
In console I can see the data I need under said structure which I copied:
analytics.viewer.zones[0].httpRequestsAdaptiveGroups[0].sum.visits
but I am getting error as shown below:
Missing analytics attribute on result
No idea why this isn't working, I've tried conditionally loading it, but hasn't helped, as I said above the data sums, visits is in console(see image), but not in the component :(
Fixed:
The issue I was facing was:
analytics: gql `
query getAnalyticsViews {
viewer {
Needed to be:
viewer: gql `
query getAnalyticsViews {
viewer {

How do I render my results using graphql in Vuejs

I am learning graphql and using strapi as a backend nuxt as a front end
I have set up the backend and am now trying to display the results
I have the following code, it is returning the results but I cannot for the life of me figure out how to display just the name field, can you assist
<template>
<div>
<!-- Events are displayed here -->
<div
v-for='organisation in organisations'
:key='organisation.id'
>
test {{ organisation }}
</div>
</div>
</template>
<script>
import gql from "graphql-tag";
export default {
data() {
return {
};
},
apollo: {
organisations: gql`
query organisations {
organisations {
data {
attributes {
name
}
id
}
}
}`
}
};
</script>
returns
test [ { "attributes": { "name": "Organisation 1", "__typename": "Organisation" }, "id": "1", "__typename": "OrganisationEntity" }, { "attributes": { "name": "test2", "__typename": "Organisation" }, "id": "2", "__typename": "OrganisationEntity" } ]
test OrganisationEntityResponseCollection
if i try {{ organisation.name }} it returns no error but nothing displayed, if I try {{ organisation.attributes.name }} i get an error
Thanks
Ah, I should have had
v-for='organisation in organisations.data'
in my v-for, now working

Vue3 apollo displaying query results using composition API

So I am using vue router and trying to display the graphql results on my page.
Here is the router link on index.js
{
path: '/clients/:id',
name: 'client_profile',
component: () => import('../views/client_profile.vue')
},
The link to the page is from a table with this code using the router-link, where an id is passed as a param:
<router-link :to="{ name:'client_profile', params: { id: data.id }}"> {{data.name}}
</router-link>
The dynamic link looks like this on the setup(), where the useQuery loads my query clientEntity:
setup(){
const route = useRoute(),
id = computed(() => route.params.id)
const { result } = useQuery(clientEntity,{
id: id.value,
})
const client = computed(() => result.value?.entities[0])
return{
client,
result
}
}
On the HTML if I put in {{client}} or {{result}} I get the following:
//results
{ "entities": [ { "address": "555 Fake Street", "name": "Test1", "notes": "", "phone": null } ] }
//client
{ "address": "555 Fake Street", "name": "Test1", "notes": "", "phone": null }
But if I try accessing some of the data like {{client.address}} or {{client.name}} the page turns blank and I get the following error:
[Vue warn]: Unhandled error during execution of render function
at <BaseTransition appear=false persisted=false mode=undefined ... >
at <Transition name="p-toggleable-content" >
at <Panel header="Description" toggleable=true style=
Object { "text-align": "left" }
>
at <ClientProfile onVnodeUnmounted=fn<onVnodeUnmounted> ref=Ref< undefined > >
at <RouterView>
at <App>
at <App>

SurveyJS Not Displaying

Trying to use surveyjs with laravel and vue, but when I try to get the survey from a api does not work.
I'm getting the data from a api in laravel controller.
surveyshow.vue
<template>
<div>
<survey :survey="survey"></survey>
</div>
</template>
created () {
...
let url = `/api/edit/i130`;
axios
.get(url)
.then(response => {
surveyJson = JSON.parse(response.data.data.json);
console.log(JSON.parse(response.data.data.json));
})
.catch(function (error) {
console.log(error);
});
this.survey = new SurveyVue.Model(surveyJson);
if I replace the variable using a constant works.
var surveyJson = {
pages: [
{
"name": "Address",
"title": "Address",
"questions": [
{
"type": "text",
"name": "address1",
"title": "Street Address",
"autocompleteAs": "placeautocomplete"
}, {
"type": "text",
"name": "address2",
"title": "Address Line 2"
}
]
}
]
};
You have a problem with the asynchronous aspect of your code. There is a callback function, which runs when the request to your api completes:
...
.then(response => {
surveyJson = JSON.parse(response.data.data.json);
console.log(JSON.parse(response.data.data.json));
})
...
but you are trying to load the survey json "outside" of it. In other words, this runs before the api request's callback has had a chance to load the survey's JSON:
...
this.survey = new SurveyVue.Model(surveyJson);
...
You should move the line that instantiates your survey inside the callback, like this:
...
.then(response => {
surveyJson = JSON.parse(response.data.data.json);
window.survey = new SurveyVue.Model(surveyJson);
})
...

Can't display images sourced from GraphQL, error [gatsby-plugin-image] missing image prop

I'm trying to source content with images from Contentful into Gatsby but I failed to get images displayed.
I installed gatsby-transformer-sharp, gatsby-plugin-image, gatsby-plugin-sharp, gatsby-remark-images and gatsby-remark-images-contentful.
down below is a simple of my code
import { GatsbyImage, getImage } from "gatsby-plugin-image"
const Projects = ({ data }) => {
const projects = data.projects.nodes
return (
<Layout>
<Seo
title={"Projects"}
description={"Projects & Websites I've Developed"}
/>
<div className={styles.portfolio}>
<h1>My Portfolio</h1>
<h2>Projects & Websites I've Developed</h2>
<div className={styles.projects}>
{projects.map(project => (
<Link
to={"/projects/" + project.slug}
key={project.id}
className={styles.project}
>
<GatsbyImage
image={getImage(project.thumb)}
alt={project.title}
/>
<div className={styles.cardText}>
<h3>{project.title}</h3>
<p>{project.stack}</p>
</div>
</Link>
))}
</div>
</div>
</Layout>
)
}
export default Projects
export const query = graphql`
query ProjectsPage {
projects: allContentfulProjects(sort: { fields: date, order: DESC }) {
nodes {
key
slug
stack
title
thumb {
gatsbyImageData(placeholder: BLURRED, layout: FULL_WIDTH)
}
id
}
}
}
`
here what i got from GraphQL
{
"data": {
"projects": {
"nodes": [
{
"key": "project",
"slug": "portfolio-website",
"stack": "html - css - javascript",
"title": "Portfolio Website",
"thumb": [
{
"gatsbyImageData": {
"images": {
"sources": [
{
"srcSet": "https://images.ctfassets.net/kj59ethbquzj/1qDaw8RjPxxhzjOehxHz1g/152307656408e0efcf7c907a59cd91a7/personal-portfolio-website.png?w=750&h=361&q=50&fm=webp 750w,\nhttps://images.ctfassets.net/kj59ethbquzj/1qDaw8RjPxxhzjOehxHz1g/152307656408e0efcf7c907a59cd91a7/personal-portfolio-website.png?w=1080&h=520&q=50&fm=webp 1080w,\nhttps://images.ctfassets.net/kj59ethbquzj/1qDaw8RjPxxhzjOehxHz1g/152307656408e0efcf7c907a59cd91a7/personal-portfolio-website.png?w=1366&h=658&q=50&fm=webp 1366w,\nhttps://images.ctfassets.net/kj59ethbquzj/1qDaw8RjPxxhzjOehxHz1g/152307656408e0efcf7c907a59cd91a7/personal-portfolio-website.png?w=1920&h=925&q=50&fm=webp 1920w",
"sizes": "100vw",
"type": "image/webp"
}
],
"fallback": {
"src": "https://images.ctfassets.net/kj59ethbquzj/1qDaw8RjPxxhzjOehxHz1g/152307656408e0efcf7c907a59cd91a7/personal-portfolio-website.png?w=1920&h=925&q=50&fm=png",
"srcSet": "https://images.ctfassets.net/kj59ethbquzj/1qDaw8RjPxxhzjOehxHz1g/152307656408e0efcf7c907a59cd91a7/personal-portfolio-website.png?w=750&h=361&q=50&fm=png 750w,\nhttps://images.ctfassets.net/kj59ethbquzj/1qDaw8RjPxxhzjOehxHz1g/152307656408e0efcf7c907a59cd91a7/personal-portfolio-website.png?w=1080&h=520&q=50&fm=png 1080w,\nhttps://images.ctfassets.net/kj59ethbquzj/1qDaw8RjPxxhzjOehxHz1g/152307656408e0efcf7c907a59cd91a7/personal-portfolio-website.png?w=1366&h=658&q=50&fm=png 1366w,\nhttps://images.ctfassets.net/kj59ethbquzj/1qDaw8RjPxxhzjOehxHz1g/152307656408e0efcf7c907a59cd91a7/personal-portfolio-website.png?w=1920&h=925&q=50&fm=png 1920w",
"sizes": "100vw"
}
},
"layout": "fullWidth",
"width": 1,
"height": 0.4817708333333333,
"placeholder": {
"fallback": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAMAAACDi47UAAABv1BMVEUaGhosLSN+gm9ORjIcHBwlJB8kJBgkJBclJBgeHRREORVFWEeEj5SNingqNys+x8tWzNFk1Nhq1dl42Nx51dp219xw19twxMhjyMxKys89x8wnZlw7MhUiIRtrKiRCLCBHys5bztJu1tuD2t501tp62N1519yokpF3b25My9A/yc4oZ10VFRUkJCQoKChHR0c1NTUqKSotOCxEw8dbxcllys1owcR30dR2z9Nyz9NkyM2+n52cjo0/wsc5vsMrXVNdTx2niSCnjCqniiOfgyAtOC1HsbVRq69errJcpah1y8982N1u1dpto7F7RlG7lpeLWXVFr7koYFYtKiJJQyxTTDZMRS9DPSssNytNk5VWf3VbfWVWdVlnsKxryc1ays+xJ06sCy2sCS2wCi2bJkYrXlUjIyMuLi48PDw0NDQsLCwrNyw6n6I/j5BJlZBJlJFavL5Xxco4v8XDEz+QCiSqCSuHCiCJDidhKS07OzszMzMeHRk4NSg7OCs5NSk5NSg6Nio5Nik7Nys6NyocGhI6OjkxMTEYGBgmJiUmJiYjJCQlJSUXFxcWFhYRERE0Lhw7NBxBORwpJRswMDA0NTUvbwraAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAB3RJTUUH5gUJBR4Qdhx/lQAAAGRJREFUCNeNzUEKglAAANEZ/5cgRLTO0ZWCTtS6jdfxEJ5BUNyF+FuIiNSit5zNiKuUAbBAMHLWqfQgctGJWsdqrze/9PH6Z+R3hNphe8z5HFIgQuFQqHbtHSVlyFN9n3w92DQfWKcTSK8wndgAAAAASUVORK5CYII="
what it might be wrong with My code? need some help :)
According to what you said in the comment section, I'd say that there's a position in projects that has no thumbnail defined (or not properly queried).
Try adding a condition wrapping the GatsbyImage display:
{project.thumb && <GatsbyImage
image={getImage(project.thumb)}
alt={project.title}
/>}
That said, check in which project you have no thumbnail.
Finally I have figured it out by
1- I removed all My content and images in Contentful CMS and create a fresh ones, It seemed there was a bug with sourcing Images into GraphQL where the Image src was always giving an empty array!!
2- Taking out getImage function and replace it with <GatsbyImage image={project.thumb.gatsbyImageData} alt={project.title}/>
,then gatsby clean to clear the .cashe and also triggered clean cashe and rebuild.
Now everything is works well.

Resources