What is defensive Programming? Pt1
When we write
programs, we (normally) have a very clear idea of what it is we want to achieve,
When we look at JavaScript form manipulation in D365 specifically, we know which fields, sections and tabs we
want to manipulate, whether it’s making a field Read-only or mandatory, based
on the value of another field, or hiding whole sections or tabs based on a
user’s login and security role and we write the scripts accordingly. However, other people may have access to the system
and may be doing updates of their own.
If those updates happen to change a field or tab name we rely on in our
code, it could cause problems. How we guard against adverse effects of these
changes is referred to as ‘defensive programming’. Let me give you a common definition of defensive
programming from our friend Mr Google:
Defensive programming
is a proactive approach to software development that focuses on
anticipating potential problems and implementing safeguards to prevent crashes,
bugs, and vulnerabilities, ensuring the application functions reliably even
under unforeseen circumstances.
This blog is not going to teach you how to write JavaScript,
but I will show you some of the basic techniques we can use when manipulating
forms or responding to events on a Form in d365.
Name Space
When we
write programs, we as programmers talk about the scope of variables or the scope
of a function in relation to other functions or libraries. Namespacing is an extension of managing ‘Scope’
and refers to the provision of scope (think boundaries) around functions. JavaScript files (Web Resources in D365) are
read into memory and used when called by an event, typically on the form. Let’s consider two functions, written by two
separate developers housed in two separate libraries.
The first
function is called ‘set date’ and sets the ‘Last Contact date on a contact
record. The library is called ‘ContactFormEvents.js’. The purpose of this function is to set the value
of a field on the form called ‘Last Contact date’ when the form is saved.
Here is a
snippet of the code
let formContext = executionContext.getFormContext();
var now = new Date();
formContext.getAttribute("new_lastcontactdate").setValue(now);
}
Now suppose
we have another library called CommonFunctions.jS that a helpful dev. has created to organise all
the common functions in to one simple library, only this Dev. Has thought that
it would be a great idea to set the next contact date to 1 month after the
record has been saved. They create the following
function
let formContext = executionContext.getFormContext();
var now = new Date()
var nextDate = now.setMonth(now.getMonth() + 1);
formContext.getAttribute("new_nextcontactdate").setValue(nextDate);
Ok, I think
you can see where this is going. Lets suppose
both libraries are added to the contact form.
The first developer attaches an event to the on save for the form
‘setDate’
Which one
is going to run, which is it the one the developer wants to run? Both are valid and both are loaded in to memory,
oh dear.
Whilst this
is an over simplification, the issue is more than valid and more common than
you would want to believe. Don’t worry,
there is a real simple fix and its not that hard once you get your head around
it. Lets look at how we can use a NameSpace
to ensure only the correct ‘setDate’ function is called.
How do we
create a NameSpace?
There are a
couple of ways, and this is how I do it:
ContactEvents = {};
}
Here I check
to see if the namespace ‘ContactEvents’ has been defined and if not I create
it.
This is
what the updated code snippet could look like using the new namespace
ContactEvents = {};
}
ContactEvents = {
setDate : function(executionContext){
var now = new Date();
formContext.getAttribute("new_lastcontactdate").setValue(now);
}
}
Now when I
call the function in the on save event, I change the call to include the namespace
so it looks like this:
ConactEvents.setDate
Now I know
which function is being called. Even if
the common library doesn’t have a name space, but the contact form events
library does, it means they won’t get mixed up.
Namespacing is only one component of defensive programming. The next one is
Validation
and Verification
If we
assume that because we created the last contact field and we’ve added a
namespaced function to update it, that everything is fine and dandy and we can
have a nice pat on back while we drink our well earned coffee we could be in
for a surprise.
Lets take
the examples from above, and lets assume the our helpful second dev, has decided
not only to add some useful functions, but they’ve also tidied up the form and created
the last contacted filed with a slightly different name more in keeping with the
rest of the naming used on the form.
Lets image the field has been recreated and is not called new_dateLastContacted,
what happens to our function when its called.
BANG! It crashes and pops up some error message that confuses the user.
Oh dear (again).
Don’t worry
we can fix this too. We interject a
test, and a validation
First we
check to see if the field is on the form we do this by grabbing the field by
name and seeing if it there or not (null).
The following snippet tries to assign a field to the variable dateFld. Then dateFld is compared to ‘null’ if it is
null, it simply jumps out with no error
if(null !== dateFld){
Else {
Return;
}
Now let’s verify the field that comes back is in fact a date field (as opposed to a text field for example)
Var fldType == dateFld.getAttributeType();
If (fldType == “datetime”){
return true;
}
So now, we know how to ensure we call the right function, we know how to test in our function that the field is still on the form and that the field is the right (or expected) type, now we can execute knowing we have the correct function, the correct field and it’s of the right type. Here is what the updated function could look like:
setDate : function(executionContext){
let formContext = executionContext.getFormContext();
Var dateFld == formContext.getAttribute("new_lastcontactdate");
if(null !== dateFld){
var fldType == dateFld.getAttributeType();
If (fldType == "datetime"){
var now = new Date();
formContext.getAttribute("new_lastcontactdate").setValue(now);
}
else {
return;
}
}
else {
return;
}
}
}
There is another component to defensive programming and it’s about
testing the actual execution steps within the function have executed correctly without
error, in JavaScript we use a function wrapper called ‘Try Catch’. I’ll save the explanation of what a try-catch
is and how to use it for another blog but the simplistic explanation is as follows:
We add a block around the execution statement and if an error occurs it
is trapped by the catch block, where we can then show it or do something else.
Execution Block
}
catch (error)
{
Display or do something with the error information
}
There are a couple of more pieces with the try-catch, they are ‘throw’
and ‘finally’, but I’ll cover these when I cover try-catch in more detail.