Your own views
The form component is meant to be framework-agnostic, so how you implement your views really depends on the framework you're using.
Banquette
comes with a default support for Vue 3 only, so we'll discuss a bit more about this specific type of implementation at the end.
Link to a view
A FormControl
on its own can't do much. You have to associate it with a view.
To do so, call the setViewModel
method, which has the following signature:
setViewModel(viewModel: FormViewModelInterface): FormViewControlInterface;
So it takes a FormViewModelInterface
as parameter and returns a FormViewControlInterface
.
These interfaces are used so the view and the control can communicate between each other.
- FormViewModelInterface in the direction
control => view
- FormViewControlInterface in the direction
view => control
FormViewModelInterface
This is the type of object the FormControl
expects to receive from the view. That's what it will use when it needs to communicate with the view.
/**
* Bridge between a FormControl and a view in the direction "FormControl -> View".
*/
interface FormViewModelInterface {
/**
* Unique id of the view model.
*/
id: number;
/**
* Update the view value with the value from the form control.
*/
setValue(controlValue: any): void;
/**
* Unset the form control assigned with the view model.
*/
unsetControl(): void;
/**
* Try to get the focus on the control.
*/
focus(): void;
/**
* Inverse of `focus`.
*/
blur(): void;
}
As you can see the interface is very simple. That all the FormControl
needs when it comes to the view is:
- to identify the
view model
:id
field, - to update the view value:
setValue
method, - to unbind itself :
unsetControl
method, - to ask for focus :
focus
method, - to ask for blue :
blur
method.
FormViewControlInterface
This is the type of object returned by the FormControl
when you call setViewModel
. That what you have to use when you need to communicate with the control.
The interface is a bit more complicated, so click here to show it.
/**
* Bridge between a FormControl and a view in the direction "View -> FormControl".
*/
export interface FormViewControlInterface {
/**
* Unique id of the control.
*/
readonly id: number;
/**
* Unique id of the form tree.
*/
readonly formId: string;
/**
* The absolute path of the component from the root of the form.
*
* The path is composed of each level name separated by "/".
*
* So the root node has a path of "/".
* If if has a child named "name", its path will by "/name".
*/
readonly path: string;
/**
* A component is `valid` when the validation has run and no error has been found.
*/
readonly valid: boolean;
/**
* A component is `invalid` when the validation has run and one or more errors has been found.
*/
readonly invalid: boolean;
/**
* A component is `validated` when the validation has run, no matter if errors have been found or not.
*/
readonly validated: boolean;
/**
* Inverse of `validated`.
*/
readonly notValidated: boolean;
/**
* A component is `validating` when its validator is currently running.
*/
readonly validating: boolean;
/**
* Inverse of `validating`.
*/
readonly notValidating: boolean;
/**
* Only `true` when the component has been validated, has not validation running and no error have been found.
*/
readonly validatedAndValid: boolean;
/**
* A component is `busy` when the view model of a component or one of its children is processing something.
*/
readonly busy: boolean;
/**
* Inverse of `busy`.
*/
readonly notBusy: boolean;
/**
* A disabled component is non-interactive and excluded from the validation.
*
* This is a "multi origin" flag, meaning it can be set multiple time by different sources.
* All original sources must remove their flag for the component to become enabled again.
*/
readonly disabled: boolean;
/**
* Inverse of `disabled`.
*/
readonly enabled: boolean;
/**
* A component is `dirty` if the user has changed its value in the UI, no matter its current value.
*/
readonly dirty: boolean;
/**
* Inverse of `dirty`.
*/
readonly pristine: boolean;
/**
* True if the component is marked as `touched`.
*
* A component is marked `touched` once the user has triggered a `blur` event on it.
*/
readonly touched: boolean;
/**
* Inverse of `touched`.
*/
readonly untouched: boolean;
/**
* A component is `changed` when its value is different from the initial value.
*/
readonly changed: boolean;
/**
* Inverse of `changed`.
*/
readonly unchanged: boolean;
/**
* A component is `focused` when its the current field on edition.
*/
readonly focused: boolean;
/**
* Inverse of `focused`.
*/
readonly unfocused: boolean;
/**
* The list of errors of the component.
*/
readonly errors: FormError[];
/**
* The original value of the control.
*/
readonly defaultValue: any;
/**
* The current value of the control.
*/
readonly value: any;
/**
* A reference on the view model instance that holds the focus.
*/
readonly focusedViewModel: FormViewModelInterface|null;
/**
* Update the value of the control with the value of the view.
*/
setValue(value: any): void;
/**
* Set the default value of this control.
*
* Calling this method will also set the field back an "unchanged" state.
* Further reset of the control will set this value back into the "real" value of the control.
*/
setDefaultValue(value: any): void;
/**
* Change the `disabled` state of the control to `true`.
*/
markAsDisabled(): void;
/**
* Change the `focused` state of the control to `false`.
*/
markAsEnabled(): void;
/**
* Change the `focused` state of the control to `true`.
*/
markAsFocused(): void;
/**
* Change the `focused` state of the control to `false`.
*/
markAsBlurred(): void;
/**
* Change the `busy` state of the control to `true`.
*/
markAsBusy(): void;
/**
* Change the `busy` state of the control to `false`.
*/
markAsNotBusy(): void;
/**
* Unset the view assigned with the form control.
*/
unsetViewModel(viewModel: FormViewModelInterface): void;
/**
* Reset the control. It has the following effects:
*
* - Set the value to the "default value",
* - Unmark the following states: `BasicState.Changed`, `BasicState.Touched`, `BasicState.Dirty`, `BasicState.Validated`,
* - Blur the control if focused,
* - Clear validation errors.
*
* Resetting the control does not impact the following states: `ContextualizedState.Disabled`, `BasicState.Busy`, `BasicState.Validating`, `BasicState.Concrete`.
*/
reset(): void;
/**
* Get all extra data.
*/
getExtras(): Record<string, any>;
/**
* Replace all extra data.
*/
setExtras(extras: Record<string, any>): void;
/**
* Get a single extra value.
*/
getExtra<T = any>(name: string, defaultValue?: any): T;
/**
* Set a single extra value.
*/
setExtra(name: string, value: any): void;
/**
* Register a callback that will be called before the value of the control changes.
*
* @return A method to call to unsubscribe.
*/
onBeforeValueChange(callback: (event: BeforeValueChangeFormEvent) => void, priority?: number): UnsubscribeFunction;
/**
* Register a callback that will be called when the value of the component changes.
*
* @return A method to call to unsubscribe.
*/
onValueChanged(callback: (event: ValueChangedFormEvent) => void, priority?: number): UnsubscribeFunction;
/**
* Register a callback that will be called when the value of a flag changes.
*
* @return A method to call to unsubscribe.
*/
onStateChanged(callback: (event: StateChangedFormEvent) => void, priority?: number): UnsubscribeFunction;
/**
* Register a callback that will be called when an error is added of removed from the component.
*
* @return A method to call to unsubscribe.
*/
onErrorsChanged(callback: (event: ErrorsChangedFormEvent) => void, priority?: number, selfOnly?: boolean): UnsubscribeFunction;
}
Through this object, you can do anything you need to the control (update its value, its states, subscribe to events, etc.).
WARNING
It's important that you use the object returned by setViewModel
to communicate with the FormControl
, and not
the FormControl
itself.
Internally the control uses this object to know that the calls come from the view model, and slightly adapts its internal behaviors in consequence.
The rest is up to you, the development of the UI components is a whole other subject, out of the scope of this documentation.
Extend Vue components
If you want to create new form components with Vue 3
, it's much simpler because Banquette
comes with a lot of built-in logic specific to VueJS
.
First of all, instead of dealing with the interfaces we discussed above, you can inherit from AbstractVueFormComponent
that does everything for you:
import { Component } from "@banquette/vue-typescript";
import { AbstractVueFormComponent } from "@banquette/vue-ui/form/abstract-vue-form.component";
import { TextViewDataInterface } from "./text-view-data.interface";
import { TextViewModel } from "./text.view-model";
@Component('my-input-component')
export default class MyInputComponent extends AbstractVueFormComponent<TextViewDataInterface, TextViewModel> {
protected setupViewModel(): TextViewModel {
return new TextViewModel(this.proxy, this.base);
}
}