changed attributes not waving with Wordpress block - reactjs

I have a fairly simple block in a plugin, built using npx #wordpress/create-block pn-DisplayBlocks. Attributes, in block.json, are:
(code changed as per fixes below)
"attributes": {
"pid": {
"type": "text",
"default": "444"
}
}
save.js is:
import { __ } from '#wordpress/i18n';
import { useBlockProps } from '#wordpress/block-editor';
export default function Save( {attributes} ) {
console.log( "SAVING", attributes )
return (
<p { ...useBlockProps.save() }>
{ __( 'Pn Image – hello from the saved content! ' + attributes.pid, 'pn-image' ) }
</p>
);
}
and edit.js is:
import { __ } from '#wordpress/i18n';
import { useBlockProps } from '#wordpress/block-editor';
import './editor.scss';
import { __experimentalNumberControl as NumberControl } from '#wordpress/components';
export default function Edit( {attributes, setAttributes} ) {
console.log( "pid", attributes.pid )
return (
<p { ...useBlockProps() }>
{
__( 'Pn Image – hello from the editor! ' + attributes.pid, 'pn-image' )
}
<NumberControl
label="PID"
isShiftStepEnabled={ true }
onChange={ value => { setAttributes( {pid: value})} }
shiftStep={ 10 }
value={ attributes.pid }
/>
</p>
);
}
NumberControl value updates pid and the block with the correct number value when changed, that all works fine, but if I preview the page I see the default value of pid showing, not the one I've just change it to. Same if I update the page to save it; console.log shows the new pid value but when reloaded into the edit page, pid reverts to the default value. Maybe I'm staring too hard but for the life of me I can't see what's wrong. Also, I'm fairly new to this so I may be doing something idiotic.
save & edit called from registerBlockType in index.js:
import { registerBlockType } from '#wordpress/blocks';
import './style.scss';
import Edit from './edit';
import Save from './save';
registerBlockType( 'pn-displayblocks/pn-image', {
edit: Edit,
save: Save,
} );
Attributes in block.json:
{
"apiVersion": 2,
"name": "pn-displayblocks/pn-image",
"version": "0.1.0",
"title": "Pn Image",
"category": "widgets",
"icon": "smiley",
"description": "Example block written with ESNext standard and JSX support – build step required.",
"supports": {
"html": false
},
"textdomain": "pn-displayblocks",
"editorScript": "file:./build/index.js",
"editorStyle": "file:./build/index.css",
"style": "file:./build/style-index.css",
"attributes": {
"pid": {
"type": "text",
"default": "444"
}
}
}

Did you try to store your attributes in comments of block ?
In according with documentation gutenberg :
The available source values are:
– (no value) – when no source is specified then data is stored in the block’s comment delimiter.
– attribute – data is stored in an HTML element attribute.
– text – data is stored in HTML text.
– html – data is stored in HTML. This is typically used by RichText.
– query – data is stored as an array of objects.
– meta – data is stored in post meta (deprecated)
Can you try with ?
"pid": {
"type": "text",
"default": "444"
}

Related

How to map through a json object that is stored in a react component not coming from an api?

