Beginning Domino developers often run into problems when writing formulas to hide or display text, fields, actions or view columns based on situation. This document addresses the common pitfalls.
Some parts of this document are specific to hide formulas, but a lot of it is just about how to write formulas, and some of the way experienced developers know to make their formulas simpler and more efficient.
NOTE: XPages and Custom Controls support the use of "display when" expressions instead of "hide when", and use JavaScript expressions. This document is about other types of design elements, which use formula "hide whens".
Why Isn't It "Display When"?
The decision was made in the early days of Notes that a value of True from a formula means to hide, and False to display. However, in many cases, the developer is thinking in the opposite way; they expect to write a "display when" formula that returns True when the information is supposed to be visible.
One way to handle this is with the "not" operator, ! (exclamation point). So if you are thinking, display this field when status is Released or the user is an Admin, you might write the (incorrect) formula:
REM {WRONG! Display when formula -- works backward from what you wanted.};
Status = "Released" | @UserRoles = "[Admin]"
For the corresponding
hide when formula, you can just use ! on the whole expression, e.g.:
!(Status = "Released" | @UserRoles = "[Admin]")
Notice the parenthesis!
Take care when negating expressions, because
!(A=B) is not the same as A!=B when either A or B is multivalued (as @UserRoles usually is). See below for more about this.
@If(...; @True; @False)
If you have an @If whose result clauses are the values @True and @False (in either order), your formula can be simplified.
|
|
|
|
|
|
|
|
|
@If(A; @True; B; @False; @True)
|
@If(A; @True; !(B))
or
A | !(B)
NOTE: the latter is not equivalent to the original expression. The original only evaluates B if A is false; A | !(B) evaluates B in all cases, which may be less efficient or may cause an error, depending.
|
And so on.
Comparing Scalars to Lists
Do not use @Contains to test whether a value is a member of a list. E.g. the hide formula:
REM {Do not do this...};
!@Contains(@UserRoles; "[Approver]")
is incorrect. That's because @Contains does a
substring search. So for instance:
A := "WILLIAM":"ROGER":"GLORIA";
B := "LIAM";
@Contains(A; B)
The formula returns TRUE because "WILLIAM" contains the substring "LIAM". When testing @UserRoles or @UserNamesList, you're wasting time if you search the values for substrings, and you may get an incorrect result. What you really want is to know whether any element of the list is exactly equal to the value you're looking for. For this, you can use the = operator.
REM {Hide from all but approvers};
!(@UserRoles = "[Approver]")
Notice the use of the ! operator here. This expression is not equivalent to:
REM {WRONG! Don't use != here.};
@UserRoles != "[Approver]"
This is a common error. The != operator returns True if the != comparison is true of
any element in the list. So for instance, in this formula:
A := "WILLIAM":"ROGER":"GLORIA";
B := "WILLIAM";
A != B
The formula returns True because there is an element of A ("ROGER") which doesn't equal B. In fact, the formula will always return True if B has one element and A has more than one unique value, since B can't possibly be equal to all of them.
(You might be thinking, what is != good for when you have a list, then? Basically, it has to work this way to be consistent with other comparison operators, but also you could use it to tell whether two multi-element lists are exactly equal -- if you also compare their @Counts -- so it's useful for that.)
To find out whether a scalar is
not in a list, then, you would use a formula of the form:
!(A = B)
If you prefer, instead of = you could use @IsMember or @IsNotMember, e.g.:
REM {Hide formula to only show to admins};
@IsNotMember("[Admin]"; @UserRoles)
From a performance standpoint, = and @IsMember are about the same; use whichever you feel most comfortable with.
Comparing Lists to Lists
In some cases, you might want to hide based on comparison of a list to another list. For instance, you want to display an action button to members of two roles, Admin and Editor:
REM {Hide formula to show only to Admins and Editors};
!(@UserRoles *= "[Admin]" : "[Editor]")
The *= operator is a "combinatoric" operator; it compares every element of one list to every element of another list. The = operator only compares pairs of values at matching positions. So for instance:
A := "7" : "2";
B := "2" : "9";
@Statusbar(@Text( A = B) + "-" + @Text(A *= B))
This formula displays "0-1" on the status bar. A = B is false because "7" != "2" and "2" != "9". But A *= B also compares the first element of A with the second element of B and vice versa.
Another way to see whether lists have any element in common is with the @Keywords function, thus:
@Elements(@Keywords(@UserRoles; "[Admin]" : "[Editor]"; "")) = 0
Notice the third argument to @Keywords, "", which tells the function to not look for word delimiters in the first argument, but just to compare elements to elements. This will prevent an erroneous result if the first argument does contain some delimiters. Even if you
know the first argument does not contain delimiters, it's more efficient to not make Notes waste time looking for them.
The *= operator is a little more efficient than using @Keywords, but for most applications, the difference is not enough to worry about.
@IsMember doesn't work to find out whether two lists have any element in common; but you can use it to find out whether one list completely contains another.
@UserRoles vs. @UserNamesList
If your formula tests whether a user is a member of a role, use @UserRoles function. @UserNamesList can also be used for this, but it's less efficient because it returns a longer list, including the user's groups
and roles.
If you are hiding based on group membership, please consider changing it to use roles instead. This is in part because testing for role membership is more efficient, and in part because it makes your design more portable. If your design contains the name of a specific group, you can't just make a copy of your application for another set of users. You would have to change the design for each different installation of the application. If you refer to a role instead, you can use the database ACL to control which groups are members of that role, so all copies of the application can continue to inherit from a single template.
Hiding Rich Text
Don't put a hide formula or check the boxes in the hide properties, for a rich text field on a form. For an explanation and alternatives, see this technote:
Unexpected behavior with hidden Notes Rich Text Fields.