Formula is a powerful form generator built for Angular. Inspired by Angular Router, Formula provides a declarative interface for building reactive forms.
Formula is in early development. Don't use this in production.
With Formula you start with the value you want to model...
const value = [
{
name: "Bob",
dob: "1988-01-01",
favouriteFood: "APPLE_PIE",
notes: "..."
},
...
]
...and craft a formula that models your value
const formula = users(user(name, age, notes, dob, favouriteFood))
Then you just hook it up to a component
@Component({
selector: "z-root",
template: `
<z-formula [formula]="formula" [value]="value"></z-formula>
`,
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.
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
Formula provides a form builder to construct Formula objects that are used to render forms.
Member | Description |
---|---|
group: FormulaBuildFn<FormulaGroup> |
Creates a factory for FormulaType.GROUP nodes |
array: FormulaBuildFn<FormulaArray> |
Creates a factory for FormulaType.ARRAY nodes |
control: FormulaBuildFn<FormulaControl> |
Creates a factory for FormulaType.CONTROL nodes |
container: FormulaBuildFn<FormulaContainer> |
Creates a factory for FormulaType.CONTAINER nodes |
Use FormulaBuilder
to functionally create and compose various form elements together.
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"),
)
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.
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<any> |
Forwards valueChanges from AbstractControl . |
@Output() statusChanges: EventEmitter<any> |
Forwards statusChanges from AbstractControl . |
@Output() submit: EventEmitter<any> |
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 |
The simplest case is a formula with a single field.
@Component({
selector: "z-example",
template: `
<z-formula [formula]="formula" [value]="value"></z-formula>
`,
})
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:
@Component({
selector: "z-text-field",
template: `
<label [innerHTML]="ctx.data.label"></label> <input [formControl]="ctx.model" />
`,
})
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.
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.
Anywhere in your Formula component template, place a single z-formula-outlet
tag.
@Component({
selector: "z-form-container",
template: `
<z-form> <z-formula-outlet></z-formula-outlet> </z-form>
`,
})
export class FormContainerComponent {}
Selectors: <z-form>
A basic wrapper for NgForm
that forwards native submit events to the root FormulaOutlet
. If nested inside another FormComponent
, renders <ng-form>
instead of <form>
. Will throw an error if not used with FormulaGroup
or FormulaArray
See Formula Outlet