I have a file inside my react project, glossaryItems.json. The file looks like this:
{
"glossary": [
{
"name": "Constant",
"pageNumber": "33",
"definition": "A value that cannot change while the program is running.",
},
{
"name": "Debugging",
"pageNumber": "45",
"definition": "The process of finding and reducing the number of defects in a computer program.",
},
{
"name": "Algorithm",
"pageNumber": "4",
"definition": "A strictly defined finite sequence of well-defined statements that provides the solution to a problem."
}
]
}
I have another file, glossaryPage.tsx where I would like to display each glossary item within a tab. I am not sure how to access the json file in order to use it within the tsx file. I ended up changing the json file to a .ts file and exported it as so:
export const glossaryItems =
[
{
"glossary": [
{
"name": "Constant",
"pageNumber": "33",
"definition": "A value that cannot change while the program is running.",
},
{
"name": "Debugging",
"pageNumber": "45",
"definition": "The process of finding and reducing the number of defects in a computer program.",
},
{
"name": "Algorithm",
"pageNumber": "4",
"definition": "A strictly defined finite sequence of well-defined statements that provides the solution to a problem."
}
]
}
]
And then imported it inside glossaryPage.tsx. I want to be able to get the each part of the json separately to be able to use it inside the tabs. So I would have one tab labeled "Constant", a second tab, "Debugging", a third tab "Algorithm" and under each tab display that information such as pagenumber and definition that applies to that tab. I tried mapping over just the glossary but was unable to. I had to map over the glossaryItems.
const GlossaryPage = () => {
const terms = glossaryItems.map(({glossary}, key) => (
<div key={key}>
{glossary.map(({name, pageNumber, definition}, key) => (
<div key={key}>
<p>{name}</p>
</div>
))}
</div>
))
return (
<SprkTabsPanel
isDefaultActive
tabBtnChildren={terms[0]} //this is where the terms are printing out on the tab
tabBtnAnalyticsString="tab-1"
tabBtnDataId="tab-1"
>
</SprkTabsPanel>
I thought that by indexing the terms it would give me the term at that index but it gives me all of the terms. This is what it looks like:
How can I get the individual values of the object?
Any help would be greatly appreciated!
Importing JSON
In order to import a .json file, you simply need to enable support in your tsconfig.json file. Set "resolveJsonModule": true inside the compilerOptions property. Now you can import the data from the JSON file as a default import.
Docs: Resolve JSON Module
Mapping Your Object
I had a look at the documentation for the Spark Design system and it seems like you need to create a separate SprkTabsPanel component for each tab. All of the individual tab panels go inside of one SprkTabs component.
import React from "react";
import { SprkTabs, SprkTabsPanel } from "#sparkdesignsystem/spark-react";
import glossaryItems from "./glossaryItems.json";
const GlossaryPage = () => {
return (
<SprkTabs idString="glossary-tabs">
{glossaryItems.glossary.map(({ name, pageNumber, definition }, key) => (
<SprkTabsPanel tabBtnChildren={name} key={key}>
<p>{definition}</p>
<p>Page Number: {pageNumber}</p>
</SprkTabsPanel>
))}
</SprkTabs>
);
};
export default GlossaryPage;

Not able to Query hyperlinks from GraphQL on Gatsby JS

I'm working on a Blog on Gatsby JS, and I'm having an issue with the hyperlinks, and I'm not able to solve it.
I have the following query
const options = {
renderNode: {
"embedded-asset-block": (node) => {
const alt = node.data.target.fields.title['es']
const url = node.data.target.fields.file['es'].url
return <img alt={alt} src={url} />
},
[INLINES.HYPERLINK]: (node) => {
if(node.data.uri.indexOf('youtube.com') !== -1){
return(
<iframe width="560" height="315" src={node.data.uri} frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
)
}else{
return(
<a href={node.data.uri}>{node.content.value}</a>
)
}
}
}
}
From this query, I'm able to add assets and youtube videos. The issue is, that when I add a hyperlink, they are shown on the page but without text.
I only see the and not the text of this link.
I know the problem is here <a href={node.data.uri}>{node.content.value}</a>, but I'm not able to query the value.
This is GraphQL:
"body": {
"json": {
"nodeType": "document",
"data": {},
"content": [
{
"nodeType": "paragraph",
"content": [
{
"nodeType": "text",
"value": "Desde ",
"marks": [],
"data": {}
},
{
"nodeType": "hyperlink",
"content": [
{
"nodeType": "text",
"value": "FelixDigital",
"marks": [],
"data": {}
}
I need to query the value (FelixDigital)
Does anybody can help me?
Thank you so much,
Can you do a debugger inside this code?
It's hard to say.. but what I would check is the value of node. To me it looks like content is an array and inside the first item of the array you have value so to me it should look more like node.content[0].value.
I'm guessing you are using Contentful to pull your data into your blog using GraphQL. With Contentful you must use documentToReactComponents on JSON nodes (all rich text items). To obtain FelixDigital, you may want to do something instead like:
import { documentToReactComponents } from '#contentful/rich-text-react-renderer';
return(
<div>
{documentToReactComponents(videos.videoItem.videoLinks.json, options)}
</div>
)
This will then use the iframe you set up in const options and grab the src uri.
More info can be found here on rendering rich text with contentful

Strange behaviour of TypeScript String.split() function

I have TypeScript model like this:
export class Product {
id:number;
name:string;
brand:Brand;
price:number;
shippingPrice:number;
description:string;
photoName:string;
productType:ProductType;
purchaseCounter:number;
rating:number;
volume:string;
ingredients:string;
}
and json file which populate this model:
{
"id": 1,
"name": "xxx",
"description": "xxx",
"price": 12.34,
"purchaseCounter": 12,
"photoName": "xx",
"shippingPrice": 12.99,
"volume": "xxx",
"rating": 4.7,
"ingredients": "A,B,C",
"brand": {
"id": 1,
"name": "xx"
},
"productType": {
"id": 3,
"name": "xxx"
}
}
Now in my TypeScript component I have function like this :
public getIngredients():String [] {
return this.product.ingredients.split(",");
}
Everytime when I am invoking this function I have error:
"TypeError: Cannot read property 'split' of undefined"
but when i change body of function to sth like this:
public getIngredients():String [] {
if (this.product.ingredients == null) {
return null;
}
return this.product.ingredients.split(",");
}
then eveyrthing is ok and split function work properly. Have You got any idea why checking if ingredients is not null fix it? I have to admin that I just start my adventure with js and ts. Thanks
UPDATE
I instantiating this Product variable here:
export class ProductOverviewComponent implements OnInit {
private product:Product;
private errorMessage:string;
constructor(private productService:ProductService) {
this.product = new Product();
}
ngOnInit():void {
this.productService.getProduct()
.subscribe(
product => this.product = product,
error => this.errorMessage = <any>error);
}
}
For now I hit to the json file but in future I will hit to server. Another think is that I pass product to another comopnent using #Input().
And this is how i call getIngredients function
<div class="col-md-12">
<div class="property-text">
<!--<h3 class="h3style">Ingredients</h3>-->
<ul>
<li *ngFor="let ingredient of getIngredients()">
{{ ingredient }}
</li>
</ul>
</div>
</div>
The TypeError that you get is a JavaScript error raised at runtime and has nothing to do with TypeScript. It happens because this.product.ingredients is undefined. Your "fix" works because undefined == null is true in JavaScript which results in an early return inside getIngredients(). If you used the === operator to compare against null your fix would no longer work.
However, the question is then why this.product.ingredients is undefined? It may be because the TypeScript this context is lost inside getIngredients(). From the code you have provided it is impossible to determine if that is the case but there is a nice write-up about the problem in the 'this' in TypeScript article on Github which might help you solve your problem. A simple first check could be to add console.log(this) inside getIngredients() to see what this really is.

ng-repeat for array of object of objects

I am working on angular-meteor.
I have a collection called grace in the following json format
grace: {
"mail-template": {
"new-status": {
"in-progress": {
"name": "demo1",
"description": "this is demo"
},
"pending": {
"name": "demo2",
"description": "this is another demo"
}
}
}
}
Here I am taking in-progress and pending as keys, name and description as values.
I want to list this inside my html using ng-repeat. But I didn't get any output and no errors found.
The code I used is
ng-repeat="(key,value) in demoCtrl.formSettings.grace['mail-template']['new-status']"
demoCtrl is my controller name and formSettings is name of helpers.
Can somebody clear my code?
I made this JSFiddle. It seems to work just fine. Make sure that you've got the correct data in the demoCtrl.formSettings.
JS
$scope.obj = {
grace: {
"mail-template": {
"new-status": {
"in-progress": {
"name": "demo1",
"description": "this is demo"
},
"pending": {
"name": "demo2",
"description": "this is another demo"
}
}
}
}
};
HTML
<div ng-repeat="(key,value) in obj.grace['mail-template']['new-status']">
{{key}}: {{value}}
</div>

Export GPX file from Leaflet

What I want to do is let the users create a GPX file by selecting some GeoJson features in Leaflet. The way in which I'm doing it is by creating a new GeoJson layer to store the selected features, then converting this to gpx with a plugin called togpx (https://github.com/tyrasd/togpx). Now I have a gpx file, but I don't know how can I let the users download it. Any suggestions? Here's my code:
var GPXfile;
var trails = new L.GeoJSON.AJAX('data/trasee.geojson', {
onEachFeature: function(feature, layer) {
layer.on({
click: function () {
var selectedGeojson = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"name": "Rocka Rolla"
},
"geometry": {
"type": "LineString",
"coordinates": feature.geometry.coordinates
}
}]
}
GPXfile = togpx(selectedGeojson);
}
})
}
}).addTo(map);
A JsFiddle might help: http://jsfiddle.net/pufanalexandru/20ara4qe/1/
You can try that ...
A link to trigger the dowload:
Export to file
Some javascript (you have to include jquery):
$("#exportGPX").on('click', function (event) {
// prepare the string that is going to go into the href attribute
// data:mimetype;encoding,string
data = 'data:application/javascript;charset=utf-8,' + encodeURIComponent(gpxData);
// set attributes href and target in the <a> element (with id exportGPX)
$(this).attr({
'href': data,
'target': '_blank'
});
// let the click go through
});
An example here: http://jsfiddle.net/FranceImage/vxe23py4/
Note: it works with Chrome, but you should try other browsers too.

Resources