#useAsTitle non-relationship virtual field use case

7 messages · Page 1 of 1 (latest)

wet folio
#

This is a slightly different use than I've seen others post about.

I have a collection of members. For a some users, I would like the "name" to display just the first name with last-initial.

This is would be useful because organization leaders can see the full member roster. A parent can see the roster, but cannot see the full name of other members.

Even if I pre-calculate and store it in the database, the current access level is what would determine the field to use. So the error about being unable to use a non-relationship virtual field for useAsTitle would persist.

Is there any sort of workaround, even a convoluted one?

const virtualNameHook: FieldHook<Member, string, Member> = 
    ({ siblingData, req }) => {
        
        //TODO: Some access method finding particular role
        return req.user 
            ? `${siblingData.firstName} ${siblingData.lastName}`
            : `${siblingData.firstName} ${siblingData.lastName?.slice(0,1)}`;
    };

//....

    {
        name: 'name',
        virtual: true,
        type: 'text',
        hooks: {
            afterRead: [
                virtualNameHook
            ]
        }
    },
kindred condor
#

admin.useAsTitle cannot currently point at a non-relationship virtual field, and that restriction is enforced in config validation. So there isn’t a supported way to make useAsTitle itself dynamically choose between fullName and firstName + lastInitial.

#

The practical workaround is to make useAsTitle point at a real field, then use an afterRead field hook on that field to return either the full name or the masked version based on req.user. That gives you a per-request title without needing a virtual useAsTitle.

balmy stump
#

Right..so the virtual field restriction exists because useAsTitle needs to be queryable — it drives the document title in list views, relationship dropdowns, and breadcrumbs, all of which require the field to exist in the database

#

even if that were not the case, the access-based display you're describing wouldn't work via useAsTitle — the title is comes client-side from form field state, not from a server-side afterRead hook

#

the only approach I can think of would be to keep a real name field (full name) and a displayName field (first + last initial), both stored. Then use access control on name to restrict who can read the full name, and use displayName as useAsTitle:

{
  name: 'displayName',
  type: 'text',
  admin: { hidden: true },
  hooks: {
    beforeChange: [({ siblingData }) => {
      return `${siblingData.firstName} ${siblingData.lastName?.slice(0, 1)}.`
    }],
  },
},
{
  name: 'firstName',
  type: 'text',
},
{
  name: 'lastName',
  type: 'text',
  access: {
    read: ({ req }) => isOrgLeader(req.user), // only leaders see full last name
  },
},