Vue.js prop validation for props - javascript

I have a child component with the following props declaration:
props: {
count: Number,
maxNum: Number
}
This is normally fine, but I also have maxNum mapped to an input field with:
<input type="text" v-model="maxNum" value="maxNum">
So even if the user puts "4" in the input, Vue.js thinks that it's a string, when in reality it's a valid number if parsed right.
I tried doing this, but it didn't fail properly on the input "apple":
props: {
maxNum: {
validator: function (v) {
parseInt(v);
return true;
}
}
}
What's the prescribed way of validating props when a v-model is involved?

Ah, so Vue.JS actually provides a nice way to do this apparently.
<input type="text" v-model.number="maxNum" value="maxNum">
The .number modifier lets the v-bind:value part of the v-model equation to treat the input value as a number.
Reference on v-model.number in Vue.JS guide is here.

I see you figured out how to solve your issue, but I'll still try to explain what went wrong here.
Issue 1
I'm guessing you expected parseInt to break on inputs without a number in them, but that's not the case. parseInt(null), parseInt('foo') and parseInt([{ bar: 'Baz' }]) will all work fine, they'll just return NaN. Consequently, your validator will always proceed to the second line and return true, thus treating any input as valid. To fix it, you would have to run parseInt on your input and check if it returns NaN, so your validator would like like this:
validator: function (v) {
return !isNan(parseInt(v));
}
Issue 2
You're not really looking for a validator. A validator just checks input values and decides whether they are valid or not, it doesn't change them in any way. In other words, the corrected validator above wouldn't cast '13' to 13, it would just prevent input like 'apple' or undefined. '13' would get through unchanged and you would have to manually cast it later on.
The feature you are looking for was provided as an optional coerce function on each component prop in Vue 1, but it was removed for version 2. If you ever need to do it in a slightly more complicated manner that isn't covered by the solution you linked to, the officially recommended way is to simply use a computed value.

Related

Getting props from children — default values

EDIT: CodePen link here.
I'm (ab)using JSX to make a form-building 'DSL' for some of my non-technical colleagues. There's a SingleChoice component that can be used like this:
<SingleChoice>
<Option value="A">
// ... what to show when A is chosen
</Option>
<Option value="B">
// ... what to show when B is chosen
</Option>
</SingleChoice>
The result is a <div> full of radio button inputs, and under the block of inputs, there are the conditional elements (based on what option is chosen).
In other words, the Option elements don't render anything by themselves, they are there just to signal to the parent how many radio buttons there are, what are their labels and what should be shown when X is chosen. They are literally empty shells, made just to carry their props, like this:
function Option({label, value, children}) { return <></> }
Yes, I could instead pass those as an array of objects { value: string, show: ReactNode }, but that's not a friendly syntax for my non-dev colleagues.
Now, to the question. In the parent I go through all his children and render the input based on their value:
...
{children.map(ch => <> <input ... /> {ch.props.value} </>)}
...
The problem is, this only works when I manually pass a value prop to the Option component. E.g. when I have
function Yes({..., value = "Yes"}) { ... }
and I do
<SingleChoice>
<Yes />
</SingleChoice>
the label is empty, as if it didn't see the default prop value. Why is that happening? Is it a bug? And how do I implement this properly? Remember, I don't really want to expose any of the implementation details to the user who writes the form (so no explicit callback passing).
The only "proper" way I could think of would be creating a context with a callback in the parent, which all the children would look up and call with their values. The problem is, there would be a lot of contexts made and updated this way, and I fear the performance implications.
I'm a bit confused as to what you want to have happen. As far as I can tell from your code on Codepen, a default prop value is never defined so when you call
<Yes/>
without passing it a prop it doesn't get rendered by your parent component. Also, a react function should just accept props as a parameter, you don't need to define value and children.
I'm answering in another answer to get the formatting
The first error you have is you run ReactDOM.render in the beginning of your code. You should add it at the end of the code, or else default values won't be used(I don't know why this is).
The second is that your functions aren't really react. In react functions take one parameter, props. You can reference value or children as props.value or props.children.
not react: function Yes({value = "Yes1", children}) { return <span></span> }
react: function Yes(props){ return <span>?+{props.value}</span> }
The third is that declaring defaultProps is done through the defaultProps command in react. So instead of setting a parameter to a value(because again, no parameters) you should write:
Yes.defaultProps={ value:"Yes1" }
Finally, your function singleChoice isn't really react as well. Theres definitely a cleaner way to refactor your code then scrolling through the yes components and checking their value. However, you can do what you want. If you fix the other problems it should work as you intended.

React can't null datetime-local input field

I'm using react together with react-semantic-ui and I'm having following input type:
<Input
onChange={e => this.onTextInputChange(e, 'date')}
value={this.state.searchQuery.date}
type='datetime-local'/>
onTextInputChange function sets input value as the state field and value of this input is bound to this field. Also, I have an additional button in my app which is responsible for clear form fields (by setting null on each state field).
state = {
searchQuery: {
date: null
}
}
Clear works pretty well for other fields, but the problem occurs when I touch datetime-local input. When I fill some part of the date I'm getting this message when I hit the clear button.
I'm able to clear this field only when I provide a full date. I know I can solve this issue by setting the value of this field to blank string ' ' during clear, but it isn't the best solution, because later I have to filter out those fields. I was even playing with react ref, but unfortunately I didn't achieve any positive result, what's more, blue cross which appears on hover clears the form. Any tips how can I handle it in a pretty way?
Edit #1:
event.preventDefault helped me to get rid of this validation message, but anyway input stays without cleared value.
You need to set the value of this.state.searchQuery.date to an empty string instead of null. In an input of type date, value={null} is not a valid as mentioned here. It is the only solution to this problem. If you specifically need null values you need to parse the empty string as null to avoid having to filter.
Value: A DOMString representing a date in YYYY-MM-DD format, or empty
Regarding the blue clear button, webkit has a pseudo-element input[type="date"]::-webkit-clear-button that allows you you style it after your needs. It is however pretty straightforward to implement a clear-button yourself as styling the button through CSS is not supported by all browsers.
Somehow I found the way to handle it.
I've made Ref in the constructor:
...
dateRef: any;
constructor(props: Readonly<Properties>) {
super(props);
this.dateRef = React.createRef();
}
...
assigned ref to my input:
<Input
...
type='datetime-local'/
ref={this.dateRef}
...
/>
In clearForm method I set '' on UI and null value in state.
private clearForm = (event: React.FormEvent): void => {
event.preventDefault();
this.date.current.inputRef.current.value = '';
this.setState({searchQuery: EMPTY_SEARCH_QUERY});
}
Also I'm preventing from search in case when input is invalid by accesing this.dateRef.current.inputRef.current.validity.valid
Maybe this solution isn't the best but it works.

