Iām trying to achieve something but not sure itās possible ā¦
Iām retrieving a a list of records using the CMA API and I would like to extract the Modular content field inside and convert it as JSON compatible with the one you generate when āCopy to clipboardā command save it into the localstorage.
What Iām doing today is pretty simple:
const presets = await client.items.list({
filter: {
// Filtering by the model with api_key "cat" and the model with ID of "dog"
type: "preset",
},
nested: true
});
console.log('Presets Modular field called blocks', presets.blocks);
All my presets have a Modular field called blocks (an array of blocks)
I would like to transform this array into a compatible object with what you have in localstorage.
Is it possible?
At the moment, I can retrive an array like this one :
Iām in a plugin and I retrieve my records using the CMA. Iām not manipulating the ctx.item object but an object I retrieve from a query with CMA API.
Maybe Iām doing something wrong but, here is the object I get from my query:
Iām getting a similar error in your object, but Iām not sure whatās causing it. May I please fork your env to a sandbox and do more testing there? Something in your record, I think, is causing issues with the ctx.itemToFormValues() method, but I need to play around more to figure out what it is.
Generally speaking, the ctx.itemToFormValues() method SHOULD work even with something fetched from the CMA. For example, on a test project, this works for me:
Source code for example plugin (click to open)
import {Canvas} from 'datocms-react-ui';
import {RenderItemFormSidebarPanelCtx} from "datocms-plugin-sdk";
import {useEffect, useState} from "react";
import {buildClient} from "@datocms/cma-client-browser"
// The block shape used by the "Copy to clipboard" functionality that gets saved to localStorage
export type DeserializedBlock = { itemId: string; itemTypeId: string };
// Our plugin doesn't necessarily have to be a sidebar; it just has to have ctx.itemToFormValues()
export function Sidebar({ctx}: { ctx: RenderItemFormSidebarPanelCtx }) {
const recordIdToFetch = "Qjv5J71QS3qYP960vNfQHg"; // From a sample project
const [blocksFromOtherRecord, setBlocksFromOtherRecord] = useState<DeserializedBlock[] | null>(null);
useEffect(() => {
(async () => {
try {
// Make sure to only use this plugin with trusted editors, because your API key is exposed to them on the client
const client = buildClient({apiToken: import.meta.env.VITE_DATOCMS_API_KEY})
// Different from items.find(); this returns a slightly shape that's easier to use with itemToFormValues()
const response = await client.items.rawFind(recordIdToFetch, {nested: true})
// Get the actual record from the response
const {data} = response
console.log('Original response', response)
// Now we run that external record through the itemToFormValues() method from the plugin's ctx
const convertedItem = await ctx.itemToFormValues(data)
// Extract the blocks that we want
const blocks = convertedItem['blocks'] as DeserializedBlock[];
console.log('Converted blocks', blocks)
setBlocksFromOtherRecord(blocks)
} catch (error) {
console.error('Conversion error', error);
}
})()
}, [])
return (
<Canvas ctx={ctx}>
<h2>Your record</h2>
<code>{JSON.stringify(blocksFromOtherRecord)}</code>
</Canvas>
);
}
Given this original response from the CMAās items.rawFind():
We get back these blocks, which should match the format used by the localStorage copy:
blocks
[
{
"text": "This is a test",
"paragraph": "This is a paragraph",
"itemId": "BnX7APJ4QJGm7HCi3kQGyg",
"itemTypeId": "MYKbJS_kTX6xi8NCDr2woQ"
}
]
However, this same thing isnāt quite working for me on the object you provided. Could you please try it yourself with the rawFind() in your own project, or else let me fork that env and do more testing?
I did a quick test regarding your example and getting an error
const response = await client.items.rawFind("UjdCtTJCSJ6i-rwxOZBmUQ", {nested: true})
// Get the actual record from the response
const {data} = response
console.log('Original response', response)
// Now we run that external record through the itemToFormValues() method from the plugin's ctx
const convertedItem = await ctx.itemToFormValues(data)
console.log('Converted', convertedItem)
The console.log for original response is like this:
Quick note that Iāve forked your main environment into a sandbox called datocms-support-main-copy-2024-11-1 and will be troubleshooting there further. Please donāt edit or delete that environment for the time being Thanks!
Interim update: Hmm, unfortunately, it looks like ctx.itemToFormValues() isnāt as pure as I thought It loads some field data from the current model, which means that it only works while youāre inside the same item type (presets). If you try to load another modelās fields via the CMA and pass it to the that method in another model type, it will crash.
Iāll have to check with the devs on this. There might be a workaround by using ctx.editItem(yourPresetId) to load that other record, run ctx.itemToFormValues()there instead, and then either copy it to the clipboard or else save it into the plugin params (which can be reused across models)ā¦ but that is super clunky and fragile, not to mention results in a terrible UX.
Ideally there would just be a way for us to expose a pure function where you can give it a CMA input and itāll return a formValues output without needing to do its own lookups. Iām not sure if thatās possible right now; let me check with the devs and get back to you ASAP.
Weāve improved ctx.itemToFormValues() so that it can now correctly parse lookups from any model, not just its own. This means that inside a plugin, you can use the CMA to fetch another record and then run it through itemToFormValues() anywhere itās available and it should work.
The OP also had some other follow-up questions they sent over email, and weāll answer those there.