Formula is a powerful form generator built for Angular. Inspired by Angular Router, Formula provides a declarative interface for building reactive forms.
ts
const value = [
{
name: "Bob",
dob: "1988-01-01",
favouriteFood: "APPLE_PIE",
notes: "..."
},
...
]
`
...and craft a formula that models your value
`ts
const formula = users(user(name, age, notes, dob, favouriteFood))
`
Then you just hook it up to a component
`ts
@Component({
selector: "z-root",
template:
,
styleUrls: ["./app.component.scss"],
})
export class AppComponent {
public formula = formula
public value = value
}
`
The result
If you'd like to learn more, read on or check out the example.
Features
Formula aims to achieve feature symmetry with the Route interface from @angular/router and
FormBuilder from @angular/forms.
- Builder (in progress): A convenient utility for generating Formula objects
- Renderer (in progress): Automatically synchronises the UI based on the current formula and value
- FormArray (in progress): Automatically populate array controls based on the current value and expose api
for adding, moving or removing controls
- Guards (not supported yet): Contextually control whether a form node can be loaded, activated or deactivated
- Resolve (not supported yet): Contextually fetch remote or async data
(eg. populating select options based on the current user)
- LoadChildren (not supported yet): To allow for code splitting/lazy loading
- StyleGuide (in progress): A common convention for declaring readable, composible, maintainable forms
- Themes (in progress): Apply different styles to the same form by targeting classes exposed by each form element
- FormContainer (in progress): Formula provides a container type useful for adding nodes that do not need a model
(eg. a submit button)
- Smart Validators (not supported yet): In addition to the usual validator options, Formula will also support
validator class tokens that will be instantiated with the Angular injector.
- Computed Fields (in progress): A method for creating one-way or two-way computed fields that react to changes in
the form model
- Material (in progress): A support library that wraps Angular Material
FormulaBuilder
Formula provides a form builder to construct Formula objects that are used to render forms.
$3
| Member | Description |
| :-------------------------------------------- | :-------------------------------------------------- |
| group: FormulaBuildFn | Creates a factory for FormulaType.GROUP nodes |
| array: FormulaBuildFn | Creates a factory for FormulaType.ARRAY nodes |
| control: FormulaBuildFn | Creates a factory for FormulaType.CONTROL nodes |
| container: FormulaBuildFn | Creates a factory for FormulaType.CONTAINER nodes |
$3
Use FormulaBuilder to functionally create and compose various form elements together.
`ts
const fb = new FormBuilder()
export const form = (name: string) =>
fb.group({
name,
component: FormContainerComponent,
})
export const text = (name: string, label: string) =>
fb.control({
name,
component: TextFieldComponent,
data: {
label,
},
})
export const formula: Formula = form("user")(
text("firstName", "First Name"),
text("lastName", "First Name"),
)
`
FormulaDirective
Creates a FormulaNode tree that is used to render a form. FormulaDirective provides a declarative
approach for dynamic forms creation
FormulaDirective requires a Formula, if a falsy value is set the view will clear and the
form will get destroyed.
$3
Formula is under active development. The current API is experimental and likely to change
before release.
| Member | Description |
| :------------------------------------------- | :----------------------------------------------------- |
| @Input() formula: Formula | The formula to be rendered. See Formula for options. |
| @Input() value: any | Form value setter. Unknown object keys are discarded. |
| @Output() valueChanges: EventEmitter | Forwards valueChanges from AbstractControl. |
| @Output() statusChanges: EventEmitter | Forwards statusChanges from AbstractControl. |
| @Output() submit: EventEmitter | Forwards submit events from a registered NgForm. |
| setForm(form: NgForm): void | Registers a NgForm with the outlet. |
| setValue(value: any): void | Immediately patches the value of the form |
$3
The simplest case is a formula with a single field.
`ts
@Component({
selector: "z-example",
template:
,
})
export class ExampleComponent {
value = {
exampleText: null,
}
formula: Formula
constructor(fb: FormulaBuilder) {
this.formula = fb.control({
name: "exampleText",
component: TextFieldComponent,
data: {
label: "Example Text",
placeholder: "Type text here",
},
})
}
}
`
In this example we are declaring a formula that contains a single form control called
exampleText. It is rendered with a component, which is up to the user to implement. The
concept is similar to that of Angular route components. For example, the TextFieldComponent
may be as simple as this:
`ts
@Component({
selector: "z-text-field",
template:
,
})
export class TextFieldComponent {
constructor(public ctx: FormulaContext) {}
}
`
Each component in the tree receives a FormulaContext containing the model, data and resolve
data.
> Important note: Formula components are rendered dynamically. You may encounter errors about missing
> ComponentFactory or unknown Provider. Ensure all components are marked as entry components and that all providers
> are provided in the NgModule the form gets rendered in.
FormulaOutlet
Renders the current FormulaNode in the node tree. It is functionally similar to router-outlet;
use this directive inside Formula components to render children of the current node.
$3
Anywhere in your Formula component template, place a single z-formula-outlet tag.
`ts
@Component({
selector: "z-form-container",
template:
,
})
export class FormContainerComponent {}
`
FormComponent
Selectors:
A basic wrapper for NgForm that forwards native submit events to the root FormulaOutlet.
If nested inside another FormComponent, renders instead of