Alpaca maintains observables that can be subscribed to by listeners that want to listen for changes to values in the form. An observable exists for every field and registration can be either programmatic or driven from configuration.
In general, if you're sticking to straight JSON-schema, you won't find much use for observables. JSON schema is fairly fixed in terms of its interdependency of fields (see dependencies).
Rather, observables are most useful when you really want to take full control of how fields refresh, update or change their state based on one or more values elsewhere in the form (at any level).
Here's an example that lets you pick a city and then lets you pick your favorite sports team. The sports teams that are available for selection change depending on what city you select.
After the form renders, the team
field subscribes to the city
field.
When the city
field's value changes, the team
listener fires.
This gives it a chance to update it's schema and refresh.
Incidentally, this example also defines a form and a button that, when clicked, submits the form in a background
Ajax post. You can use the submit
method to submit directly (which will redirect your browser) or the
ajaxSubmit
to run the submit in the background and get a promise.
This example does the same thing as the one above but uses a data source to load values for the team
field. After rendering, it sets up an event listener for the change
event. When the city
field changes it's value, the team
field is refreshed.
The data source is reloaded. In doing so, the data source uses the observable
method to look up the
value of the city
field by path.
This example also uses a key/value object instead of an array to specify both the schema.enum and options.optionLabels.
Each field control has a set of methods that you can use to set and retrieval observable state. These methods are:
field.subscribe([scope], id, fn)
Subscribes a function as an event handler for an observable identified by it's ID. The scope variable
is optional and identifies a namespace for the observable. If not provided, the namespace is one that
is global to the form being rendered and can be acquired using field.getObservableScope()
.
field.unsubscribe([scope], id, fn)
Unsubscribes a function as an event handler for an observable identified by it's ID.
field.observable([scope], id)
Retrieves an observable. The observable has methods get()
(which can return null),
set(value)
and clear()
. Note that setting or changing the value of an
observable will cause any observable subscribers to trigger.
field.clearObservable([scope], id)
Clears the value of an observable. This is equivalent to observable(id).clear()
.
Note that setting or changing the value of an observable will cause any observable subscribers to trigger.
By default, each field is rendered with the notion of an observable scope (or namespace) into which the observables
are written and read from. If you have two forms rendering on the same page, using two separate $.alpaca()
calls, you will have two separate observable namespaces by default. If you set an observable value in form1, it won't
be accessible by form2.
If you have a nested form, each field in the nested structure will use the same observable scope.
The observable scope can be gotten from a field using field.getObservableScope()
.