Prevent or Limit Special Characters

Has anyone created a way to prevent/limit special character entry into text/numeric fields?

The two components have entirely different approaches.

Text Field:
There's no way to "prevent" a user from typing restricted characters, but you can amend the value of the Text Field and remove any such characters with a script like this in the valueChanged script of props.value (you'll also need to set props.deferUpdates to have a value of false):

if currentValue and currentValue.value:
    disallowed = ["*", "[", "]"]
    reassembled = "".join([checked_char for checked_char in currentValue.value if checked_char not in disallowed]) 
    if reassembled != currentValue.value:
        if previousValue:
            self.props.value = previousValue.value
        else:
            self.props.value = ""

The Numeric Entry Field doesn't really allow for this sort of approach, but it natively prevents symbols which don't make sense for numbers. Your best option with the Numeric Entry Field is to simply supply valid maximum and minimum values and let the component perform its own validation.

What's the difference between

?

currentValue is a Qualified Value object, which can sometimes initialize as None leading to NoneType issues when attempting to immediately reference currentValue.value, which is the actual value of the property.

OK, so what your snippet does is compare the original string (previous value), to the invalid-character-checked string that the user typed. Correct?

So if I'm going to take the user's input and update the table data, after checking for the characters not allowed, I can simply run the update query.

How about creating an inheritable script that would use something like a table of special characters to disallow? Not sure how that would work out. If it's an actual table, then I'd have to create a dictionary/list to loop through? Compare characters, remove, reassemble...

Does that seem realistic?

Well, I just got the None Type error, lol

image

I think it doesn't like the previousValue.value

Do I need to instantiate it somehow? Well, now it works fine for some reason. Erred three times, now it's happy.

Well, sort of. Before you do anything, remember that this is being done in the valueChanged script, meaning this value has already been changed - so any table update should not occur here, lest it happen with the "invalid" characters included. What my script does is AFTER the user has input the invalid characters - and that value has been received and applied to the property - we then amend the value and replace it with the old value. But if your script to update a table is located elsewhere - like some Button click - then yes, you're safe to apply the sanitized value.

There's no reason you can't make a project level script which sanitizes inputs. For security reasons, your SQL should prevent any disallowed characters. Never rely on someone else for security where you can avoid it - even if you're a Database.

Hmmm. Thinking about it, previousValue.value would definitely be None the first time the script executes if you never gave the Text Field a value to start with. :man_facepalming: I edited the original script suggestion.

Thanks a bunch! I'm going to run with this and see how far I can take it, :slight_smile:

What do you think of this for a project script?

def charTest01(cValue, pValue):
# Compare the current value and previous value of text field
# look for any forbidden characters
# remove said characters
# leave sanitized string
	
	if cValue:
		disallowed = ["*","[","]","#","%","&"]
		reassembled = "".join([checked_char for checked_char in cValue if checked_char not in disallowed])
		if reassembled != cValue:
			if pValue:
				return pValue
			else:
				return ""

Edit: when passing values to the function, I'm going to have to use currentValue and previousValue aren't I?

Question on using currentValue: Why do we need to say if currentValue? What does that do exactly?

A few different things are happening with if currentValue and currentValue.value:

  1. Python evaluates different things as 'false' - besides the singleton False, also numeric values equal to 0, empty collections, empty strings, and the singleton None (AKA null in other languages).
  2. Python automatically coerces whatever expression is given in the if statement into a boolean; no need to type check.
  3. Python evaluates boolean expressions joined with and lazily; if the first is false, the second will not be evaluated.

So, if currentValue is None (null) when that statement is entered, the statement will immediately exit, and the second line of the if, checking currentValue.value, will not throw the TypeError that it otherwise would because NoneType has no attribute value.

2 Likes

And one more question for clarity:
currentValue.value is the value or text already in the text field, and previousValue.value is what was typed, then captured when focus moved to another component?

No.

Assuming your component has no starting value, here's a step-by-step flow of what's happening, with some unimportant steps omitted for brevity:

  1. Component initializes in session.
  2. User supplies character to Text Field ("a").
  3. TextField.props.value in the session is set to "a".
  4. The change script for TextField.props.value is executed, where previousValue (QualifiedValue object) is None, and currentValue is a QualifiedValue object which contains information about the current value ("a") of the Text Field.
  5. In your scenario, this script verifies validity of the string content, before potentially replacing the "new" value with what it was before.
  6. User supplies character to Text Field ("s").
  7. TextField.props.value in the session is set to "as".
  8. The change script for TextField.props.value is executed, where previousValue is no longer None, and now had a value attribute of "a", and currentValue is a QualifiedValue object which contains information about the current value ("as") of the Text Field.
3 Likes

Doh! :man_facepalming:
I had the current and previous backwards in my reply. Must have been a long day.