/ Development  

Unlocking configuration; Create mind-blowing custom properties for your entity in runtime

Hi there AppWorks fans,

Welcome to a new installment of AppWorks tips.

We, as low-code developers and analysts always run ahead on the features to implement. Sometimes with a static result, but there are end-users (or “knowledge workers”) that want to have more control on what information is saved on an entity. Specially for these users, we implement a feature where these users can add their own properties to an entity instance. To give a little control on what is eventually saved in the AppWorks database, we provide the administrators the possibility to configure custom-type of properties to select from. Keep on reading as I see this type of implementation in almost every project, but this post moves it to a new level!


Let get right into it…

I start clean, with an empty VM, clean workspace, and clean project. The first thing I do with my awdev account is creating a new administration entity with the name custom_prop. You can use this table for the input on the different properties to add:

Name Label Type Notes
Type cp_type Text Length 64; as in a description; like e-mail, or passport, color, document
Default cp_default Text Length 1024; anything can be in here…also comma separated “list” data!
Is read only cp_is_read_only Boolean
Is required cp_is_required Boolean
Kind cp_kind EnumText (static) Length 16; [“text” (default), “boolean”, “date”, “date_time”, “number”, “list”]
RegEx cp_regex Text Length 512; can be a length-check, or other specific checks on e-mail, or color codes

Generate all the other BBs and make these additional changes:

  • Rename LayoutDefault to lyt_default (just a naming convention from my side!)
  • Rename DefaultList to lst_custom_props with label Custom properties, and apply it with category Admin
  • Edit the entity properties with a display name Custom property and name custom_prop
  • Remove the ‘None’ option from the boolean type of fields
  • Expand the ‘Text’ type of fields to the correct length…Like in the notes of the table above
  • Add the static enumeration values as described in the notes; You can remove the ‘none’ option from the list

Our first publication will be something like this:

props_001

Where the ‘Create’ form looks like this:

props_002

In runtime, we can now create our own type of properties; Like this example set:

Type Default Is read only Is required Kind RegEx
‘e-mail’ noreply@mydomain.com False True Text ^([a-zA-Z0-9_\.\-]+)@([a-zA-Z0-9_\.\-]+)\.([a-zA-Z]{2,5})$
‘colors’ #FF0000,#00FF00,#0000FF, True True List ^(?:(?:#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}))*,)*$
‘leading zero number’ 000001 False True Number ^[0-9]+$
‘yes / no’ yes False True Boolean ^(?:yes|no)$
‘date format (mm/dd/yyyy)’ 12/31/2023 False True Date ^(0[1-9]|1[012])[- \/.](0[1-9]|[12][0-9]|3[01])[- \/.](19|20)\d\d$
‘datetime format (yyyy-mm-dd hh:mm:ss)’ 2019-07-10 12:12:00 False True DateTime ^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9]$

Yes, the default value for the “colors” list-example indeed contains an extra comma…The regex took me too long to figure out! 😅
Evaluate your regular expressions here

props_003

Now what? We have the administration part ready, let’s make sure we consume from it. We make it really simple; just create a ‘Case’ entity with a simple ‘Name’, and ‘Subject’ property; Generate all the other stuff…You know the drill by now! On the new ‘Case’ entity, we create a new ‘hasChild’ relation to a new entity named property. You can now open this new entity which will we give a prop_value property with again the default BBs applied; renamed to your own needs. I also make sure the lst_properties is not available in runtime (as it’s only for internal use within our case!):

props_004

Next step is adding a ‘toOne’ relation from this ‘Property’ entity to the ‘Custom Prop’ entity (I named it to_one_custom_prop). With this update, we can now also add this relation to the ‘Create’ form of our ‘Property’ entity. Like this:

props_005

You can make it required as well! Also, make sure to disable the ‘Create’ action in the bottom…It fell of my screenshot!

