I have a component called TextInput.vue, and inside I created a div.
<div ts-text-input :ts-text-input-filled="setFilledAttribute && !!value"
:ts-text-input-not-valid="!valueValid">
<input :value="value" #input="setValue" #keyup.enter="enterClicked" :placeholder="placeholder" :title="title">
what I wanted to do now is that to disable some spaces inside the input box so that the user is unable to type in with spaces/spacebar (like, e.g., username input box)
Here is what I have done; I try to use the function trim(), but it seems I can't still fix it.
in the computed function
computed: {
value: function() {
const {valueGetter, valueGetterOptions} = this,
getter = this.$store.getters[valueGetter];
value.trim();
return valueGetterOptions ? getter(valueGetterOptions) : getter;
},
Any hints would be helpful. thanks. (sorry for my bad English)
You can directly prevent that the user adds a white space to your input field. preventDefault() tells the user agent that the default action should not be taken as it normally would be.
<input #keydown.space="(event) => event.preventDefault()">
Or to make it even shorter (as Sovalina pointed out):
<input #keydown.space.prevent>
To prevent spaces on all input events - keypress, paste or drag and drop text:
const removeEventSpaces = e => {
e.preventDefault();
const left = e.target.value.substring(0, e.target.selectionStart);
const right = e.target.value.substring(e.target.selectionEnd, e.target.value.length);
const pasted = (e.dataTransfer || e.clipboardData).getData('text').replace(/ /g, '');
e.target.value = left + pasted + right;
}
<input #keydown.space.prevent #paste="removeEventSpaces" #drop="removeEventSpaces"/>
#keydown.native.space didn't work for me. #keydown.native.space.prevent did.
In this case, you can use Regular Expressions
value.replace(/\s/, '')
or to be sure the data is stored without any capital letters
value.replace(/\s/, '').toLowerCase()
u could use get and set
var inputData = ''
export default {
name: 'yourFromComponent',
computed: {
inputValue: {
get () {
return inputData
},
set (value) {
inputData = value.replace(/\s/g,'')
}
}
}
}
if you use vuex just change the inputData to your store referenz
Related
import React from 'react';
export default class CreateNote extend React.component {
constructor(props) {
super(props);
this.state = {note:{title:" ",content:" "} };
console.log(this.state);
}
const inputEvent = (event) => {
const value = event.target.value;
const name = event.target.name;
this.setState({
note:{title: ,content: }
})
}
render(){
return(
<input
type="text"
placeholder="Title"
name="title"
id=""
value={note.title}
onChange={inputEvent}
/>
<textarea
name="contant"
id=""
value={note.contant}
cols=""
rows=""
placeholder="Write a Notes"
onChange={inputEvent}
onClick={expanded}>
</textarea>
)
}
When I write text in the input field and textarea then do not go data to note state. I want the input data to go into the title and the textarea data to go into the content.
Now I will write what on setState? I want to see result in the console.
Tip: Your code is riddled with errors and typos (e.g., CreateNote extend React.component instead of CreateNote extends React.Component , writing const for a function inside a Class Component, contant instead of content). For better chances of getting a help, kindly post the working code that you have, so that duplicating the issue becomes easier for the people looking to help you.
Now on to the solution. If you are a beginner, the best way to get what you want is to make separate functions - one that is triggered when the text inside the input is changed, and the other which is triggered when the text inside textarea is changed.
Note that you have kept title and content inside a state object note. This means in order to change title or content, you have to update the entire note object. Be careful if you update just one key of note without persisting the other one (e.g., if you update title only and want to leave content unaffected), you should use the spread operator ... which helps to clone the object's values, so then after that you can update the value of the key you want.
titleChange = (e) => {
this.setState({
note: {
...this.state.note,
title: e.target.value
}
});
};
contentChange = (e) => {
this.setState({
note: {
...this.state.note,
content: e.target.value
}
});
};
Update, a cleaner approach: We can also use the "name" attribute of the input and textarea to our benefit and combine the two functions into one (I've named it inputChange )
inputChange = (e) => {
this.setState({
note: {
...this.state.note,
[e.target.name]: e.target.value
}
});
};
You can find a working CodeSandBox here. Note that using spread operator will not be necessary if you move out the required fields title and content out from notes and make them state variables directly. Check out the AppWithoutNote.js file to see how it can be implemented.
With draft-js and a styled component, I made an inline input intended for a calculator. When I type into it with a keyboard, it works as expected:
When I press the plus button, a "+" is added to the text, but the view doesn't scroll:
Here's the behavior and the code in a codesandbox:
https://codesandbox.io/s/charming-brattain-mvkj3?from-embed=&file=/src/index.js
How can I get the view to scroll when text is added programmatically like that?
At long last, I figured out a solution. I added this to the calculator input component:
const shell = React.useRef(null);
const [scrollToggle, setScrollToggle] = React.useState(
{ value: false }
);
React.useEffect(() => {
const scrollMax = shell.current.scrollWidth - shell.current.clientWidth;
shell.current.scrollLeft += scrollMax;
}, [scrollToggle]);
const scrollToEnd = () => {
setScrollToggle({ value: !scrollToggle.value });
};
Then at the end of the insertChars function I added scrollToEnd();.
And I set ref={shell} on <InputShell>
When trying to create a login form with outlined text fields in Vutify, the chrome autocomplete overlap with labels,
<v-text-field
v-model="email"
label="e-mail"
name="email"
outlined
prepend-icon="mdi-account"
type="text"
required
>
</v-text-field>
you can regeneare here please fill and submit, then go back.
This is how I have fixed this issue.
It seems our main problems are the following:
The autofill from Chrome, at loading page, not made interface react, letting the design like in your image.
So at the time of injection, we should do a fix ourself but no event, from Chrome, can inform us when login/password are auto-filled.
It's interesting to see any click from the browser window FROM USER automatically inform reactivity and all work fine again but it's not work FROM trigger/dispatch internal way.
So first, we need to find a way to react after login/password autofill.
And second, we need to fix ourself the design because only a FROM USER action made the design work fine again.
1. React after autofilling at loading page
Like Harkness mention it, we can try to check :-webkit-autofill at regular interval while X second after code was mounted to see if an autofilling was injected (work fine for Chrome/Firefox/Edge from my test)
Another solution is to use the animationstart event (see here: https://github.com/material-components/material-components-web/issues/4447#issuecomment-580401216)
I use the first solution:
export default {
//...
data() {
return {
//...
autofillFix: false,
}
},
//...
mounted() {
this.autoLoginCheckingInterface()
},
//...
autoLoginCheckingInterface() {
// each 100ms we check if the issue was produced
let intervalDetectAutofill = setInterval(() => {
if (
// we target at least one of the stuff that will be affected by autofill
// to do our checking
document.querySelectorAll('input[type="password"]:-webkit-autofill')
.length > 0
) {
// and we inform the system about the issue if it is produced
this.autofillFix = true
// we stop to check if issue was produced
clearInterval(intervalDetectAutofill)
}
}, 100)
// if after 3s nothing appear, means no autofill was made
setTimeout(() => {
if (intervalDetectAutofill) {
clearInterval(intervalDetectAutofill)
intervalDetectAutofill = null
}
}, 3000)
},
//...
}
<!--
we will inject `.autofill-fix` class to be able fix design ourself at time of this bug occur
-->
<v-text-field
...
:class="{ 'autofill-fix': autofillFix }"
...
label="Email address or username"
...
dense
outlined
#focus="autofillFix = false"
/>
<!--
we use #focus to let the normal behavior take again the lead
because we know this USER ACTION will made Chrome work well again
-->
<v-text-field
...
:class="{ 'autofill-fix': autofillFix }"
...
label="Password"
type="password"
...
dense
outlined
#focus="autofillFix = false"
/>
2. Fix ourself the design
We can see what are the change when v-text-field is filled. Without content, we can see this:
And after autofilling, we can see this:
So from the red part, we can see the following code need to be injected at time
of .autofill-fix presence to fix the design in the proper way
.autofill-fix.v-text-field--outlined.v-input--dense .v-label {
left: -28px!important;
transform: translateY(-16px) scale(.75);
}
Note: You need change the CSS selector if you not use outlined or dense. Be careful about the specificity of selector https://specificity.keegan.st/. In fact, you need adapt the fixed change to your design
Another way is to defined like #elazard suggest here an autofill variable like this
data () {
return {
login: null,
password: null,
autofill: false,
intervalDetectAutofill: null
}
},
<v-text-field
v-model="password"
type="password"
label="Password"
:placeholder="autofill ? ` ` : null"
/>
With the solution given by #adam-reis, in the mounted() of the login page
mounted () {
// search for autofill every 100ms
this.intervalDetectAutofill = setInterval(() => {
if (document.querySelectorAll("input[type=\"password\"]:-webkit-autofill").length > 0) {
this.autofill = true
}
}, 100)
// clean interval if needed after 3s
setTimeout(() => {
if (this.intervalDetectAutofill) {
clearInterval(this.intervalDetectAutofill)
this.intervalDetectAutofill = null
}
}, 3000)
},
And of course setting autofill to false if user input
watch: {
password () {
this.autofill = false
},
autofill () {
// clean interval if autofill detected or user input
if (this.intervalDetectAutofill) {
clearInterval(this.intervalDetectAutofill)
this.intervalDetectAutofill = null
}
}
},
I believe I've achieved a good result with very generic few lines of coding.
mounted() {
setTimeout(() => {
const els = document.querySelectorAll("input:-webkit-autofill")
els.forEach((el) => {
const label = el.parentElement.querySelector("label")
label.classList.add("v-label--active")
})
}, 500)
},
If the browser autofill the v-text-field, this code will add the "active" class to the Label. The v-text-field behaves have no change.
ok so what i did is something like this :
on the input
:placeholder="!autofilled ? ' ' : ''"
in the script
data() {
return {
form: {
email: '',
password: '',
},
error: null,
autofilled: false,
};
},
watch: {
'form.email'() {
this.autofilled = true;
},
},
What it does : basically setting placeholder to one blank space always "raises" the label. the unfortunate side is that setting it statically will make the label unable to go back down even if you empty the input after it is filled. so what i did is make the placeholder dynamic and only set it as a blank space before any change is made to the input after that placeholder goes back to nothing.
it isnt perfect because on initial load before the user has a password saved the labels will be raised but i havent found anything much better than that.
The autofill feature on browsers usually works by straight away setting the value of the fields in question. In this case, the label of the fields, moves out of the way only when the input field is focused, and stays away when it blurs with a value in the field. In case of autofill, the focus event isn't triggered, so the label stays where it is.
To fix this behaviour, you would have to (or get someone to) make changes in Vuetify.
You could give your input an id and read the input's value when the component is mounted and if it's anything else than empty then you could set your data value to that value that the input is holding, that way the label will go up as you would expect. Based on your more recent comments it seems like you would also need to wait for the DOM to be updated so the best thing we can do is to do our check with the help of nextTick:
mounted() {
this.$nextTick(() => {
const emailValue = document.getElementById('email').value;
this.email = emailValue || '';
});
}
I have a textBox which will allow me to enter the building name and after I click on 'Add Building' button, it takes the value and passes it to the next page.So, in my screenshot below, the values 'AbsBuildingOne' and 'AbsBuildingTwo' comes from the textBox input.So, currently I am able to pass a single value from the textBox to the next page, but I am not able to understand if I want to enter the second building how do I save the previous value in this page and add the second value below it. I guess I have to do it with arrays, but I am not able to figure it out how to proceed.
Currently, my code looks as shown below for passing the single value.I have added a state:
this.state = {
textBoxValue: '',
};
So, in my textBox the onUpdateHandler function looks like this:
onClickAddBuildingTextBoxHandler = (inputData) => {
this.setState({ textBoxValue: inputData.value});
}
My textBox component looks like this.Its a custom textBox designed for the project:
<SceTextBox
placeholder='Enter Building Name'
id='AddBuilding'
type='buildingName'
maxLength='40'
onTextUpdatedHandler={this.onClickAddBuildingTextBoxHandler.bind(this)}
forceValidate={this.state.forceValidate}
validator={app.appUtil.validator}
isError={this.state.textBoxEmptyError}
errorMsg={this.errorMessage}
/>
So, currently I am passing the value of this.state.textBoxValue to my next component where the building names are displayed.
So, how do I proceed with multiple values and how do I save all the building names? Also, I have to delete the building name when i click on close icon. So do I have to work with push/pop in array? Someone please guide me with this.
Edit 1:
My new state looks like this:
this.state = {
buildingNames: {
[id++]: ''
}
};
Both my functions are:
onClickAddBuildingButtonHandler = () => {
const { buildingNames } = this.state;
this.setState({
buildingNames: {
...buildingNames,
[id++]:''
}
});
if (!this.state.selectedOptions) {
this.setState({ isRadioEmptyError: true });
if (!this.state.buildingNames.id) {
this.setState({ textBoxEmptyError: true });
}
}
if (this.state.selectedOptions && this.state.buildingNames) {
this.props.navigateToAddBuilding(this.state.buildingNames);
}
}
onClickAddBuildingTextBoxHandler = (inputData) => {
const { buildingNames } = this.state;
this.setState({
buildingNames:{
...buildingNames,
[inputData.id] : inputData.value
}
});
}
To get an array of values from a textarea you can use the this.state.value.split('\n') to separate split the string on each new line producing an array for you.
Below is a very simple example demonstrating its effectiveness. Just type in the textarea and look at the console. Once you make a new line you'll see a second element appears in the array.
function handleKeyUp() {
const textarea = document.getElementById('my-ta');
console.log(textarea.value.split('\n'));
}
<textarea id='my-ta' onKeyUp='handleKeyUp()'></textarea>
EDIT #1:
Here is a Code Sandbox Example 1 demonstrating how it is done. Type in the textarea, make a new line and type some more and then hit the toggle button. You'll see the values displayed and if you toggle back they're in the textarea still.
EDIT #2:
So here is another example of how something can be done. This will show you how to add an item to a list. Code Sandbox Example 2
I'm going to leave it up to you to combine the two methods.
I have a React application which displays some spans:
<span>Hello</span> <span>my</span> <span>name</span> <span> is </span> ...
I would like the user to select the text with the mouse like so
..and then get the selected value, or highlight the text etc.
How would I do this in React? I am not sure what event handlers to use and how to get hold of the current selection! A minimal example or a hint would be appreciated!
Use onMouseUp and onDoubleClick events to detect to call method, that will determine selection using JavaScript Selection API.
Use window.getSelection() to get selection object.
To get selected text, use window.getSelection.toString().
To get coordinates of selected text area for rendering popup menu, use selection.getRangeAt(0).getBoundingClientRect().
As an example implementation, take a look at react-highlight library.
There is no React-specific solution for this. Just use window.getSelection API.
To output highlighted text run window.getSelection().toString()
Here's an example in React using a functional component:
const Post = () => {
const handleMouseUp() {
console.log(`Selected text: ${window.getSelection().toString()}`);
}
return (
<div onMouseUp={handleMouseUp}>Text</div>
);
}
As Lyubomir pointed out the window.getSelection() API does the trick!
I think that it's the right way...
document.onmouseup = () => {
console.log(window.getSelection().toString());
};
Step 1: - Initially Mouse will capture every time you enter something
<textarea type="text"
className="contentarea__form__input"
onMouseUpCapture={this.selectedText}>
</textarea>
Step 2: To make sure it only catches your selected text I used if-else condition
selectedText = () => {
window.getSelection().toString() ? console.log(window.getSelection().toString()) : null;
}
Due to this bug in Firefox, I was unable to get it working with
window.getSelection() as suggested by other answers.
So I had to do the following (in React):
constructor(props) {
this.textAreaRef = React.createRef();
}
getSelection() {
const textArea = (this.textAreaRef.current as HTMLTextAreaElement);
console.log(textArea.value.substring(
textArea.selectionStart,
textArea.selectionEnd
)
);
}
render() {
<textarea onMouseUp={() => this.getSelection()}
ref={this.textAreaRef}>
</textarea>
}
I just made a custom hook for this (in typescript if needed):
export const useSelectedText = () => {
const [text, setText] = useState('')
const select = () => {
const selected = window.getSelection() as Selection
setText(selected.toString())
}
return [select, text] as const
}
This can be done using react-text-annotate
library: https://www.npmjs.com/package/react-text-annotate
We can do using state simply to select and highlight them.
import styled from 'styled-components';
const ClipBoardLink = styled.span<{ selection: boolean }>`
background: ${(p) => (p.selection ? '#1597E5' : '')};
margin-left: 0.5rem;
color: ${(p) => (p.selection ? '#fff' : '')};
`;
const GroupOrderModel = () => {
const [isCopied, setIsCopied] = useState(false);
const handleIsClipboardCopy = async (text: string) => {
setIsCopied(true);
navigator.clipboard.writeText(text);
setTimeout(() => {
setIsCopied(false);
}, 5000);
};
return (
<ClipBoardLink selection={isCopied} onClick={async () => await handleIsClipboardCopy("Click me!")}>Click me!</ClipBoardLink>
);
}