Write Custom Validation for Multiple Fields using MVC - asp.net-mvc

I have a customer form having phone number fields like CountryCode, AreaCode and PhoneNumber. I would like to write a custom validation for these 3 fields where all of them can either remain empty (they are optional) or all of them can remain filled (no field can be left empty if one or two are filled).
Am trying to write a custom validation for this situation, however, am not clear how to do it. Please help.

It's unclear exactly what you're looking for here. If you're just struggling with the boolean logic, all you need is:
if (!String.IsNullOrWhiteSpace(model.CountryCode) ||
!String.IsNullOrWhiteSpace(model.AreaCode) ||
!String.IsNullOrWhiteSpace(model.PhoneNumber))
{
if (String.IsNullOrWhiteSpace(model.CountryCode)
{
ModelState.AddModelError(nameof(model.CountryCode), "Country code is required.");
}
if (String.IsNullOrWhiteSpace(model.AreaCode)
{
ModelState.AddModelError(nameof(model.AreaCode), "Area code is required.");
}
if (String.IsNullOrWhiteSpace(model.PhoneNumber)
{
ModelState.AddModelError(nameof(model.PhoneNumber), "Phone number is required.");
}
}
Essentially, you just first check to see if any of them have a value. Then, you individually add an error for each one that does not have a value.
That said, these broken out phone number fields are atrocious. I'm not sure where the idea came from, but it's like you just can't get people off of them now. Phone number formats vary wildly, and not every phone number actually has an "area code". It's far better to have a single "phone" field where the user can simply type their entire phone number. Then, you can use something like this port of Google's libphonenumber library to validate the number and format it into a standard form. You can even use the library to parse out the individual pieces of country code, area code, and number, if you need to store it like this. Just be prepared that the area code may not have a value and even if it does, it may not be exactly 3 digits. Same goes for number portion, as well: you can't assume it will always be 7.
Validate a phone number
var phoneUtil = PhoneNumberUtil.GetInstance();
try {
var phoneNumber = phoneUtil.Parse(model.Phone, countryISO2);
if (!phoneUtil.IsValidNumber(phoneNumber))
{
ModelState.AddModelError(nameof(model.Phone), "Invalid phone number.");
}
} catch (NumberParseException) {
ModelState.AddModelError(nameof(model.Phone), "Invalid phone number.");
}
Where countryISO2 is the two character country code: "US", "GB", etc. If you want to accept international phone numbers, you should collect the country from the user.
Format a phone number
phoneUtil.Format(phoneNumber, PhoneNumberFormat.NATIONAL);
Get component parts of a phone number
var countryCode = phoneNumber.CountryCode;
string areaCode;
string number;
var nationalSignificantNumber = phoneUtil.GetNationalSignificantNumber(phoneNumber);
var areaCodeLength = phoneUtil.GetLengthOfGeographicalAreaCode(phoneNumber);
if (areaCodeLength > 0) {
areaCode = nationalSignificantNumber.Substring(0, areaCodeLength);
number = nationalSignificantNumber.Substring(areaCodeLength);
} else {
areaCode = "";
number = nationalSignificantNumber;
}

Related

How to get SwiftUI TextField to show numbers but also accept an empty value

I am trying to get a text field that only accepts numbers but will also accept an empty value (ie. it is an optional field). This is what the textfield looks like:
TextField("Phone Number", value: $phoneNumber, formatter: numberFormatter)
.disableAutocorrection(true)
.autocapitalization(.none)
and my number formatter looks like this:
#State private var numberFormatter: NumberFormatter = {
var nf = NumberFormatter()
nf.numberStyle = .none
nf.zeroSymbol = ""
return nf
}()
I have tried two different solutions yet both have problems. The first solution is to define phoneNumber as an optional integer Int? so that the TextField will accept a blank input:
#State var phoneNumber: Int? = nil
However, this messes with the TextField so that when I change its value via the app the changes don't update the actual variable phoneNumber. So, when I go to send my data in, phoneNumber still stays at nil. I think this has something to do with the numberFormatter and how it wont't accept nil variables.
So, my second solution is to initialize phoneNumber as an Int like so:
#State var phoneNumber: Int = 0
This solution does update phoneNumber when I change the text in the TextField. However, it will only show a blank space in the TextField box when I type in 0 (because of my .zeroSymbol definition in the numberFormatter). When I try to just put a blank space (ie. delete all the text) and then click out of the TextField, it just reverts back to the number that it was before. This same thing happens when I put a non-numeric entry into the field, which I am okay with because it should only accept numbers, but I want to still allow the user to include a blank entry.
I am using XCode and creating an iOS app. Thank you for any help.
A possible solution is to intercept input/output via proxy binding and and perform needed additional validation/processing.
Tested with Xcode 13.3 / iOS 15.4
Here is main part:
TextField("Phone Number", value: Binding(
get: { phoneNumber ?? 0},
set: { phoneNumber = phoneNumber == $0 ? nil : $0 }
), formatter: numberFormatter)
Complete test module in project
Another solution that I found is just to make phoneNumber a string with default "". Then, validate it by trying to convert it to an Int via Int(phoneNumber). If Int(phoneNumber) == nil, then raise an error. Otherwise, you now have a new optional integer that can be nil or the integer provided:
var intPhoneNumber: Int?
if phoneNumber == "" {
intPhoneNumber = nil
} else {
intPhoneNumber = Int(phoneNumber)
if intPhoneNumber == nil {
completion(.failure(.custom(errorMessage: "Please enter a valid phone number. Try again.")))
return
} else if intPhoneNumber! < 0 {
completion(.failure(.custom(errorMessage: "Invalid Phone Number. Please enter a valid 10 digit phone number.")))
return
} else if numberOfDigits(in: intPhoneNumber!) != 10 {
completion(.failure(.custom(errorMessage: "Invalid Phone Number. Please enter a valid 10 digit phone number.")))
return
}
}

How do I differentiate between two countries with the same country code using libPhoneNumber-iOS?

I am using libPhoneNumber-iOS (https://github.com/iziz/libPhoneNumber-iOS) to parse international phone numbers, and I am having a bit of trouble differentiating between different countries with the same country code (e.g. USA and Canada both use +1). Does anyone know if this library does this?
Here is the code I'm using to retrieve the country code (region code i.e. "US"):
let fullPhoneNumber = "+15065552222"
guard let phoneUtil = NBPhoneNumberUtil.sharedInstance() else {
return "error"
}
do {
let phoneNumber = try phoneUtil.parse(fullPhoneNumber, defaultRegion: "ZZ")
return phoneUtil.getRegionCode(forCountryCode: phoneNumber.countryCode)
} catch {
return "error"
}
This code returns "US" for the country code, even though it is a Canadian number. Does anyone know what I can do to get the accurate country using this library?

Can I use iOS9 Contacts Framework to get a formatted phone number?

I would like to be able to store a phone number in a standard way, e.g. just the digits (potentially with the '+' for the country code), something like these examples...
"17185555555"
"+17185555555"
"+447788888888"
... but I'd like to be able to DISPLAY it to a user in a properly formatted way, e.g.
"1 (718) 555-5555"
"+1 (718) 555-5555"
"+44 (7788) 888888"
...WITHOUT having to rely on a CNContactViewController to format it.
Obviously doing this just for US/Canada numbers would be easy - it's just one standard format - but I'd like it to be generic so it can work for numbers from any country. I know this question gives an answer for US/Can numbers only.
I know that I could use a CNContactViewController to display the number in the correct format, e.g.
let newContact = CNMutableContact()
newContact.givenName = "John"
newContact.familyName = "Smith"
newContact.phoneNumbers = [CNLabeledValue(label: CNLabelPhoneNumberiPhone, value: CNPhoneNumber(stringValue:"17185555555"))]
let contactView = CNContactViewController(forContact: newContact)
self.presentViewController(contactView, animated: true, completion: nil)
This will show the number on screen properly formatted, i.e.
1 (718) 555-5555
... so I know that something in the framework can do it. (This approach works for other country phone number formats, as long as you prefix the number with the right country code - e.g. "+44...")
From various other questions I know that I can get the raw digits and country code out of a CNContact, e.g. (following above example)
for pNumber: CNLabeledValue in newContact.phoneNumbers {
let value = pNumber.value as! CNPhoneNumber
let cc = value.valueForKey("countryCode") as? String
let digits = value.valueForKey("digits") as? String
print("cc:" + cc + ", " + digits)
}
... but this will just show the unformatted string again - not what I am looking for in this case.
Any help or other recommended approaches really appreciated!
My answer proposes another lib
You can format your numbers with this lib.
And you can use like this:
let phoneNumber: NBPhoneNumber = try phoneUtil.parse("17185555555", defaultRegion: yourRegion)
let formattedString: String = try phoneUtil.format(phoneNumber, numberFormat: .E164)

Phone number validation and formatting on iOS

I am using this library https://github.com/me2day/libPhoneNumber-iOS for validating the phone numbers.
I want to ask is that on whatsapp If you enter a phone number digits greater or less than the original Phone number of some country it shows error and give you a message that the digits are less than or greater than. How can I do this? how can I figure out the number of digits in phone number used in particular country and gives error according to that.
At the moment I am validating the phone number of country like this
let phoneUtil = NBPhoneNumberUtil.sharedInstance()
do {
let number:NBPhoneNumber = try phoneUtil.parse(phoneNumber, defaultRegion:countryCode)
let isValidNumber: Bool = phoneUtil.isValidNumberForRegion(number, regionCode: countryCode)
return isValidNumber
}catch let error as NSError {
print(error.localizedDescription)
return false
}

Numeric Format for No Thousands Separator

Is there a number format that would produce a localized number without the thousands separator?
Globalize.format("1000.12", "n?" )
I realize I could do:
Globalize.culture().numberFormat[","]="";
But I have some fields where I want it off and some where it should be on. For example... If the value is:
1000.123 -> Want it to show formatted to 1000,12 or 1000.12 depending on locale..But without the thousands separator.
You can use the "d" format instead of the "n" format to exclude the thousands separator.
Globalize.format(1000.12, "d");
Edit
Note that this will only work if you don't care about the decimal part.
If you care about the decimal part, as far as I know, you can't exclude the thousands separator except through one of the following methods:
Set the thousands character in the culture object to an empty string:
Globalize.culture().numberFormat[","] = "";
Globalize.format(1000.12, "n");
You could turn this into a utility function fairly easily:
function formatNumberNoThousands(num, format, culture) {
var numberFormat = Globalize.cultures[culture || Globalize.culture().name].numberFormat,
thousands = numberFormat[","];
numberFormat[","] = "";
try { return Globalize.format(num, format, culture); }
finally { numberFormat[","] = thousands; }
}
Perform a replace on the string result of the format:
Globalize.format(1000.12, "d").replace(new RegExp("\\" + Globalize.culture().numberFormat[","], "g"), "");
Which can also be easily turned into a utility function:
function formatNumberNoThousands(num, format, culture) {
return Globalize.format(num, format).replace(new RegExp("\\" + Globalize.culture(culture).numberFormat[","], "g"), "");
}
With this approach, if you know there will never be more than one thousands character in the formatted result you can remove the regexp. Otherwise if you plan on using this a lot or inside of a loop, you will want to cache the regexp and re-use it.

Resources