Flow: false positives when type checking a dynamic object with dynamic accessors

What I am trying to do is pretty basic. Given an object, and without caring about the properties names, I want to ensure all its values to be of a certain type.
Therefore I have something like the following code:
// #flow
type DynamicStructure = {
[string]: number
}
const key: string = "someKey"
const someStructure: DynamicStructure = {
[key]: "invalid, should be a number"
}
The weird thing is that I am getting "no errors!" after applying Flow on the code above, which is clearly wrong. You can verify this behaviour on the Flow REPL
On the other hand, when I don't use dynamic accessors for the object everything work as expected. For example for the following code I get the expected errors:
// #flow
type DynamicStructure = {
[string]: number
}
const someStructure: DynamicStructure = {
"someKey": "invalid, should be a number"
}
Am I doing something wrong? or is this a Flow issue?
Thanks in advance.
Yes, this looks like a Flowtype bug: https://github.com/facebook/flow/issues/2928
Flow is a static type checker. It has a number of weaknesses around the handling of computed properties because of this.
You have a perfectly reasonable example of how flow 'could' work, but you're making the assumption that it tracks the assignment of a literal to a variable and that there are no side effects that might cause it to invalidate its idea of whether the variable remains the same when used as the computed property.
See refinement invalidations in the docs

jQuery validation, NOT the normal plugin

I've been looking at the jQuery Validation Plugin and it seems like overkill (my site's script requirements are ballooning), and also not quite what I want.
Let me define some terminology: an <input type="text"> field's status is VALID if it matches both RegEx 1 and RegEx 2, PARTIAL if it matches RegEx 1 but not RegEx 2, and INVALID if it doesn't match RegEx 1.
For example, RegEx 1 could be /^[A-Z_]*$/ and RegEx 2 could be /^[A-Z]+(_[A-Z]+)*$/.
The requirements are:
any key press which would lead to an INVALID status is ignored, without interfering with focus or the caret position, and without the value ever being seen to change,
otherwise the status is updated after every key press to be either VALID or PARTIAL, and
whenever an input's status changes, a callback is invoked.
Seems pretty straightforward. This is basically the QStringValidator model.
I have jQuery core but I'm new to it. How can I implement this? Thanks.
P.S. if the best solution lies outside of jQuery, IE support is not required.
if (this.value.match(this.getAttribute('data-regex1')) {
if (this.value.match(this.getAttribute('data-regex2')) {
do_whatever_for_full();
} else {
do_whatever_for_partial();
}
} else {
do_whatever_for_invalid();
}
Put your regexes into the element as custom attributes (data-regex1, data-regex2) then check the data as it changes (not sure how many events you want to check but you probably have to check with onkeydown ignoring enter and tab, or you could create a timer onfocus and just check every half second or so).

parseInt always returns NaN?

long story short, i was trying to validate a phone field. ive added
the isNaN and parseInt for checking the " " in the field but that said
This below never validates to true..what am i missing?
if(isNaN(parseInt(phone))){
error.text("Sorry but this phone field requires numbers only");
return false;
} else {
return true;
}
it always fails...it never reads true even when i enter a number in the field and submit.
i always get the error mssg.
EDIT: I am testing input values from a form, phone is the name of the field.
Various ways to coerse JS strings to numbers, and their consequences:
(source: phrogz.net)
I personally use *1 as it is short to type, but still stands out (unlike the unary +), and either gives me what the user typed or fails completely. I only use parseInt() when I know that there will be non-numeric content at the end to ignore, or when I need to parse a non-base-10 string.
Edit: Based on your comment, if using phone.val() fixed it then
You were using jQuery (which you never mentioned, and should have), and
You actually had/have a jQuery object, wrapping one or more DOM elements (probably just one).
Whenever you do var foo = $('…'); then the foo variable references a jQuery object of one or more elements. You can get the first actual DOM element from this via var fooEl = foo[0]; or var fooEl = foo.get(0);…but even then you still have a DOM element and not a particular property of that.
For form inputs, you need to get the .value from the DOM element, which is what the jQuery .val() method does.
parseInt is a bit odd at times:
> parseInt("123-456-789")
123
Fortunately you can probably solve your case with:
> Number("123-456-789")
NaN
parseInt only returns NaN if the first character cannot be converted to a number.
https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/parseInt
I've seen Number() suggested, but that will still allow things like -21 or 123.456. The best way to check for the absence of non-digits in a string is like this:
function hasNonDigit(str){
return /\D/g.test(str.toString());
}
console.log(hasNonDigit("123-456-7890"));
console.log(hasNonDigit("1234567890"));

Categories

Resources