Skip to main content

Selectors and Selections

One common pattern is having properties that only activate when a controlling property has a specific value. As a convenience1, this can be handled using the selector feature.

Setting selector marks a property as a selector, and setting a selection value marks properties that should only be evaluated when the selector matches that value. (If selection is set without a value, it defaults to the property name.)

import { Schema, SchemaResolver } from '@versionzero/schema';
const resolver = new SchemaResolver();

const schema = await resolver.compile(
new Schema('object')
.property('action', new Schema('string').selector())

.property('resizeSettings', new Schema('object')
.selection('resize')
.property('width', new Schema('number'))
.property('height', new Schema('number'))
)
.property('rotateSettings', new Schema('object')
.selection('rotate')
.property('angle', new Schema('number'))
)
);

const defaultConfig = {
resizeSettings: { width: 123, height: 456 },
rotateSettings: { angle: 90 }
}

console.log( await schema.process({...defaultConfig, action: 'resize' }) );
console.log( await schema.process({...defaultConfig, action: 'rotate' }) );

Multiple selections can specify the same selector value.

Alternatives

Selector properties must always be located in the immediate parent of a selection. If this relationship doesn't match your requirements, you can easily implement a similar pattern using other techniques. The selector/selection feature is just "syntactic sugar" for convenience.

  • If selection depends on criteria within the selection itself, consider using a union.
  • Value processors like $reference and $property can be used to fetch selector values from the output.
  • Use a condition handler to control selection.
  • The values option lets you constrain legal choices for a selector property.

Footnotes

  1. Selector and selection pairing is checked during compilation, but the runtime implementation is a synthesized condition handler.