I only want to line break if variable var is not a null value, how would I go about this?
<div>
{var && `${var}`} <br />
{var2 && `${var2}`}
</div>
Like any other conditional element
<div>
{var && `${var}`}
{var && <br />}
{var2 && `${var2}`}
</div>
Or shorter,
<div>
{var && <>{`${var}`}<br /></>}
{var2 && `${var2}`}
</div>
If you are only checking for null
{var !== null ? '<br />' : ''}
If any falsy statement is the condition you want to check (undefined , null , NaN , 0 , "" , and false)
{var ? '<br />' : ''}
You are creating HTML in JavaScript, so I assume you have used back-ticks around opening and closing div first. Then based on the value of var, You want to set the break point.
First thing is var is a keyword in Javascript and should not be used as the variable name.
Second is we can do something like this using ES6 back-ticks and interpolation.
let myVaribale = 23; // Not Null
const htmlTest = `<div>
<span>Testing the line-break</span>
${myVaribale && `<br/>` }
<p> Life is a test</p>
</div>`;
document.body.innerHTML = htmlTest;
<html>
</html>
Try using
{var ? '<br />': ''}
I'm trying to make a input box component that has instant feedback using Formik. I want the input box to turn green when the user input matches a predefined string (the "answer"), gray if the input matches the prefix of the answer (including the empty string) and red otherwise. This string is stored as a property of the initial values, values.answer. The Formik validate function checks if the input equals values.answer and sets values.correct = true. I then created a css class corresponding to a green input box and set the className of the input conditional on the value of values.correct. The problem is it only seems to update (i.e turn green with a correct input) when I click out of focus of the input box (i.e onBlur). I would like it to work onChange. How would I do this?
Here is the relevant code sandbox: https://codesandbox.io/s/instant-feedback-box-lub0g?file=/src/Frame.js
Cool problem, but you've overcomplicated your code a little bit 😉 Some feedback:
touched is set to true during onBlur by default. You can override this by using setTouched(), but I found it simpler to just use values instead of touched in your form
try to keep values as minimal as possible, it's only meant to access input values so there's no need for hint and answer to be assigned to it
the purpose of the validation function is to return an errors object and not to set values, so remove assignments like values.correct = true
You don't need to store isDisabled in state, you can derive it from formik.submitCount and formik.isSubmitting
const Note = () => {
const [showFrame, setShowFrame] = useState({ 1: true });
const onCorrectSubmission = (frameId) => {
setShowFrame({ ...showFrame, [frameId]: true });
};
const text =
"What is the sum of the first three natural numbers? (give answer as a word, i.e one, two etc.)";
const hint = "The first three natural numbers are 1, 2, and 3";
const answer = "six";
return (
<div>
<h1>Induction</h1>
{showFrame[1] ? (
<Frame
id={1}
text={text}
hint={hint}
answer={answer}
onCorrectSubmission={onCorrectSubmission}
/>
) : null}
{showFrame[2] ? (
<Frame
id={2}
text={text}
hint={hint}
answer={answer}
onCorrectSubmission={onCorrectSubmission}
/>
) : null}
</div>
);
};
const Frame = ({
id,
text,
hint,
answer,
values,
onCorrectSubmission,
...props
}) => {
const validate = (values) => {
const errors = {};
if (!answer.startsWith(values.cloze)) {
errors.cloze = hint;
} else if (values.cloze !== answer) {
errors.cloze = true;
}
return errors;
};
const formik = useFormik({
initialValues: {
cloze: ""
},
validate,
onSubmit: (values) => {
onCorrectSubmission(id + 1);
}
});
const isFinished = formik.isSubmitting || formik.submitCount > 0;
return (
<form enablereinitialize={true} onSubmit={formik.handleSubmit}>
<p>{text}</p>
<input
id="cloze"
name="cloze"
type="text"
autoComplete="off"
{...formik.getFieldProps("cloze")}
disabled={isFinished}
className={`input
${!answer.startsWith(formik.values.cloze) ? "invalid-input" : ""}
${formik.values.cloze && !formik.errors.cloze ? "valid-input" : ""}
`}
/>
{formik.values.cloze && formik.errors.cloze ? (
<div>{formik.errors.cloze}</div>
) : null}
<button disabled={!!formik.errors.cloze || isFinished} type="submit">
Submit
</button>
</form>
);
};
export default Frame;
Live Demo
I need to hide TimelineConnector if it is on the last item. How will i be able to hide it?
Pls check my codesandbox
CLICK HERE
{timelines.lastIndexOf(index) !== 1 ? <TimelineConnector /> : ""}
You could do using ternary operator
{timelines.map((timeline, index) =>
index !== timelines.length - 1 ? (
<TimelineItem>
...
</TimelineItem>
) : null
)}
Forked demo
Just compare to timelines length:
{index !== timelines.length - 1 && <TimelineConnector />}
{index < (timelines.length - 1) && <TimelineConnector />}
How about:
{timelines.length !== (index - 1) ? <TimelineConnector /> : null}
Simply add a check of length with index - 1
{index - 1 != timelines.length ? <TimelineConnector /> : ""}
Forked demo
I have this html:
<div
class="data__file"
v-for="(data, index) in paginatedData"
:key="index"
>
<label class="data__info" :for="data.idfile" #click="onClickWithShift($event, index)">
<img
:src="data.link"
alt=""
:class= "{ 'data__image' : 1 ,'data__image-active' : (data.checked === 1) }"
/>
<input
v-if="isManager === true"
type="checkbox"
class="data__access"
:value="data.idaccess"
:checked="(data.checked === 1) ? 1 : null"
v-model="checkedFilesPermission"
/>
<input
v-if="isManager === false"
type="checkbox"
class="data__access"
:value="data.idfile"
:checked="(data.checked === 1) ? 1 : null"
v-model="checkedFilesDownload"
/>
</label>
</div>
This code generate list of checkbox inputs, then I need when user click on label with shift (because input`s is display:none), all checkboxes between clicked inputs will checked or unchecked like it make with jquery here
How can I shift-select multiple checkboxes like GMail?
But I cant realise how I can get it.
Big thanks to user Spiky Chathu, I did how He said, and its work without v-model , but when I try use v-model, it doesn`t work.
also this is my data:
data() {
return {
isManager: this.$store.getters.isManager,
checkedFilesPermission: [],
checkedFilesDownload: [],
lastCheckedIdx: -1,
checkedCount: 0,
paginatedData: [
{"link":"images/2020/08/20200803.jpg","idfile":296,"idaccess":2},
{"link":"images/2020/08/20200807.jpg","idfile":6,"idaccess":99},
{"link":"images/2020/08/20200812.jpg","idfile":26,"idaccess":29},
{"link":"images/2020/08/202123.jpg","idfile":960,"idaccess":2919},
{"link":"images/2020/08/2020032.jpg","idfile":16,"idaccess":9339},
{"link":"images/2020/08/20200000.jpg","idfile":2,"idaccess":9},
]
};
I think main problem that VUE somehow block input with v-model
I have come up with a solution to your problem. I have added a mock object to recreate the same scenario hoping that you have a array of objects.
Editted : Solution has been modified to cater multiple deselect scenario
new Vue({
el: '#app',
data: {
paginatedData: [
{"link":"https://img.icons8.com/list","idfile":296,"idaccess":2},
{"link":"https://img.icons8.com/list","idfile":6,"idaccess":99},
{"link":"https://img.icons8.com/list","idfile":26,"idaccess":29},
{"link":"https://img.icons8.com/list","idfile":960,"idaccess":2919},
{"link":"https://img.icons8.com/list","idfile":16,"idaccess":9339},
{"link":"https://img.icons8.com/list","idfile":2,"idaccess":9},
],
lastCheckedIdx: -1,
checkedFilesPermission : []
},
methods: {
onClickWithShift(event, idx, idFile) {
var action = (this.checkedFilesPermission.indexOf(idFile) === -1) ? 'select' : 'deselect';
if (event.shiftKey && this.lastCheckedIdx !== -1) {
var start = this.lastCheckedIdx;
var end = idx-1;
// can select top to bottom or bottom to top
// always start with lesser value
if (start > end) {
start = idx+1;
end = this.lastCheckedIdx;
}
for (var c = start; c <= end; c++) {
var currentIdFile = this.paginatedData[c]['idfile'];
this.markSelectDeselect(c, action, currentIdFile);
}
}
this.markSelectDeselect(idx, action, idFile);
this.lastCheckedIdx = idx;
if (this.checkedFilesPermission.length === 0) {
//reset last checked if nothing selected
this.lastCheckedIdx = -1;
}
},
markSelectDeselect(idx, action, idFile) {
var currentPos = this.checkedFilesPermission.indexOf(idFile);
if (action === 'select' && currentPos === -1) {
this.checkedFilesPermission.push(idFile);
} else if (action === 'deselect' && currentPos !== -1){
this.checkedFilesPermission.splice(currentPos, 1);
}
}
}
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<div
class="data__file"
v-for="(data, index) in paginatedData"
:key="index"
>
<input
:id="data.idfile"
type="checkbox"
class="data__access"
:value="data.idfile"
v-model="checkedFilesPermission"
/>
<label class="data__info" :for="data.idfile" #click="onClickWithShift($event, index, data.idfile)">
<img
:src="data.link"
alt=""
:class= "{ 'data__image' : 1 ,'data__image-active' : (checkedFilesPermission.indexOf(data.idfile) !== -1) }"
/>
</label>
</div>
</div>
You need to click on the image icon to see the result, since you have mentioned the input is hidden. I kept it visible here so that you can see it is actually getting changed
Here's something I just tried that seems to do the work
<template>
<div>
<div v-for="(item, index) in items" :key="index">
<label>
<input type="checkbox" v-model="item.checked" #click="checked($event, index)">
{{item.file}}
</label>
</div>
<pre>{{items}}</pre>
</div>
</template>
<script>
export default {
data() {
return {
lastCheckedIndex: null,
lastChange: null,
items: [
{ file: "foo1", idx: 10 },
{ file: "foo2", idx: 20 },
{ file: "foo3", idx: 40 },
{ file: "foo4", idx: 30 },
{ file: "foo5", idx: 10 },
{ file: "foo6", idx: 90 },
{ file: "foo8", idx: 50 },
]
}
},
methods: {
checked(event, index) {
// wheter or not to the multiple selection
if (event.shiftKey && (null != this.lastCheckedIndex) && (this.lastCheckedIndex != index)) {
const dir = index > this.lastCheckedIndex ? 1 : -1; // going up or down ?
const check = this.lastChange; // are we checking all or unchecking all ?
for (let i = this.lastCheckedIndex; i != index; i += dir) {
this.items[i].checked = check;
}
}
// save action
this.lastCheckedIndex = index;
this.lastChange = !this.items[index].checked; // onclick is triggered before the default change hence the !
}
},
};
</script>
I'm trying to change the state of opening/closing times (and then post to an endpoint) on multiple days which are returned to me in an object like so:
I have rendered these in a React component:
{this.state.time.map(each => (
<Fragment key={each.code}>
<OpeningHours
code={each.code}
day={each.description}
name={each.code}
open={each.open !== null ? each.open : '00:00'}
close={each.close !== null ? each.close : '00:00'}
onChange={this.onTimeChange}
/>
</Fragment>
))}
The user will set these times by manually editing the time input. How would I get this open or close property of the day being edited and then store that in the time state? So far I've tried this, which works, but only if there was just an opening time or one field in general. The issue arises since I have 2 fields to edit:
onTimeChange(e) {
let times = this.state.time.slice();
for(let i in time){
if(times [i].name == event.target.name){
times [i].value = event.target.value;
this.setState ({time});
break;
}
}
}
EDIT: OpeningHours component
const OpeningHours = props => (
<div className={styles.block}>
<label htmlFor={props.code} className={styles.label}>{props.day}</label>
<div className={styles.container}>
<input
type="time"
name={props.name}
value={props.open !== null ? props.open : '00:00'}
onChange={props.onChange}
className={styles.timefield}
/>
<input
type="time"
name={props.name}
value={props.close !== null ? props.close : '00:00'}
onChange={props.onChange}
className={styles.timefield}
/>
</div>
</div>
);
One way to solve this could be to pass more information to the onChange function about what is actually changing.
onTimeChange(field, e) {
let times = this.state.time.slice();
for(let i in time){
if(times [i].name == event.target.name){
if(field === 'open') {
times [i].open = event.target.value;
this.setState ({time});
break;
} else if (field === 'closed') {
times [i].closed = event.target.value;
this.setState ({time});
break;
}
}
}
This would require a slight change to your input components:
<input
type="time"
name={props.name}
value={props.open !== null ? props.open : '00:00'}
onChange={ e => props.onChange('open', e) }
className={styles.timefield}
/>
You can bind identifying information to the onChange function and then use that to make the changes.
`<input onChange={this.handleChange.bind(this, props.code)}</input>`
Here is an example of how data is bound: https://jsfiddle.net/jphuangjr/aq7mtfgo/112/