We move back to our ‘Case’ entity as we want to make sure we can create our new ‘Property’ instances on it. For this, I create a new ‘Form’ BB with name CreateProperties on the ‘Case’ entity, and I add my ‘hasChild’ relation onto it; Make sure to convert it to a ‘Grid’ and add the additional properties we would like to see. In my case the prop_value and the related ‘toOne’ cp_type property…Are you still with me? So, like this:

props_006

At the bottom of that right panel (It fell of the screenshot again!), you can make sure to only ‘Create’ and ‘Delete’ new properties. You can even make sure the creation happens in a modal popup, instead of inline of the grid. Have a double-check yourself!

The last thing we miss now (for our first try-out), is adding this new form in the layout of the ‘Case’ entity. So, open that lyt_default BB, add a second ‘Form’ panel next to the existing one and select the new form CreateProperties. Like this:

props_007

Now it’s time for a publication and to have a check in runtime for a newly created ‘Case’ entity…

props_008

NICEEEEE!! What’s next? Well, first a coffee break! ☕


Business rules

Have you tried creating any additional properties yet? I hope you have; I also hope you make the conclusion that the value you fill in doesn’t matter! Correct!? Well, that value is exactly what we would like to validate on. So, the value for our property must match the criteria we configured for that custom property…You’re still with me here? So, when I create a new additional property of config type ‘e-mail’ it must be validated to the regex behind that configuration! How on earth!? Well, watch and learn…

We’re talking about ‘Rule’ BBs (of different events) here…Let’s start simple and make sure that when our property type selection changes (in other words; Our relation to the custom property changes!), we want to grab the default value of the configuration property and fill it in. Simple, but efficient like this e_onrelchange_set_default ‘Rule’ BB (on the ‘hasChild’ ‘Property’ entity):

props_009

Publish, and evaluate it…It should work for you as well!

Next step is about validation; So, when the property value changes it must be validated against 3 things:

  1. If it’s required; so, it must have a value at that moment
  2. If it’s read only; a bit harder to implement, but it means you can’t change it once the value is created
  3. If the value is in place, it should meet the regular expression…That’s where it gets interesting! 😎

The first one is easy…Just a new ‘onCreate’ event with name e_oncreate_is_required with this implementation:

props_010

This is the advanced view behind it: item.Properties.prop_value is null && item.to_one_custom_prop.Properties.cp_is_required==true

I also apply a second ‘Rule’ BB e_onpropchange_is_required like this:

props_011

Why? Well, this first one is during the creation phase of the property; The other, when changing the value afterward!

Read here all about the Identity.ItemStatus check you see in the screenshot…It all has a reason!
Could we do it with one onchange ‘Rule’ BB? Sure, but would it behave like our implementation!? I don’t think so!

Let’s continue…The second one making it read only…

For this one we make a rather nasty workaround with a “helper” property named prop_value_old. We don’t show this property, but we fill its value via an ‘onCreate’ Rule BB with the current value (a task for yourself!). After this, we can check this value with the current value like this ‘onChange’ Rule BB named e_onpropchange_is_read_only:

props_012

The ‘Disable’ action is always hard to understand; In dummy talk: When our old value and current value are equal, we disable the field…You would normally think the other way around; So, when those field values are NOT equal to each other, we leave the field enabled! #ItIsWhatItIs.

Does it work?…Ohw YEAH! 🤗

props_013

Finally, the third one…The RegEx check! The fun part of this post…As how on earth will we check a regular expression out of the box!? Watch and learn my friend as we make the impossible, possible!…What a nasty aftertaste! 😱

Create a new ‘Rule’ BB (or make a copy from e_oncreate_is_required) and name it e_oncreate_matches_regex. Open it and have a low-code implementation like this:

props_014

WHAT? REALLY? That simple? Yes, my friend…that easy! I was also a bit amazed!? Watch out for the extra exclamation mark at the front which flips the expression; So, when it NOT matches, show the error…

For you to copy: ! item.Properties.prop_value.matches(item.to_one_custom_prop.Properties.cp_regex)

Does it work? YESSSS, it works…smoothly! 😁

