I am trying to create an input box that only accepts Hex characters [A-Fa-f0-9].
I know there are a lot of options but have found none that ticks all my checkboxes.
Need to restrict actual user input (so characters other than A-Fa-f0-9 will NOT be allowed entry/keypress/paste
I know this can be done through keyup/keydown/onchange event, but I need this to be enabled globally, more elegant solution
Another option is the input pattern, but this still allows user entry of 'invalid' characters, will just display later on that the validation has failed.
My thoughts:
Top choice: If it is possible to extend the input type:
<input type="hex">
If possible to have a vue directive for this (supposing q-input is my component):
<q-input v-hex>
So I created this vue directive that removes all non-hex characters and converts letters it to uppercase:
Vue.directive('hex', {
update: function (el, binding) {
console.log('Input ' + binding.value)
var newVal = binding.value.replace(/[^a-fA-F0-9\n\r]+/g, '')
console.log('New Hex Only Val = ' + newVal.toUpperCase())
binding.value = newVal
}
})
But it doesn't update the parameter passed in the v-model (parameter holding the binding.value).
Here is my fiddle:
https://jsfiddle.net/keechan/nade9cy0/3/
Sample input -> expected output:
123abc -> 123ABC
12hf23dn -> 12F23D
How can I update its respective v-model parameter? Please note that this directive will be used globally so no hardcoding anywhere.
Anyone help?
Thanks
Vue.directive('hexonly', {
bind(el, binding, vnode) {
const vm = vnode.componentInstance;
el.__unbindHexonly = vm.$watch('value', value => {
vm.__emitValue(value.replace(/[^a-fA-F0-9\n\r]+/g, '').toUpperCase());
}, { immediate: true });
},
unbind(el) {
el.__unbindHexonly && el.__unbindHexonly();
}
});
https://jsfiddle.net/6bw0zvdm/
Quasar uses some deferred model update magic, thus using specific private method __emitValue (source)
By the way, you don't need to specify directive expression ("txt"), v-hexonly will work.
Related
Hey I'm working on custom input component for integers only, with 2 modes based on acceptNegatives props, that indicates if it should accept the negative numbers or not, the problem is that my current implementation allows to add multiple - signs while the component should accept the negatives too.
What I'm trying to acomplish is to on prop acceptNegatives="true" the component should accept input or paste value like -2 or 2 not -0 or "--", "--2" because those values are treated as empty string for some reason.
Html of input component it uses default input from Buefy
<template>
<b-input
:placeholder="placeholder"
:value="value"
#input="handleChange"
#paste.native="handlePaste"
#keydown.native="handleKeyDown"
#mousewheel.native="$event.preventDefault()"
#blur="handleBlur"
:icon-right="iconRight"
:name="name"
autocomplete="off"
:type="type"
></b-input>
</template>
The JS part
Keydown event handler
handleKeyDown(event) {
const { key } = event;
if (this.acceptNegatives && this.isAdditionalKeyAllowed(event)) {
this.$emit("keydown", key);
return;
}
if (this.isAdditionalKeyAllowed(event) || this.isValidNumber(key)) {
return this.allowPositiveNumbersOnly(event);
}
event.preventDefault();
},
Is additional key
isAdditionalKeyAllowed(event) {
const allowedKeyWithoutMeta = Object.values(ALLOWED_KEYS).includes(keyCode);
if (this.hasAlreadyMinusSign(event.target.value, key)) {
return false;
}
if (this.acceptNegatives) {
return (
allowedKeyWithoutMeta || key === "-"
);
}
return allowedKeyWithoutMeta;
},
hasAleardyMinusSign fn to check if we already have minus sign, fails because - is treated as empty string
hasAlreadyMinusSign(value, key) {
return this.acceptNegatives && value.includes("-") && key === "-";
},
I've prepared also little demo how it works in Vue. Currently I have no idea how to resolve this. Should I use watcher or the value should be computed value somehow? Thanks for advice.
https://codesandbox.io/s/integernumberinput-j4jlj?file=/src/components/IntegerNumberInput/IntegerNumberInput.js
You need a way to strip the possibility of '------1', the easiest way to do this is using some regex:
inputVal() {
// Regex that replaces all occurrences of "-" except the last
return this.value.replace(/[-](?=.*[-])/g, "");
}
Then bind the input to this computed and use a getter and setter, something like this:
inputVal() {
get() {
// Regex that replaces all occurrences of "-" except the last
return this.value.replace(/[-](?=.*[-])/g, "");
},
set(val) {
this.value = val;
}
}
Or you could add the first snippet I've provided as a method that you call during some other validation process. That's up to you to decide
I would like to be able to use the free text input of the Floating Filter as a regular expression parser. Meaning that, for example, if in my Floating Filter I Have a value "Greg*", the filtered data should contain all values starting with "Greg".
Expected Usage Example
I want to be able to implement my own regular expressions rules to it. I understand I can do it in the filter button on the right but I want to do it on the free text input visible, without having the user clicking on a button.
I solved my question after looking more closely to the ag-grid documentation, thing that I should have done from the start 🙂
Since Floating Filters are actually showing their parent Filter state, one should then implement his own custom Filter and Floating Filter together. So I implemented two classes CustomTextFilter and CustomTextFloatingFilter, implementing respectively IFilterComp and IFloatingFilterComp from the ag-grid library.
export class CustomTextFilter implements IFilterComp{
// IFilterComp method implementations here [...]
}
export class CustomTextFloatingFilter implements IFilterComp{
// IFilterComp method implementations here [...]
}
Within CustomTextFilter class, the most important is to implement the doesFilterPass method, where we can implement our regexp logic. Below is an example with a rule that allows us to search for multiple strings separated by a comma:
doesFilterPass(params: IDoesFilterPassParams): boolean {
var passed = false;
var valueGetter = this.valueGetter;
var filterText = this.eFilterText.value
if (this.isFilterActive()) {
var value = valueGetter(params);
passed = filterText.toLowerCase().split(",").some((word: any) => {
return word !== null && word !== undefined && word.trim() !== ''
&& value.toString().toLowerCase().trim().indexOf(word) >= 0;
});
}
return passed;
}
Then in CustomFloatingFilter we make sure that we that we listen to the input event so we can transfer the value from the floating filter to the parent filter, and then refresh the grid:
init(params: IFloatingFilterParams): void {
// Some gui code here [...]
this.eFilterInput = this.gui.querySelector('input');
this.eFilterText = this.gui.querySelector('#floatingFilterText');
var that = this;
this.eFilterText.addEventListener("input", (event: any) => {
that.filterText = event.target.value;
params.parentFilterInstance((instance: any) => {
instance.setModel({ value: that.filterText });
instance.gridApi.onFilterChanged();
});
});
}
Hope this will help!
I have computed property in my data this.coinPairingOptions that needs to render its radio buttons based on some of the other fields in this schema. I have reduced the amount of code in the file to save space.
data: function () {
return {
schema: {
{model: "symbolPair", type: "radios", label: "Pair with", values:
this.coinPairingOptions, required: true}
},
computed: {
coinPairingOptions() {
console.log("computing coinPairingOptions")
let coin = this.model.symbol.toUpperCase();
let options = [];
if (this.model.exchange === 'Coinbase') {
options = this.getCoinbasePairs
} else if (this.model.exchange === 'Binance') {
options = this.getBinancePairs
} else {
}
console.log(options.get(coin));
return options.get(coin);
},
}
In the dev tools I can see the computed property changing to the correct values however it is not changing in the data. Apparently, this is appropriate behavior, but what is a way around this? I have tried putting {{this.coinPairingOptions}} in the html and it errors because it's a computed property with not value initially.
Any help would be appreciated!
You can't use computed property in data, because data evaluates before the computed properties did.
You can use a watcher to achieve the intended result. Have a look at the documentation, you can add the argument immediate to trigger the callback immediately with the current value of the expression.
Computed properties are already accessible in the template by using {{}}. You don't need to put a this in front of the computed.
How is it possible to get the new typed character (not the whole new value of the ngModel) then apply logic on it, then returning a new value for the ngModel ?
Example: let's say when a user types inside an input we want to take the new character he types then change it to something and append it to the previous value of the ngModel. Tried keypress and onModelChange but didn't seem to work
<ion-textarea #chat_input
(ngModelChange)="modelChanged($event)"
placeholder="Text Input"
[ngModel]="editorMsg"
(keyup.enter)="sendMsg()"
(ionFocus)="onFocus()">
</ion-textarea>
And My method is like:
modelChanged(e){
// e is the new value of editorMsg and not the new typed character, if I get the new character I can do
this.editorMsg+=e;
}
But I get only the new typed value from modelChange how can I get only the new typed character ?
I believe what you’re looking for is the keyup event.
You could have something like
<input (keyup)="onKey($event)">
And then in your component
onKey(event: any) {
console.log(event.key)
}
The important bit here is to use the event.key and not event.target.value. If you used the latter one it would always give you the input value instead of the input key
You can get more information for what you need here, where I got the relevant bit from
You can do the following:
modelChanged(e){
let lastChar;
if (e.length) {
lastChar = e[e.length -1]; // this gets the last character of the srting..
// do you logic with lastChar..
}
}
It works using (ngModelChange). See this StackBlitz
component.html
<input [ngModel]="name" (keypress)="onKeypress($event)">
component.ts
export class AppComponent {
name = 'try me';
onKeypress(event: KeyboardEvent) {
console.log(event);
// Do what you want...
this.name = event.key;
event.preventDefault();
}
}
I want to assign value to ng-model if it is empty. I have assign default value to ng-model input from controller but when user makes change to remove that value and makes input empty then I want to assign default value to ng-model.
For example.
Controller:
App.controller('mainController',['$scope', function($scope) {
$scope.assignOne= 16;
}]);
View:
<input ng-model="assignOne" ng-change="changeMe()"/>
Now, input value becomes 16.
If user make changes and manually removes this value 16 then I want default value 1 instead of empty string in changeMe() function.
Note:
I can check for empty ng-model and override value of ng-model in controller. But, Is there any way better way to use, something which angular provides.
I am just new to AngularJS.
Try $watch as follows
$scope.$watch(
"assignOne",
function handleChange( newValue, oldValue ) {
console.log( "assignOne:", newValue );
if(newValue == '') {
newValue = 16;
}
}
);
another solution to meet your sample:
App.controller('mainController',['$scope', function($scope) {
$scope.assignOne= 16;
// function called by " ... ng-change="changeMe() "
$scope.changeMe = function() {
if ($scope.assignOne == '') {
$scope.assignOne = 1;
}
}
}]);
PS: for the previous answer, there is a little miss: it doesn't change the "assignOne" var, but only a local copy, which is lost at the end of the function call... Correct it by changing $scope.assignOne !
PS 2: the 2 solutions are good, but can resolve different requirements :
$watch is like a trigger on the model object, and is the most efficient if you want check business rules on the navigator side
ng-change is more interesting for handling some interactions, which are not stricts rules...
The 2 ways could be used...