Attempted JXA of Custom Metadata does not work

Good point. The same can be seen on stack overflow, where people ask an innocent JavaScript question and get overwhelmed with jQuery stuff. Fortunately, that’s happening less now.

1 Like

Any better source which focuses on JXA?

I’m not aware of any books which deal with JXA specifically.

JXA has two parts:

  • JavaScript (quite a small language, and the Mozilla pages are the best source)
  • The Automation library which Apple makes available to the JXA JavaScript interpreter, which is mainly covered by the release notes.

(linked to here: JavaScript for Automation (JXA) Resources)

The methods and properties of the Automation library object are:

Automation
    Application
        currentApplication
    Library
    ObjC
        $
        Ref
            equals
        bindFunction
        block
        castObjectToRef
        castRefToObject
        deepUnwrap
        dict
        import
        interactWithUser
        registerSubclass
        super
        unwrap
        wrap
    ObjectSpecifier
    Path
    Progress
    delay
    getDisplayString
    initializeGlobalObject
    log

and the whole set of things under ObjC you can initially ignore really – they allow access to external Apple library functions.

1 Like

Better than my brilliant site? :stuck_out_tongue_winking_eye:

Seriously, there’s very little stuff out there on JXA. Many people probably were frustrated and have abandoned this ship. If you search for JXA, you’ll always stumble on the same, few and old sources. And sometimes you’ll be lucky and find a true gem helping tremendously with a problem.

OTOH, JXA differs, at its core, not very much from JavaScript. So, any text on JavaScript should help you find your ways in JXA. The main hurdles in JXA are the sometimes weird (for lack of a stronger term) interactions with Apple’s underlying software. Like this whole business with Object Specifiers, unclear usage of files and folders in Finder (just try to avoid them) and lack of some functionality in the ObjC bridge, notably code blocks and callbacks.

And since @winter beat me to it: The Automation library object contains a lot of useful stuff and some completely obscure (like block and dict). Most of that is only relevant if you want to interface with Apple frameworks like PDFKit and such.

1 Like

Exactly – it’s just the standard macOS JavaScript interpreter – no difference at all, in fact, other than the presence of the Automation library in the global name-space.

Hi @winter and @chrillek

I realized that this and a few other variations on it work when all the selected items have a value for the custom metadata field. But if the metadata field is blank/undefined in 1 or more selected DT3 records then the script fails. Why is that?

image

If the customMetaData field is empty, i.e. there’s no CMD defined for this record, then ...customMetaData() probably returns undefined. Your code doesn’t check for this, but tries to always access the mdattorneyemail1 field of the return value. You could do something like

const CMD = app.selectedRecords[i].customMetaData();
if (CMD && CMD.mdattorneyemail1) {
  // do something with CMD.mdattorneyemail1
} else {
  // do something else: Nothing, write a log message, have an alert popup
}

Alternatively, you could use the method getCustomMetadata instead of accessing the property directly.

Hence the .flatMap in the code shown earlier:

.flatMap(x => { // Execute the following lines for each element "x" of this Array
    const v = x.mdattorneyemail1; //get the value of the custommetadata field AttorneyEmail1

    return v ? [v] : []; //return a one-element array or an empty array nothing. The latter will be removed by flatMap, so that the final array only contains data for the non-empty metadata fields
})

which discards lines which return only an undefined (Boolean false) value for x.mdattorneyemail1

But what if customMetaData() returns undefined?

Same list-wrapped return value pattern, for example:

(() => {
    "use strict";

    const app = Application("DEVONthink 3");

    return app.selectedRecords().flatMap(
        record => {
            const customMeta = record.customMetaData;

            return customMeta.exists() ? (() => {
                const v = customMeta().mdattorneyemail1;

                return v ? [v] : [];
            })() : [];
        }
    );
})();
1 Like

Seems more robust.

and, of course, it can be variously reformulated, e.g.

(() => {
    "use strict";

    const app = Application("DEVONthink 3");

    return app.selectedRecords().flatMap(record => {
        const
            customMeta = record.customMetaData,
            v = customMeta.exists()
                ? customMeta().mdattorneyemail1
                : false;

        return Boolean(v) ? [v] : [];
    });
})();

That works well! Huge thanks to both of you