Search code examples
htmlregexangularcurrencypercentage

Need a Regex to force either $ or % but not both


Currently, I have the following input defined on an HTML page inside an Angular 9 app:

<input type="text" formControlName="amountToWithholdInput"
       onkeyup="this.value = this.value.replace(/[^0-9.%$]/, '');">

As a person types, it automatically removes any character that isn't a number, a $, a % or a decimal.

How do I modify this so it will remove a % if they've already typed a $, and vice-versa (remove the $ if they've already typed a %)? In other words, it needs to check and see if a particular character exists, then remove the "opposite" character if they try to type that.

Or am I going about this all wrong? Is there some other way to do this that I haven't thought of?


Solution

  • Essentially you are asking your users to insert a numeric value with an optional fractional part and a trailing $ or % sign and everything else should be dropped on key input.

    So, we could use a regex that matches any string but keeps the fractional numbers and a single sign in an optional group and then replace the original string only with that group $1 while the remainder in the full match gets dropped. Try it:

    <input type="text" formControlName="amountToWithholdInput" 
        onkeyup="this.value = this.value.replace(/^((?:[0-9]+\.?[0-9]*)[%$]?)?.*/, '$1')">

    To make this work we need to ensure the inner regex can also match an incomplete version of the final string, i.e. the number, dot, and sign part need to be made optional as well. If you need a more specific (e.g. only two fractional numbers) or different order (e.g. dollar sign first, percent last) we can adjust the inner regex easily but the same concept can be applied, i.e.

    <input type="text" formControlName="amountToWithholdInput" 
        onkeyup="this.value = this.value.replace(/((?:[0-9]*\.?[0-9]{0,2}[%]?)|(?:[$]?[0-9]*\.?[0-9]{0,2}))?.*/, '$1')">

    Here, the order of the sub-patterns becomes important as we want to match only one sign and only in a specific position.