In all examples below, metadata is an array to which the element gets added. If you're creating a function for nested output, this might not always suit your purposes - so adjust accordingly. See Nesting metadata for examples of nested JavaScript output.
Generic snippets
Basic function structure
window.myFuncName=function(item){let metadata = [];// Code snippets should be herereturn metadata = [];}
If you're writing a function that creates a flat output, you can simply add blocks by using metadata.push("String of block").
If you're writing a function that creates a nested output, you'll have to create intermediate Object blocks during the processing/formatting of the item's data.
Adding an element as block if it exists
if(item.<element_path>{ metadata.push(item.<element_path>);}// Example :// Item title, located under the item's `data` propertyif(item.data.title){ metadata.push(item.data.title);}
Attribute-specific snippets
data.creators
Goes through the item's list of creators, makes each creator's name into a page reference with format [[firstName lastName]], and adds the creator's role between parentheses after if the creator is not an author.
Returns : Author(s):: [[First Author]], [[Second Author]], [[An Editor]] (editor)
if (item.data.dateAdded){ metadata.push(`Date Added:: ${zoteroRoam.utils.makeDNP(item.data.dateAdded, {brackets:true})}`);};
data.itemType
Maps an item's type to its non-machine name, as specified in the user's or extension's typemap.
Returns : Type:: [[Book]]
// Use mapping specified in user-defined typemap, // otherwise fall back on typemap_defaultif (item.data.itemType) {let mapping =zoteroRoam.typemap[item.data.itemType] ||item.data.itemType;if(zoteroRoam.config.userSettings.typemap){ mapping =zoteroRoam.config.userSettings.typemap[item.data.itemType] || mapping; }metadata.push("Type:: "+"[["+ mapping +"]]");}
if (item.data.itemType){metadata.push(`Type:: [[${zoteroRoam.formatting.getItemType(item)}]]`);};
data.tags
Goes through the list of the item's tags, and make every tag into a Roam tag with format #[[tag]] to support multi-word tags.
Returns : Tags:: #[[tag1]], #[[tag2]], #[[a multi-word tag]]
if (item.data.tags.length>0){metadata.push(`Tags:: ${zoteroRoam.formatting.getTags(item)}`);};
Additional data (collections, PDFs, notes...)
Getting an item's collections
Collections data is requested along with items data, and is stored in the zoteroRoam.data.collections object. You can inspect its contents using the console if you'd like.
This allows the extension to identify which collections an item belongs to - and that's what the zoteroRoam.formatting.getItemCollections function does :
// This is taken directly from the extension's source code :zoteroRoam.formatting.getItemCollections(item){if(item.data.collections.length>0){ return item.data.collections.map(collecID => zoteroRoam.data.collections.find(collec => collec.key == collecID && collec.library.id == item.library.id));
} else {returnfalse; }}
The function returns an Array containing all the collections the item belongs to. The collections will be raw data objects, with all the data associated with a given collection.
You can use that output to obtain the collections' names, or any other information about them.
If you want to get data about PDF attachments + notes, it's recommended to include them in your data request. That requires asking for items (all of them) rather than items/top (top-level items) in your data URI, and (for notes) giving your API key access to notes (see Step 1.1).
The utility function zoteroRoam.formatting.getItemChildren can search for an item's children within the loaded dataset. If they're present, it will return them (as raw data or formatted output) ; if there aren't any, it will return 'false'. Lastly, in the case that the item has children but none are found in the dataset, the function will also return a remoteChildren: true property - which you can use to request them if you want to.
Basic syntax
// At the minimum, all you have to do is this :let children =zoteroRoam.formatting.getItemChildren(item);// It will call the getItemChildren function with default parameters :// pdf_as: "links"// notes_as: "formatted"// split_char: "\n"// You can also set those arguments explicitly, as a reminder :let children =zoteroRoam.formatting.getItemChildren(item, {pdf_as:"links", notes_as:"formatted", split_char:"\n"});
Function arguments
Argument
default
Possible values
What it does
pdf_as
"links"
"links" or "raw"
Determines the output format of PDF items :
"links" -> an Array of Markdown-style links to the PDFs
"raw" -> an Array containing the raw data for the PDF items, as returned by the Zotero API
notes_as
"formatted"
"formatted" or "raw"
Determines the output format of note items :
"formatted" -> an Array of Arrays, where each child Array contains one note item's contents
"raw" -> an Array containing the raw data for the note items, as returned by the Zotero API
split_char
"\n"
Anything
Determines how to split notes into blocks :
"\n" will split by newline ;
"</p>" will split by paragraph ;
...
What the function returns
// If the item has no PDFs, no notes :{ pdfItems:false, notes:false}// If the item has PDFs + notes :{ pdfItems: [...],// Each element = one PDF item notes: [[...], [...]] // Each element = one note item}// PDFs and notes are searched & processed separately// If there are no results, the value returned is 'false'// If there are 1+ results, the value returned is an Array// => Structure & value will depend on the function's arguments,// but pdfItems will be an Array and notes will be an Array of Arrays
// In the case where the item is known to have children,// but none are found in the loaded dataset,// this is what the function will return :{ pdfItems:false, notes:false, remoteChildren:true}
How to use the function in your code
To make use of this function, there are 2 important things :
1) know what output format you're asking for (cf. function arguments above),
2) begin by checking if there is any data on the children.
This is how the built-in function zoteroRoam.formatting.getItemMetadata handles this :
// Let's say we're creating a custom function called customPaperFormat :window.customPaperFormat=function(item){let metadata = [];// ... let children = zoteroRoam.formatting.getItemChildren(item, {pdf_as: "links", notes_as: "formatted", split_char: "\n"});
if(children.pdfItems){ // This checks that pdfItems is not 'false'// We asked for Markdown links, so children.pdfItems will be an Array of string elements// Here we're putting them together in one comma-separated String// If there is only one, this will simply return the one linkmetadata.push(`PDF links : ${children.pdfItems.join(", ")}`); }if(children.notes){ // This checks that notes is not 'false'// Let's make an intermediate object, in order to nest the notes under [[Notes]] :let notesBlock = {string:`[[Notes]]`, children: []};// We asked for formatted notes, so 'notes' will be an Array of Arrays// Here we're making a flat Array (because we don't care that the annotations come from different note items)// And then we push the notes Array to the 'children' property of our notes BlocknotesBlock.children.push(...children.notes.flat(1));// Finally we add notesBlock to the metadata arraymetadata.push(notesBlock); }// ...return metadata;}
This is what the code above will output as Roam blocks :
PDF links : [First PDF attachment.pdf](link to this PDF), [Second PDF attachment.pdf](link to that PDF)
[[Notes]]
Block from note 1
Another block from note 1
Block from note 2
Another block from note 2
If there are only PDFs :
PDF links : [First PDF attachment.pdf](link to this PDF), [Second PDF attachment.pdf](link to that PDF)
If there are only notes :
[[Notes]]
Block from note 1
Another block from note 1
Block from note 2
Another block from note 2
Nothing will get added to the metadata array, and so no Roam blocks will be created.