Once created, we can update our value as well which also needs to validate again to my regular expression! Well, I created a new ‘Rule’ BB e_onpropchange_matches_regex with a condition like this: (! item.Properties.prop_value.matches(item.to_one_custom_prop.Properties.cp_regex)) && item.Identity.ItemStatus==1…Guess what? It’s NOT working!?!?

props_015

WHAT?…I extended it to this:

1
2
3
(! item.Properties.prop_value.matches(item.to_one_custom_prop.Properties.cp_regex)) 
&& item.Identity.ItemStatus==1
&& item.to_one_custom_prop.Properties.cp_type == 'e-mail'

Now I only get errors for the ‘e-mail’ types, so that’s OK! To double-check if I’m doing something wrong here, I moved to this:

1
2
3
(! item.Properties.prop_value.matches('^([a-zA-Z0-9_\.\-]+)@([a-zA-Z0-9_\.\-]+)\.([a-zA-Z]{2,5})$')) 
&& item.Identity.ItemStatus==1
&& item.to_one_custom_prop.Properties.cp_type == 'e-mail'

So, I hardcoded the regex…That solves the problem, but my conclusion is that we found a bug here as my related property is not resolved to an expected string result!

It’s getting even more interesting; watch what happens when I output the regex itself in the error message:

props_016

Now, all of a sudden; 2 are correct!?!? #SUPPORT? In the meanwhile, you can use a workaround like this?:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
((! item.Properties.prop_value.matches('^([a-zA-Z0-9_\.\-]+)@([a-zA-Z0-9_\.\-]+)\.([a-zA-Z]{2,5})$')) 
&& item.Identity.ItemStatus==1
&& item.to_one_custom_prop.Properties.cp_type == 'e-mail') ||
((! item.Properties.prop_value.matches('^(?:(?:#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}))*,)*$'))
&& item.Identity.ItemStatus==1
&& item.to_one_custom_prop.Properties.cp_type == 'colors') ||
((! item.Properties.prop_value.matches('^[0-9]+$'))
&& item.Identity.ItemStatus==1
&& item.to_one_custom_prop.Properties.cp_type == 'leading zero number') ||
((! item.Properties.prop_value.matches('^(?:yes|no)$'))
&& item.Identity.ItemStatus==1
&& item.to_one_custom_prop.Properties.cp_type == 'yes / no') ||
((! item.Properties.prop_value.matches('^(0[1-9]|1[012])[- \/.](0[1-9]|[12][0-9]|3[01])[- \/.](19|20)\d\d$'))
&& item.Identity.ItemStatus==1
&& item.to_one_custom_prop.Properties.cp_type == 'date format (mm/dd/yyyy)') ||
((! item.Properties.prop_value.matches('^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1]) (2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9]$'))
&& item.Identity.ItemStatus==1
&& item.to_one_custom_prop.Properties.cp_type == 'datetime format (yyyy-mm-dd hh:mm:ss)')

Not nice at all; it’s hardcoded; AND…It doesn’t even get the expected result…Strange! During property creation it all went so smooth…Or is it just my regex skills that need an upgrade!? I don’t know…Maybe it has to do with the escape character \ as well? Comment me as I saw enough now! I simply made sure the values are never editable after they’re created. My users just need to remove it and add a new one! 😏

Just for you to note: the ‘matches()’ follows the principles from Java, but you have some examples already now from this post!


Great, what a rush, what a “DONE”…It went sooo smoothly, but we eventually hit that concrete wall again! It hurts, but we’ll just continue with our workaround which still makes our end-users happy! We learned a lot here when it comes to adding your own custom properties to an entity directly in runtime. Regular expressions passed our knowledge, configuring things in runtime is always interesting to gain knowledge from, and we saw some workarounds with “helper” properties with nicely crafted ‘Rule’ BBs. I leave it for now…Have a good weekend and I see you in the next post.

Don’t forget to subscribe to get updates on the activities happening on this site. Have you noticed the quiz where you find out if you are also “The AppWorks guy”?