Flags are a generalized way for documents to store additional information as needed by systems and modules.
Official Documentation
Legend
Document.defineSchema // `.` indicates static method or property
Document#setFlag // `#` indicates instance method or property
Flags are the safest way that packages can store arbitrary data on existing documents. If a package allows the user to set some data which isn't normally on the Document's data schema, it should leverage flags.
Systems and modules can define unique pairings of
type
field values andsystem
using Data Models for many document classes. This provides significantly greater control over the data validation process. However, if you need to modify a document subtype another package defined, e.g. a module providing additional properties to a"weapon"
item implemented by the system, then use flags rather than try to monkey patch the data schema defined by the system.
Documents with configurable types includeActiveEffect
,Actor
,Card
,Cards
,ChatMessage
,Combat
,Combatant
,Item
, andJournalEntryPage
A flag does not have to be a specific type, anything which can be JSON.stringify
ed is valid.
Below are important things to always consider when working flags.
Flags live on the root of a Document's properties, on the same level as name
.
Document
├─ id
├─ parent
├─ name
├─ flags <---
└─ someOtherSchemaKey (e.g. `system`)
The flags
object is keyed by scope then flag name as seen in setFlag
.
flags: {
<scope>: {
<flag name>: value
}
}
These are the most common ways developers interact with flags.
Flags are automatically namespaced within the first parameter given to Document#setFlag
.
The following are expected scopes:
core
world
id
of the world's system.id
of a module present in game.modules
- Note that this does not need to be an active module.If an unexpected scope is provided, Foundry core will throw
an error.
const newFlagValue = 'foo';
someDocument.setFlag('myModuleName', 'myFlagName', newFlagValue);
There are some caveats and pitfalls to be aware of when interacting with objects stored in flags.
See Some Details about
setFlag
and objects below for more information.
setFlag
While setFlag
is the easiest and generally best way to update the flags
field, it's not the only way.
Manually updating a Document's
flags
value does not have any of the client-side validations thatsetFlag
has.For instance, nothing prevents an update from directly replacing the namespaced flags object with a direct record of keys and values.
Be careful when interacting with flags manually to keep this structure lest you accidentally break someone else's package (or they yours).
Changing a flag value during a normal document update is a way to set multiple flags at once, or to both update the flag and standard schema data at the same time.
To do so, the data fed to the Document#update
method should target the specific namespaced object and flag name being edited.
const updateData = {
['flags.myModuleName.myFlagName']: newFlagValue,
};
// is the same as
const updateData = {
flags: {
myModuleName: {
myFlagName: newFlagValue
}
}
}
someDocument.update(updateData);
Simply mutating a flag's value on a document's data with =
assignment will not persist that change in the database, nor broadcast that change to other connected clients. Keep this in mind when editing a document during a hook that fires in prepareData
.
There are two main ways to get a flag value: Following the chain of sub-fields, or with Document#getFlag
The arguments passed to getFlag
have the same constraints as those passed to setFlag
. Providing an unexpected scope will throw
rather than return undefined
. If you need a flag from a module which might not exist in the world, it is safer to access it on the data model itself.
const flagValue = someDocument.getFlag('myModuleName', 'myFlagName');
// flagValue === 'foo'
Flags are readable on the Document's data as detailed in the Data Format section:
someDocument.flags.packageId.flagKey
Keep in mind that accessing deeply properties in javascript requires the optional chaining operator, ?.
, to avoid throwing errors if one of the properties in the path doesn't exist. Secondly, if any of the properties along the way is hyphenated, e.g. package-id
, you'll have to use brackets and a string access, e.g. flags["package-id"].flagKey
. The getFlag
method already handles these conditions which is why it's usually preferable to use it in place of ordinary javascript property chaining.
A safe way to delete your flag's value is with Document#unsetFlag
. This will fully delete that key from your module's flags on the provided document.
someDocument.unsetFlag('myModuleName', 'myFlagName');
This is semantically equivalent to the following call, just missing some additional protections checking the scope.
someDocument.update({'flags.myModuleName.-=myFlagName': null})
Since flags are simply data on a Document, any hook that fires during that document's event cycle and most other hooks involving the document will have access to that data.
For example, a flag
on ChatMessage
might be injected into the DOM for that message.
chatMessage.setFlag('myModule', 'emoji', '❤️');
Hooks.on('renderChatMessage', (message, html) => {
if (message.getFlag('myModule', 'emoji')) {
html.append(`<p>${message.flags.myModule.emoji}</p>`);
}
});
setFlag
and objectsWhen the value being set is an object, the API doesn't replace the object with the value provided, instead it merges the update in. Document#setFlag
is a very thin wrapper around Document#update
.
The database operation that update
eventually calls is configured by default to update objects with mergeObject's default arguments.
Example to demonstrate:
game.user.setFlag('world', 'todos', { foo: 'bar', zip: 'zap' });
// flag value: { foo: 'bar', zip: 'zap' }
game.user.setFlag('world', 'todos', {});
// flag value: { foo: 'bar', zip: 'zap' }
// no change because update was empty
game.user.setFlag('world', 'todos', { zip: 'zop' });
// flag value: { foo: 'bar', zip: 'zop' }
Document#setFlag
should perhaps be thought about as "updateFlag" instead, but that's only partly true because it can set that which doesn't exist yet.
Where this has the most effect is when one wants to store data as an object, and wants to be able to delete individual keys on that object.
The initial instinct of "I'll setFlag
with an object that has everything but that key which was deleted," does not work. There are some options available, some more intuitive than others.
This is the recommended way to use
setFlag
to delete a key within a flag object.
The foundry-specific syntax for deleting keys in Document#update
(-=key: null
) works with setFlag
as well:
game.user.setFlag('world', 'todos', { ['-=foo']: null });
// flag value: { zip: 'zop' }
Document#unsetFlag
The unsetFlag
method is a thin wrapper around Document#update
which first modifies the key being updated to use the Foundry Specific key deletion syntax (-=key: null
).
This means unsetFlag
could be used in a roundabout way to remove a specific key within a flag's object:
game.user.unsetFlag('world', 'todos.foo');
// flag value: { zip: 'zop' }
null
If you're happy with the key being null
, setting a key's value to null
explicitly works as expected:
game.user.setFlag('world', 'todos', { foo: null });
// flag value: { foo: null, zip: 'zop' }
This error is thrown when the first argument to Document#setFlag
(or unsetFlag
/getFlag
) mentions a package which is not installed.
Typically this is due to a typo.
This is commonly encountered in use cases where one module checks another module's data and operates based on that data and can be sneaky as a deactivated module does not trigger it.