Hey, I was wondering if it is possible to add some sort of meta data or property information to a node, object or group? For example if I had a group of objects that made up a button component on an art-board, could I add some data to that group to say that this is a button? Then in my plugin panel, I would provide some options relevant to button objects.
Hi @HanielMoore,
SceneNode::pluginData
should get you what you’re looking for: scenegraph · Adobe XD Plugin Reference
This is a simple property every SceneNode
has which you can use to read and write metadata:
mySceneNode.pluginData = { /* [...] */ };
console.log(mySceneNode.pluginData);
I hope this helps,
Happy coding ,
Pablo
Hi Pablo,
Thank you for your reply! So this would be something like the below?
mySceneNode.pluginData = {
“type”: “button”
};
Correct. pluginData
could be any (I believe JSON-serializable) type, but objects are, of course, great here as you can store multiple values in a key-value-pair manner. If, however, the only thing you ever store is the type, you could equally use
mySceneNode.pluginData = 'button'; // or whatever other type ;-)
Does a simple string work? I thought it had to be a string in JSON format .
Metadata specific to your plugin. Must be a value which can be converted to a JSON string, or undefined to clear the stored metadata on this node.
var object = {type: "button"};
mySceneNode.pluginData = JSON.stringify(object); // or whatever other type ;-)
Hi @Velara,
According to RFC 7159 (and www.json.org), JSON text is defined as
JSON-text = ws value ws
where
value = false / null / true / object / array / number / string
Therefore, JSON can also have a string or number as “root”, meaning "some string"
is as valid in JSON as, e.g., { a: "some value" }
.
Therefore, a string does work as it actually is JSON. The only thing that won’t work are ring-structures (a
is a property of b
and b
is a property of a
) and functions, which, of course, can’t get serialized as JSON.
According to the documentation, however, it could be any type:
sceneNode.pluginData :
*
Therefore, it is (in my opinion), fair to conclude that any JSON-serializable type should work here and then automatically gets serialized and deserialized…
So XD is converting the object into JSON in it’s setter…
I read it as:
Must be converted to a JSON string
I’ve been doing that manually. :doh:
var myObject = {a: "button", b: "name" };
sceneNode.pluginData = JSON.stringify(myObject);
Note to self: don’t read documentation half awake or half asleep.
I mean, in truth, it’s probably much easier to debug that way. Otherwise, if something ever went wrong in the XD-internal JSON (de-/) serialization, that could be a pretty difficult problem to track down…
Further to this, how would I go about modifying a property once it is set? I have tried a couple of different ways and it doesn’t seem to work. One of the ways I tried was:
mySceneNode.pluginData.mode = “dark”;
Any suggestions?
PluginData is stored as a string in JSON format. So you would parse the plugin data back into an object first.
var data = {};
// get existing plugin data
if (sceneNode.pluginData!=null && sceneNode.pluginData!="") {
data = JSON.parse(sceneNode.pluginData);
// verify its an object or the type you expect here
}
// update data
data.mode = "dark";
// update plugin data value
sceneNode.pluginData = data; // object is converted by XD API into JSON string
sceneNode.pluginData = JSON.stringify(data); // or you convert it to string
Hi Velara, thank you for your reply. I’m pretty sure it is already an object. I have tested with the following which states it is an object.
console.log(typeof button.pluginData);
Here is what I use:
/**
* Get data saved on the pluginData property or null if not set
* @param {SceneNode} item
* @returns {Object} data
**/
function getSceneNodePluginData(item) {
var data = null;
try {
if (item.pluginData) {
var value = item.pluginData;
data = JSON.parse(value);
}
}
catch(error) {
log(error);
}
return data;
}
/**
* Save data to pluginData property using a string value in JSON format
* @param {SceneNode} item
* @param {String} value
* @returns {Boolean|Error} returns true if set or Error if cannot set plugin data
**/
function setSceneNodePluginDataValue(item, value) {
try {
item.pluginData = value;
return true;
}
catch(error) {
return error;
}
}
More complicated set scene node data:
/**
* Save data to pluginData property
* @param {SceneNode} item
* @param {Object} data
* @param {String} editLabel
* @returns {Boolean|Error} returns true if set or false or Error if cannot set plugin data
**/
function setSceneNodePluginData(item, data, editLabel = null, showingPanel = false) {
const { root, selection } = require("scenegraph");
const { editDocument } = require("application");
try {
if (showingPanel) {
editDocument( () => {
item.pluginData = JSON.stringify(data);
});
}
else {
item.pluginData = JSON.stringify(data);
}
return true;
}
catch(error) {
var errorString = error + "";
if (errorString.indexOf("Panel plugin edits must be initiated from a supported UI")!=-1) {
// Call is from panel. Needs to wrap in editDocument(). Set showingPanel
}
else if (errorString.indexOf("is not permitted to make changes from the background.")!=-1) {
// call must be part of click event? or promise not completed (or await async related)
}
else if (errorString.indexOf("There are no edit records found.")!=-1) {
// ?
}
else {
log(error);
}
return error;
}
return false;
}
You don’t have to manually convert into JSON strings – as the docs say, you can directly use any type which is JSON-compatible …as in the post above from @HanielMoore:
mySceneNode.pluginData = {
type: "button"
};
However, as with any object-valued property on scenenodes, you can’t trigger an update just by overwriting one field of that object. You need to re-invoke the pluginData
setter:
var data = sceneNode.pluginData;
data.mode = "dark";
sceneNode.pluginData = data;
(or you can make this into a one-liner using Object.assign()
)
Plus I imagine doing your own JSON-to-string-to-JSON conversion could easily miss optimizations that the direct .plugin
setting could do.
Thanks @peterflynn that works perfectly for what I was trying to do!