Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Cover image for Conditional Tasks in Dynamic Forms
Mynd.co profile imageMateo Tibaquirá
Mateo Tibaquirá forMynd.co

Posted on • Edited on

     

Conditional Tasks in Dynamic Forms

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>;}
Enter fullscreen modeExit fullscreen mode

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'),);}
Enter fullscreen modeExit fullscreen mode

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'}]}]
Enter fullscreen modeExit fullscreen mode

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;}
Enter fullscreen modeExit fullscreen mode

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();}}},
Enter fullscreen modeExit fullscreen mode

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}
Enter fullscreen modeExit fullscreen mode

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;}
Enter fullscreen modeExit fullscreen mode

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});
Enter fullscreen modeExit fullscreen mode

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},]
Enter fullscreen modeExit fullscreen mode

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));}...}}
Enter fullscreen modeExit fullscreen mode

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)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
coffeetea profile image
Vitalii Baziuk
convert shawarma into code
  • Location
    Kyiv, Ukraine
  • Work
    Senior Developer
  • Joined

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)?

CollapseExpand
 
matheo profile image
Mateo Tibaquirá
Senior Angular Developer, Library Author, Open Source Advocate, Dependency Maintainer by hobby.
  • Location
    Colombia
  • Work
    Angular 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 ;)

CollapseExpand
 
dnmakarov profile image
dnmakarov
  • Joined

It looks very powerful and flexible! Can't wait to play it in my projects

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

More fromMynd.co

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp