
TL;DR
We have support for conditional behaviors in@myndpm/dyn-forms
documented atmynd.dev and we are able to provide them easily like the Controls, Validators, AsyncValidators, etc.
In complex forms use-cases, some controls directly depend on the value or status of some other form control. Then we implement custom behaviors, likehiding
a field when another control has some value, ordisabling
it depending on a complex condition, etc.
To support this, we've addedMatchers
andConditions
, which can be provided just like theValidators
andAsyncValidators
as we saw in the previous chapter of this series. If you want to get an initial idea from the code, you can check thissource file and thisreal use-case demo.
DynTreeNode
Each dynamic control has a composed Node service instance which holds the data of this point in the form hierarchy. It provides theAPI and the data to manipulate the form in a customized way when needed.
The node has thecontrol
Form Instance, theparams
object, some utility methods toquery
orselect
parent and child controls, manipulate the visibility, etc. We will use this node inside the Conditions, Matchers and any other custom Handlers.
Conditions
To match a special requirement, we need to define one or more conditions, so when all (AND
) or one (OR
) of them are fulfilled we perform a particular action. The Condition Function type consists of:
interfaceDynControlConditionFn{(node:DynTreeNode):Observable<any>;}
it streams a truthy value whenever the condition is fulfilled or not, for example, we could check if a specific control has the expected value:
(node:DynTreeNode)=>{returnnode.query('specific.control').valueChanges.pipe(map(controlValue=>controlValue==='xValue'),);}
we can join these conditions with the required operator (AND | OR
) for our use-case, and then evaluate the action to execute in the specificMatcher
.
Matchers
We define our requirement with the Matchers that we want to runwhen
all or a single condition is satisfied:
match:[{matchers:['DISABLE'],// one or more matcherswhen:[{// the library provides a DEFAULT condition handler// to process path, value and negationpath:'other.field',value:'expectedValue'}]}]
theDISABLE
matcher is included in the library withENABLE
,SHOW
,HIDE
(display: none) andINVISIBLE
(visibility: hidden).
One matcher consists of a function which performs a task in the form hierarchy; to do so, it receives theDynTreeNode
instance:
interfaceDynControlMatcherFn{(args:{node:DynTreeNode;hasMatch:boolean;firstTime:boolean;results:any[];}):void;}
So, for example theDISABLE
matcher operates into the form control when the specified conditions are fulfilled (has match):
{id:'DISABLE',fn:():DynControlMatcherFn=>{return({node,hasMatch})=>{hasMatch?node.control.disable():node.control.enable();}}},
Advanced Stuff
This conditional processing enables us to do some additional logical operations, likenegate
the result of one or all the conditions, so we are able to play with conditions upside down and have the simplest specification of our requirements.
Matcher Example
For example, if we want to run a Matcher for all the options of a SELECT except a few of them,OR
without another condition, we can define that requirement with the few known values instead listing all the other values (which can be a long list), and negate the matcher input:
match:{matchers:['MyMatcherID'],operator:'OR',// the operator is AND by defaultwhen:[{path:'selectorName',value:['A','B','C']// this will check if selectorName.value is IN this array},{path:'other.control',value:'anotherValue'},],negate:true}
the Matcher will receivehasMatch: true
when the selector has a value NOT in the provided list.
Also note that you can provide your Matcher factories with a customid
like'MyMatcherID'
just like we will do with conditions in the following section.
Condition Factory
We can register Factories with anid
and afn
as we do with Validators, and parametrize them in the Config Object:
exportinterfaceDynControlCondition{id:string;fn:(...args:any[])=>DynControlConditionFn;}
Remember thatDynControlConditionFn
returns anObservable<any>
so you can implement and provide your custom conditions like:
constconditions=[{id:'MyConditionId',fn:(...args:any[])=>{// Factoryreturn(node:DynTreeNode)=>{// Conditionreturnnode.control.valueChanges.pipe(map(...));}}}];@NgModule({imports:[DynFormsModule.forFeature({conditions});
Conditions Config
You can use your custom conditions in these ways:
// inline functionwhen:[(node:DynTreeNode)=>{// manipulate the form via DynTreeNode}]// factory ID without argumentswhen:['MyConditionId',]// parametrized factorywhen:[['MyConditionId',args],]// or declarative inline configwhen:[{condition:'MyConditionId',path:'other.control',// path is the only mandatory field in this format,param1:'anyValue',// the whole object will be passed to your DynControlConditionFn},]
in the last notation the whole config object is passed to the Factory, that's how theDEFAULT
condition handler receives thepath
,value
andnegate
config values.
Note: If novalue
is configured, theDEFAULT
handler emitstrue
everytime the configuredpath
control value changes:
id:'DEFAULT',fn:({path,value,negate}):DynControlConditionFn=>{return(node:DynTreeNode):Observable<boolean>=>{if(value===undefined){returnnode.query(path).valueChanges.pipe(mapTo(true));}...}}
Conclusion
We have covered most of the details ofMatchers
andConditions
, and how one or many conditions can be configured so when one or all of them are fulfilled, they trigger a matcher which can modify the state of the form hierarchy via theDynTreeNode
API.
If you have an idea after this reading, or after using this library in an Angular App, please share it with us! :)
You canrequest features and join ourdiscussions.
// PS. We are hiring!
Top comments(3)

Great to read this!
And I'm really excited about Advanced stuff part, it took a few mins more to get it clear for me but then I've understood how powerful it is!
Btw, can we update current control value using some custom matcher (for example, based on other control value)?

- LocationColombia
- WorkAngular Solutions Architect
- Joined
sure! having access to thecontrol
Form Instance allows us to manipulate it.
Additionaly we have API to query other controls, API for visibility, API for events (hooks, call custom methods inside the DynForm instance), etc.
The matcher have access to the DynTreeNode of the configured control and from there you can do any custom advanced stuff ;)
For further actions, you may consider blocking this person and/orreporting abuse