Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 67 additions & 4 deletions apps/toolbox/src/pages/forms.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Page, Observable, EventData } from '@nativescript/core';
import { Page, Observable, EventData, TextField, PropertyChangeData } from '@nativescript/core';

let page: Page;

Expand All @@ -9,8 +9,71 @@ export function navigatingTo(args: EventData) {

export class SampleData extends Observable {
textInput = '';
textChange(args) {
console.log(args.object.text);
this.notifyPropertyChange('textInput', args.object.text);
formattedSSNInput = '';
formattedPhoneInput = '';
valueFormatterSSN = formatSSN;
valueFormatterPhone = formatPhoneNumber;

textChange(args: PropertyChangeData) {
console.log(args.value);
this.notifyPropertyChange('textInput', args.value);
}

textChangeSSN(args: PropertyChangeData) {
console.log(args.value);
this.notifyPropertyChange('formattedSSNInput', args.value);
}

textChangePhone(args: PropertyChangeData) {
console.log(args.value);
this.notifyPropertyChange('formattedPhoneInput', args.value);
}
}

function formatPhoneNumber(value: string, useParens?: boolean) {
value = value.replace(/\D/g, '');
var size = value.length;
if (useParens) {
if (size > 0) {
value = '(' + value;
}
if (size > 3) {
value = value.slice(0, 4) + ') ' + value.slice(4, 11);
}
if (size > 6) {
value = value.slice(0, 9) + '-' + value.slice(9);
}
} else {
if (size > 3) {
value = value.slice(0, 3) + '-' + value.slice(3, 10);
}
if (size > 6) {
value = value.slice(0, 7) + '-' + value.slice(7);
}
}
return value;
}

function formatSSN(value: string) {
// if input value is falsy eg if the user deletes the input, then just return
if (!value) return value;

// clean the input for any non-digit values
const ssn = value.replace(/[^\d]/g, '');

// ssnLength is used to know when to apply our formatting for the ssn
const ssnLength = ssn.length;

// we need to return the value with no formatting if its less then four digits
if (ssnLength < 4) return ssn;

// if ssnLength is greater than 4 and less the 6 we start to return
// the formatted number
if (ssnLength < 6) {
return `${ssn.slice(0, 3)}-${ssn.slice(3)}`;
}

// finally, if the ssnLength is greater then 6, we add the last
// bit of formatting and return it.
return `${ssn.slice(0, 3)}-${ssn.slice(3, 5)}-${ssn.slice(5, 9)}`;
}
36 changes: 23 additions & 13 deletions apps/toolbox/src/pages/forms.xml
Original file line number Diff line number Diff line change
@@ -1,15 +1,25 @@
<Page xmlns="http://schemas.nativescript.org/tns.xsd" navigatingTo="navigatingTo" class="page">
<Page.actionBar>
<ActionBar title="Labels and TextView" class="action-bar">
</ActionBar>
</Page.actionBar>
<ScrollView>
<StackLayout padding="20">
<Label text="TextField:" fontWeight="bold" />
<TextField textChange="{{ textChange }}" marginTop="6" backgroundColor="#efefef" padding="8" fontSize="18" keyboardType="url" />

<Label text="{{ textInput }}" marginTop="6" />

</StackLayout>
</ScrollView>
<Page.actionBar>
<ActionBar title="Labels and TextView" class="action-bar">
</ActionBar>
</Page.actionBar>
<ScrollView>
<StackLayout padding="20">
<Label text="TextField:" fontWeight="bold" />
<TextField textChange="{{ textChange }}" marginTop="6" backgroundColor="#efefef" padding="8" fontSize="18" keyboardType="url" />

<Label text="{{ textInput }}" marginTop="6" />

<Label text="SSN Formatted input:" fontWeight="bold" marginTop="12" />
<TextField hint="XXX-XX-XXXX" style.placeholderColor="silver" textChange="{{textChangeSSN}}" keyboardType="number" color="black" width="80%" borderColor="silver" borderWidth="1" height="40" textAlignment="center" borderRadius="4" valueFormatter="{{valueFormatterSSN}}" padding="5" maxLength="11">
</TextField>
<Label text="{{ formattedSSNInput }}" marginTop="6" />

<Label text="Phone Formatted input:" fontWeight="bold" marginTop="12" />
<TextField hint="XXX-XXX-XXXX" style.placeholderColor="silver" textChange="{{textChangePhone}}" keyboardType="number" color="black" width="80%" borderColor="silver" borderWidth="1" height="40" textAlignment="center" borderRadius="4" valueFormatter="{{valueFormatterPhone}}" padding="5" maxLength="12">
</TextField>
<Label text="{{ formattedPhoneInput }}" marginTop="6" />

</StackLayout>
</ScrollView>
</Page>
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export abstract class EditableTextBase extends TextBase implements EditableTextB
public autocorrect: boolean;
public hint: string;
public maxLength: number;
public valueFormatter: (value: string) => string;

public abstract dismissSoftInput();
public abstract _setInputType(inputType: number): void;
Expand Down
4 changes: 4 additions & 0 deletions packages/core/ui/editable-text-base/index.android.ts
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,10 @@ export abstract class EditableTextBase extends EditableTextBaseCommon {

public onTextChanged(text: string, start: number, before: number, count: number): void {
// called by android.text.TextWatcher
if (this.valueFormatter) {
this.text = this.valueFormatter(text.toString());
this.android.setSelection((this.text || '').length);
}
// const owner = this.owner;
// let selectionStart = owner.android.getSelectionStart();
// owner.android.removeTextChangedListener(owner._editTextListeners);
Expand Down
8 changes: 7 additions & 1 deletion packages/core/ui/editable-text-base/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class EditableTextBase extends TextBase {
/**
* Gets or sets the autofill type.
*/
autofillType: CoreTypes.AutofillType;
autofillType: CoreTypes.AutofillType;

/**
* Gets or sets whether the instance is editable.
Expand All @@ -58,6 +58,12 @@ export class EditableTextBase extends TextBase {
*/
maxLength: number;

/**
* Format input values
* Note: useful for input masking/formatting
*/
valueFormatter: (value: string) => string;

/**
* Hides the soft input method, ususally a soft keyboard.
*/
Expand Down
33 changes: 24 additions & 9 deletions packages/core/ui/text-field/index.ios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,16 +190,31 @@ export class TextField extends TextFieldBase {
}

if (this.updateTextTrigger === 'textChanged') {
// 1. secureTextEntry with firstEdit should not replace
// 2. emoji's should not replace value
// 3. convenient keyboard shortcuts should not replace value (eg, '.com')
const shouldReplaceString = (textField.secureTextEntry && this.firstEdit) || (delta > 1 && !isEmoji(replacementString) && delta !== replacementString.length);
if (shouldReplaceString) {
textProperty.nativeValueChange(this, replacementString);
if (this.valueFormatter) {
// format/replace
let currentValue = textField.text;
let nativeValueChange = `${textField.text}${replacementString}`;
if (replacementString === '') {
// clearing when empty
nativeValueChange = currentValue.slice(0, delta);
}

const formattedValue = this.valueFormatter(nativeValueChange);
textField.text = formattedValue;
textProperty.nativeValueChange(this, formattedValue);
return false;
} else {
if (range.location <= textField.text.length) {
const newText = NSString.stringWithString(textField.text).stringByReplacingCharactersInRangeWithString(range, replacementString);
textProperty.nativeValueChange(this, newText);
// 1. secureTextEntry with firstEdit should not replace
// 2. emoji's should not replace value
// 3. convenient keyboard shortcuts should not replace value (eg, '.com')
const shouldReplaceString = (textField.secureTextEntry && this.firstEdit) || (delta > 1 && !isEmoji(replacementString) && delta !== replacementString.length);
if (shouldReplaceString) {
textProperty.nativeValueChange(this, replacementString);
} else {
if (range.location <= textField.text.length) {
const newText = NSString.stringWithString(textField.text).stringByReplacingCharactersInRangeWithString(range, replacementString);
textProperty.nativeValueChange(this, newText);
}
}
}
}
Expand Down