I need to create a custom onChange event handler. I'm using formik library. I want to add a dash when a user inputs the 6. character in a field and remove the dash when the users removes the character in front o the dash.
I figured it out.
const [field, meta] = useField(props);
const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
let newEvent = event;
let value: string = event.target.value;
console.log(event);
if (value.length === 7 && value.at(-1)?.match(/[0-9]/)) {
newEvent.target.value = `${value.slice(0, 6)}-${value.at(-1)}`;
}
if (value.length === 7 && value.at(-1) === "-") {
newEvent.target.value = `${value.slice(0, 6)}`;
}
return field.onChange(newEvent);
};
Related
In redux Promise.resolve(dispatch(null)) is possible?
I am trying to dispatch null in order to create an action ? Will this be possible?
This is not possible in redux. As redux documentation says, an action can only be dispatched when it is a plain object and type of action is not undefined.
Here is the code snippet for dispatch function in redux/src/createStore.js file.
function dispatch(action) {
if (!isPlainObject(action)) {
throw new Error(
'Actions must be plain objects. ' +
'Use custom middleware for async actions.'
)
}
if (typeof action.type === 'undefined') {
throw new Error(
'Actions may not have an undefined "type" property. ' +
'Have you misspelled a constant?'
)
}
if (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
const listeners = currentListeners = nextListeners
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
return action
}
Instead of dispatching like the way you suggested, a better implementation would be
Promise.resolve(dispatch({ type: 'YOUR_SAMPLE_ACTION_TYPE', data: null }))
I hope it helps. Thanks!
I tried reactive form valueChanges but valueChanges method doesn't return input field name which has changed.
I thought code like this. but I think this is not smart way. Because I have to compare every each input field. so I need more smart way.
// get init view data from local storage
this.localstorageService.getPlaceDetail().subscribe(data => {
this.initPlaceDetail = data;
// watch changed value
this.editPlaceForm.valueChanges.subscribe(chengedVal => {
if (chengedVal['ja_name'] !== this.initPlaceDetail.languages.ja.name.text) {
this.changedJA = true;
}
if (chengedVal['ja_romaji'] !== this.initPlaceDetail.languages.ja.name.romaji) {
this.changedJA = true;
}
// ...... I have to check all input fields??
});
});
I'm adding form controls from an array and something like this worked for me. Just reference the item you need instead of expecting the valueChanges observable to pass it to you.
myFields.forEach(item => {
const field = new FormControl("");
field.setValue(item.value);
field.valueChanges.subscribe(v => {
console.log(item.name + " is now " + v);
});
});
This is my way to get changed control in form.
I shared for whom concerned.
Method to get list control changed values
private getChangedProperties(form): any[] {
let changedProperties = [];
Object.keys(form.controls).forEach((name) => {
let currentControl = form.controls[name];
if (currentControl.dirty)
changedProperties.push({ name: name, value: currentControl.value });
});
return changedProperties;
}
If you only want to get latest changed control you can use
var changedProps = this.getChangedProperties(this.ngForm.form);
var latestChanged = changedProps.reduce((acc, item) => {
if (this.changedProps.find(c => c.name == item.name && c.value == item.value) == undefined) {
acc.push(item);
}
return acc;
}, []);
Instead of listening to whole form changes you can listen to value changes event for each form control as shown in below code:
this.myForm.get('ja_name').valueChanges.subscribe(val => {
this.formattedMessage = `My name is ${val}.`;
});
I'm trying to update the Angular2 Forms Validation example to handle an Async Validation response. This way I can hit an HTTP endpoint to validate a username.
Looking at their code they currently aren't currently using a Promise and it's working just fine:
/** A hero's name can't match the given regular expression */
export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
return (control: AbstractControl): {[key: string]: any} => {
const name = control.value;
const no = nameRe.test(name);
return no ? {'forbiddenName': {name}} : null;
};
}
I'm trying to update to return a Promise. Something like:
/** A hero's name can't match the given regular expression */
export function forbiddenNameValidator(nameRe: RegExp): ValidatorFn {
return (control: AbstractControl) => {
const name = control.value;
return new Promise( resolve => {
resolve({'forbiddenName': {name}});
});
};
}
However, the result I get doesn't display the error message, it's showing undefined.
My thought is it has something to do with the way they are handling displaying the errors:
onValueChanged(data?: any) {
if (!this.heroForm) { return; }
const form = this.heroForm;
for (const field in this.formErrors) {
// clear previous error message (if any)
this.formErrors[field] = '';
const control = form.get(field);
if (control && control.dirty && !control.valid) {
const messages = this.validationMessages[field];
for (const key in control.errors) {
this.formErrors[field] += messages[key] + ' ';
}
}
}
}
However I'm not sure of a better way of doing this.
Angular2 example:
https://angular.io/docs/ts/latest/cookbook/form-validation.html#!#live-example
Link to my example attempting to return Promise:
https://plnkr.co/edit/sDs9pNQ1Bs2knp6tasgI?p=preview
The problem is that you add the AsyncValidator to the SyncValidator Array. AsyncValidators are added in a separate array after the SyncValidators:
this.heroForm = this.fb.group({
'name': [this.hero.name, [
Validators.required,
Validators.minLength(4),
Validators.maxLength(24)
],
[forbiddenNameValidator(/bob/i)] // << separate array
],
'alterEgo': [this.hero.alterEgo],
'power': [this.hero.power, Validators.required]
});
How can I do the following with the Ace Editor.
User types the '#' character
Autocomplete pops up
User makes a selection from the dropdown
The '#' gets removed now that the selection has been made
I basically want the # as a trigger for the autocomplete, but I don't want it hanging around after.
Thank you
addAutoComplete() {
var me = this;
ace.require('./config').loadModule('ace/ext/language_tools', () => {
this.aceEditor.setOptions({
enableBasicAutocompletion: true,
enableSnippets: true,
enableLiveAutocompletion: false
});
me.include = /[a-z.]/i;
this.aceEditor.on('change', (obj, editor) => {
switch (obj.action) {
case 'insert':
let lines = obj.lines;
let char = lines[0];
if ((lines.length === 1) && (char.length === 1) && me.include.test(char)) {
setTimeout(() => {
me.aceEditor.commands.byName.startAutocomplete.exec(me.aceEditor);
}, 50);
}
break;
}
});
});
}
that's the demo. works fine
We can bind the '#' keyword and trigger the autocomplete:
editor.commands.addCommand({
name: "myCommand",
bindKey: { win: "#", mac: "#" },
exec: function (editor) {
autocomplete();
}
});
autocomplete: function () {
staticWordCompleter = {
var getWordList = function(editor, session, pos, prefix, callback, isRHSEditor) {
var wordList = []; // add your words to this list
callback(null, wordList.map(function(word) {
return {
caption: word,
value: word
};
}));
editor.completers = [staticWordCompleter];
}
None of the above approaches worked for me. I ended up rolling my own autocomplete/autosuggest for this to work. It's hard to contrive an example but in short the steps are:
Capture the onChange of the editor
If the editor action meets the criteria (i.e. the # symbol is pressed), display a box at the cursor position. This can be done by setting the box to position absolute, and setting the top/left attributes:
const {row, column} = window.editor.getCursorPosition();
const {pageX, pageY} = window.editor.renderer.textToScreenCoordinates(row, column)
const autosuggest = document.getElementById("ELEMENT_NAME")
autosuggest.style.left = pageX
autosuggest.style.top = pageY
Add commands to disable/redirect any actions such as the arrow keys/enter key, and re-enable when a selection is made.
function disableEnterKeyInEditor() {
editor.commands.addCommand(commmand_enter);
}
function enableEnterKeyInEditor() {
editor.commands.removeCommands(commmand_enter);
}
command_enter = {
name: "enterKeyPressedCommand",
bindKey: {win: "Enter", mac: "Enter"},
exec: function (editor) {
return true;
}
};
This was much easier than working with ace's autocomplete.
You can check with below code:
var getWordList = function(editor, session, pos, prefix, callback, isRHSEditor) {
var wordList = [];
if(prefix === '#') {
wordList.push('add you're name list here');
}
callback(null, wordList.map(function(word) {
return {
caption: word,
value: word
};
}));
}
I been trying to find out how to make a textarea field in my Spring Webflow project we are using Dojo (dijit) for the forms. can someone please help. below is my code!
<td valign="top"><form:input path="bio" class="value" /> <script
type="text/javascript">
Spring.addDecoration(new Spring.ElementDecoration({
elementId : "name",
widgetType : "dijit.form.ValidationTextBox",
widgetAttrs : {
promptMessage : "Please enter your name from 2 to 10 characters",
invalidMessage : "A 2 to 10 characters value is required.",
required : true,
regExp : "^[a-zA-Z]{2,10}$"
}
}));
</script> <br />
<p></td>
this should work:
<form:textarea id="bio" path="bio" />
<script type="text/javascript">
Spring.addDecoration(new Spring.ElementDecoration({
elementId : "bio",
widgetType : "dijit.form.SimpleTextarea",
</script>
[EDIT] I just checked and textArea's don't seem to have any kind of validation, so I created one for you based on ValidationTextBox.
just put this somewhere in your javascript file:
dojo.provide("dijit.form.ValidationTextArea");
dojo.require("dojo.i18n");
dojo.require("dijit.form.TextBox");
dojo.require("dijit.Tooltip");
dojo.requireLocalization("dijit.form", "validate");
/*=====
dijit.form.ValidationTextBox.__Constraints = function(){
// locale: String
// locale used for validation, picks up value from this widget's lang attribute
// _flags_: anything
// various flags passed to regExpGen function
this.locale = "";
this._flags_ = "";
}
=====*/
dojo.declare(
"dijit.form.ValidationTextArea",
dijit.form.TextBox,
{
// summary:
// Base class for textbox widgets with the ability to validate content of various types and provide user feedback.
// tags:
// protected
templateString: "<textarea name=${name} ${nameAttrSetting} dojoAttachPoint='focusNode,containerNode,textbox' autocomplete='off'></textarea>",
baseClass: "dijitTextArea",
attributeMap: dojo.delegate(dijit.form._FormValueWidget.prototype.attributeMap, {
rows:"textbox", cols: "textbox"
}),
// rows: Number
// The number of rows of text.
rows: "3",
// rows: Number
// The number of characters per line.
cols: "20",
// required: Boolean
// User is required to enter data into this field.
required: false,
// promptMessage: String
// If defined, display this hint string immediately on focus to the textbox, if empty.
// Think of this like a tooltip that tells the user what to do, not an error message
// that tells the user what they've done wrong.
//
// Message disappears when user starts typing.
promptMessage: "",
// invalidMessage: String
// The message to display if value is invalid.
invalidMessage: "$_unset_$", // read from the message file if not overridden
// constraints: dijit.form.ValidationTextBox.__Constraints
// user-defined object needed to pass parameters to the validator functions
constraints: {},
// regExp: [extension protected] String
// regular expression string used to validate the input
// Do not specify both regExp and regExpGen
regExp: "(.|[\r\n])*",
regExpGen: function(/*dijit.form.ValidationTextBox.__Constraints*/constraints){
// summary:
// Overridable function used to generate regExp when dependent on constraints.
// Do not specify both regExp and regExpGen.
// tags:
// extension protected
return this.regExp; // String
},
// state: [readonly] String
// Shows current state (ie, validation result) of input (Normal, Warning, or Error)
state: "",
// tooltipPosition: String[]
// See description of `dijit.Tooltip.defaultPosition` for details on this parameter.
tooltipPosition: [],
_setValueAttr: function(){
// summary:
// Hook so attr('value', ...) works.
this.inherited(arguments);
this.validate(this._focused);
},
validator: function(/*anything*/value, /*dijit.form.ValidationTextBox.__Constraints*/constraints){
// summary:
// Overridable function used to validate the text input against the regular expression.
// tags:
// protected
return (new RegExp("^(?:" + this.regExpGen(constraints) + ")"+(this.required?"":"?")+"$")).test(value) &&
(!this.required || !this._isEmpty(value)) &&
(this._isEmpty(value) || this.parse(value, constraints) !== undefined); // Boolean
},
_isValidSubset: function(){
// summary:
// Returns true if the value is either already valid or could be made valid by appending characters.
// This is used for validation while the user [may be] still typing.
return this.textbox.value.search(this._partialre) == 0;
},
isValid: function(/*Boolean*/ isFocused){
// summary:
// Tests if value is valid.
// Can override with your own routine in a subclass.
// tags:
// protected
return this.validator(this.textbox.value, this.constraints);
},
_isEmpty: function(value){
// summary:
// Checks for whitespace
return /^\s*$/.test(value); // Boolean
},
getErrorMessage: function(/*Boolean*/ isFocused){
// summary:
// Return an error message to show if appropriate
// tags:
// protected
return this.invalidMessage; // String
},
getPromptMessage: function(/*Boolean*/ isFocused){
// summary:
// Return a hint message to show when widget is first focused
// tags:
// protected
return this.promptMessage; // String
},
_maskValidSubsetError: true,
validate: function(/*Boolean*/ isFocused){
// summary:
// Called by oninit, onblur, and onkeypress.
// description:
// Show missing or invalid messages if appropriate, and highlight textbox field.
// tags:
// protected
var message = "";
var isValid = this.disabled || this.isValid(isFocused);
if(isValid){ this._maskValidSubsetError = true; }
var isValidSubset = !isValid && isFocused && this._isValidSubset();
var isEmpty = this._isEmpty(this.textbox.value);
if(isEmpty){ this._maskValidSubsetError = true; }
this.state = (isValid || (!this._hasBeenBlurred && isEmpty) || isValidSubset) ? "" : "Error";
if(this.state == "Error"){ this._maskValidSubsetError = false; }
this._setStateClass();
dijit.setWaiState(this.focusNode, "invalid", isValid ? "false" : "true");
if(isFocused){
if(isEmpty){
message = this.getPromptMessage(true);
}
if(!message && (this.state == "Error" || (isValidSubset && !this._maskValidSubsetError))){
message = this.getErrorMessage(true);
}
}
this.displayMessage(message);
return isValid;
},
// _message: String
// Currently displayed message
_message: "",
displayMessage: function(/*String*/ message){
// summary:
// Overridable method to display validation errors/hints.
// By default uses a tooltip.
// tags:
// extension
if(this._message == message){ return; }
this._message = message;
dijit.hideTooltip(this.domNode);
if(message){
dijit.showTooltip(message, this.domNode, this.tooltipPosition);
}
},
_refreshState: function(){
// Overrides TextBox._refreshState()
this.validate(this._focused);
this.inherited(arguments);
},
//////////// INITIALIZATION METHODS ///////////////////////////////////////
constructor: function(){
this.constraints = {};
},
postMixInProperties: function(){
// Copy value from srcNodeRef, unless user specified a value explicitly (or there is no srcNodeRef)
if(!this.value && this.srcNodeRef){
this.value = this.srcNodeRef.value;
}
this.inherited(arguments);
this.constraints.locale = this.lang;
this.messages = dojo.i18n.getLocalization("dijit.form", "validate", this.lang);
if(this.invalidMessage == "$_unset_$"){ this.invalidMessage = this.messages.invalidMessage; }
var p = this.regExpGen(this.constraints);
this.regExp = p;
var partialre = "";
// parse the regexp and produce a new regexp that matches valid subsets
// if the regexp is .* then there's no use in matching subsets since everything is valid
if(p != ".*"){ this.regExp.replace(/\\.|\[\]|\[.*?[^\\]{1}\]|\{.*?\}|\(\?[=:!]|./g,
function (re){
switch(re.charAt(0)){
case '{':
case '+':
case '?':
case '*':
case '^':
case '$':
case '|':
case '(':
partialre += re;
break;
case ")":
partialre += "|$)";
break;
default:
partialre += "(?:"+re+"|$)";
break;
}
}
);}
try{ // this is needed for now since the above regexp parsing needs more test verification
"".search(partialre);
}catch(e){ // should never be here unless the original RE is bad or the parsing is bad
partialre = this.regExp;
console.warn('RegExp error in ' + this.declaredClass + ': ' + this.regExp);
} // should never be here unless the original RE is bad or the parsing is bad
this._partialre = "^(?:" + partialre + ")$";
},
filter: function(/*String*/ value){
// Override TextBox.filter to deal with newlines... specifically (IIRC) this is for IE which writes newlines
// as \r\n instead of just \n
if(value){
value = value.replace(/\r/g,"");
}
return this.inherited(arguments);
},
_setDisabledAttr: function(/*Boolean*/ value){
this.inherited(arguments); // call FormValueWidget._setDisabledAttr()
this._refreshState();
},
_setRequiredAttr: function(/*Boolean*/ value){
this.required = value;
dijit.setWaiState(this.focusNode,"required", value);
this._refreshState();
},
postCreate: function(){
if(dojo.isIE){ // IE INPUT tag fontFamily has to be set directly using STYLE
var s = dojo.getComputedStyle(this.focusNode);
if(s){
var ff = s.fontFamily;
if(ff){
this.focusNode.style.fontFamily = ff;
}
}
}
this.inherited(arguments);
if(dojo.isIE && this.cols){ // attribute selectors is not supported in IE6
dojo.addClass(this.textbox, "dijitTextAreaCols");
}
},
reset:function(){
// Overrides dijit.form.TextBox.reset() by also
// hiding errors about partial matches
this._maskValidSubsetError = true;
this.inherited(arguments);
},
_onBlur: function(){
this.displayMessage('');
this.inherited(arguments);
},
_previousValue: "",
_onInput: function(/*Event?*/ e){
// Override TextBox._onInput() to enforce maxLength restriction
if(this.maxLength){
var maxLength = parseInt(this.maxLength);
var value = this.textbox.value.replace(/\r/g,'');
var overflow = value.length - maxLength;
if(overflow > 0){
if(e){ dojo.stopEvent(e); }
var textarea = this.textbox;
if(textarea.selectionStart){
var pos = textarea.selectionStart;
var cr = 0;
if(dojo.isOpera){
cr = (this.textbox.value.substring(0,pos).match(/\r/g) || []).length;
}
this.textbox.value = value.substring(0,pos-overflow-cr)+value.substring(pos-cr);
textarea.setSelectionRange(pos-overflow, pos-overflow);
}else if(dojo.doc.selection){ //IE
textarea.focus();
var range = dojo.doc.selection.createRange();
// delete overflow characters
range.moveStart("character", -overflow);
range.text = '';
// show cursor
range.select();
}
}
this._previousValue = this.textbox.value;
}
this.inherited(arguments);
}
}
);
you can use it the way you use ValidationTextBox, just use ValidationTextArea instead.