I'm using ngx-chips to add a list of emails as tags into an input. A validator makes sure that each tag appears like an email.
How can I make sure that:
1) The validator only triggers when a tag is being added (i.e., user hits enter, space or comma)
2) If the email is not valid when enter/space/comma is hit, that the value persists (i.e., it does not clear...so that the user can fix it)
A stackblitz is here: https://stackblitz.com/edit/ngx-chips-example-2qdudc
Below is my email validator:
public validators = [ this.must_be_email ];
public errorMessages = {
'must_be_email': 'Please be sure to use a valid email format'
};
private must_be_email(control: FormControl) {
var EMAIL_REGEXP = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,3}$/i;
if (control.value.length != "" && !EMAIL_REGEXP.test(control.value)) {
return { "must_be_email": true };
}
return null;
}
Below is the tag:
<tag-input [(ngModel)]='emails'
name="emails"
#email="ngModel"
[errorMessages]="errorMessages"
[validators]="validators"
[editable]='true'
(onTagEdited)="onTagEdited($event)"
[separatorKeyCodes]="[32,188,186,13,9]"
[placeholder]="'Add email'"
[secondaryPlaceholder]="'Enter email address(es)'"
[clearOnBlur]="true"
[addOnPaste]="true"
[addOnBlur]="true"
[pasteSplitPattern]="splitPattern"
theme='bootstrap'
required >
</tag-input>
For 2), I tried changing "return null" to control.value in the validator...but that did not work
Ultimately the awkward implementation of this control makes things difficult. If it were a ControlValueAccessor with a model that includes the current input and array of tags, it would be a much simpler matter to find a solution.
As with @AlesD's answer, I went with a solution that utilizes onAdding
. One issue he brings up is the problem with using this
. To get around that problem I use the bind()
function when necessary.
In order to implement your desired behavior I did three things:
onAdding
. It validates the tag, and if the validation fails, sets addFirstAttemptFailed to true and returns an error observable (I upgraded to rxjs 6). Throwing this error prevents the tag from being added.Unfortunately the method that is called during onAdding has some rigs.
setInputValue()
, passing the value that's already set. Believe me I tried a thousand variants before I stumbled upon this side effect. Trying to call updateValueAndValidty()
on either the FormControl instance on your component or various instances of Form and FormControl within the TagInputComponent never quite worked.throwError()
. Unfortunately the way the subscription is setup internally is that the TagInput component only calls catchError()
on it's subscription callback function, not on the source observable. So the error gets displayed in the console. Again - I tried a bunch of different ways to get around it.@ViewChild('tagInput')
tagInput: SourceTagInput;
public validators = [ this.must_be_email.bind(this) ];
public errorMessages = {
'must_be_email': 'Please be sure to use a valid email format'
};
public readonly onAddedFunc = this.beforeAdd.bind(this);
private addFirstAttemptFailed = false;
private must_be_email(control: FormControl) {
if (this.addFirstAttemptFailed && !this.validateEmail(control.value)) {
return { "must_be_email": true };
}
return null;
}
private beforeAdd(tag: string) {
if (!this.validateEmail(tag)) {
if (!this.addFirstAttemptFailed) {
this.addFirstAttemptFailed = true;
this.tagInput.setInputValue(tag);
}
return throwError(this.errorMessages['must_be_email']);
}
this.addFirstAttemptFailed = false;
return of(tag);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With