Can someone please help me understand dynamic refs in Vue3? I have a group of radio buttons and trying to return the value of the selected button. In the code below the same value is returned regardless of which button I click.
<script setup>
const props = defineProps({
mealServices: Array,
});
const activeMealService = ref([])
const setActiveMealService = () => {
console.log('meal id is ' + activeMealService)
};
</script>
<div v-for="mealService in mealServices" :key="mealService.id">
<input :ref="(el) => {activeMealService = mealService.meal_type_id}"
#click="setActiveMealService" type="radio" name="meal_type"
:id="mealService.meal_type_id"
:value="mealService.meal_type_id"
v-model="form.meal_type_id"/>
<label :for="mealService.meal_type_id">{{ mealService.meal_type.name }}</
</div>
:ref="(el) => {activeMealService = mealService.meal_type_id}" is called for every iteration, right?
I guess you're resetting the active meal in every render to the last meal.
You don't need a Template ref to get the current selected meal.
Try removing this line
Related
I am trying to add an onClick eventhandler into a material ui and sometimes it is called, sometimes it is not. However, it's working fine with regular buttons
handleClick = (event) => {
const value = event.target.value;
console.log(value);
this.setState({ filtered: this.state.videos.filter(item => {
return item.category === value
})
})
<Button value="java" onClick={this.handleClick} color="primary">Java</Button>
<Button value="React" onClick={this.handleClick} color="primary">React</Button>
<Button value="C#" onClick={this.handleClick} color="primary">C#</Button>
<Button value="javascript" onClick={this.handleClick} color="primary">JavaScript</Button>
when I updated to console.log to get event.target, I got the result shown in the image below
I found the issue, but still don't know how yo fix it. React adds two spans to the Button that have no attribute name, so when I click the button, the function gets called, but not when I click the span
You can use event.currentTarget.value instead of event.target.value.
Material-ui's Button has a nested span inside the button, so when you use event.target.value and the user clicks the span you get the span as event.target, if you'd use event.currentTarget you'd get the element that the event listener is attached to - the button.
See a working example: https://codesandbox.io/s/cocky-cookies-s5moo?file=/src/App.js
Inside your handle click, you could also do:
return (item.category === value || item.category === event.target.innerHTML)
But obviously CD..’s answer is better
Besides relying on currentTarget, you can always curry the parameter to the callback function (imagine you are not passing in static content, but maybe the index of an iteration or some dynamic values stored in an object, etc)
Example
handleClick = (value) => () => {
console.log(value);
this.setState({ filtered: this.state.videos.filter(item => {
return item.category === value
})
})
<Button value="java" onClick={this.handleClick('java')} color="primary">Java</Button>
<Button value="React" onClick={this.handleClick('React')} color="primary">React</Button>
<Button value="C#" onClick={this.handleClick('C#')} color="primary">C#</Button>
<Button value="javascript" onClick={this.handleClick('javascript')} color="primary">JavaScript</Button>
I am new to reactjs. A sign in and sign up component is created. When clicking the button, a classname is supposed to be added to the specific element, in my case, signWrapper. I've tried few ways but it doesn't work. How do I add a classname to a specific element by clicking the button?
My code is on the codesandbox. Any suggestions are highly appreciated.
https://codesandbox.io/s/stoic-hertz-pmk23?file=/src/SignInAndSignUp.js:0-2298
you used your onClick function in a wrong way . you have to invoke this .
instead of this
onClick = { () => this.handleClick}
you have to do this
onClick = { () => this.handleClick()}
and handle your onClick like this
handleClick = () => {
this.setState({ active: !this.state.active }); // to toggle the state
};
here is the working example https://codesandbox.io/s/delicate-star-ghp7h?file=/src/SignInAndSignUp.js:283-360
Invoke your onClick function like so:
onClick={this.handleClick}
handleClick = () => {
// I added ...this.state so it doesn't affect any other state variables you may add
this.setState({ ...this.state, active: !this.state.active });
};
Also, check out the library clsx. It's very helpful when you have multiple classes needing to be activated or unactivated based on conditionals. This would look something more like the following:
className={clsx('signWrapper', { 'right-panel-active': active })}
As a noob to the JS universe, I had a question pertaining to a click event for a basic Quote Generator program. The idea is that upon clicking the '.generate' button, the content that appears within the 'taskEl' paragraph is deleted and refreshed with the next randomly generated quote. Thank you for any help with this.
<body>
<div class="card">
<button type ="button" class="generate">Generate Quote</button>
</div>
<script>
const quote = document.querySelector('.generate');
const card = document.querySelector('.card');
const myQuotes = ['It was the best of times, it was the worst of times', 'To the victor belong the spoils', 'Float like a butterfly, sting like a bee', 'No man is an island'];
const people = ['Charles Dickens', 'William Marcy', 'Muhammad Ali', 'John Donne'];
quote.addEventListener('click', getQuote);
function getQuote() {
const taskEl = document.createElement('p');
const taskEl2 = document.createElement('p');
let random = Math.floor(Math.random() * myQuotes.length);
taskEl.className = 'outputBox';
taskEl.appendChild(document.createTextNode(myQuotes[random]));
card.appendChild(taskEl);
taskEl2.className = 'sourceBox';
taskEl2.appendChild(document.createTextNode(people[random]));
card.appendChild(taskEl2);
};
getQuote();
</script>
I'd suggest you to place click attribute inside html element, like this:
<button type ="button" class="generate" onclick="getQuote()">Generate Quote</button>
To substitude the contents of a paragraph use innerText.
taskEl.innerText = 'some random quote';
I'm trying to determine the best way to establish cross-component-communication. My first thought was to use ractive.fire with a wildcard, but that doesn't seem to work. Am I trying to mis-use ractive.fire? What would be the suggested way for doing cross-component-communication with ractive?
Ractive.components.pubSub = Ractive.extend({
oninit() {
this.on('*.customEvent', () => alert('pub sub got your custom event!'))
}
})
Ractive.components.something = Ractive.extend({
template: '#something'
})
let ractive = new Ractive({
target: 'body',
template: '#app'
})
<script src="https://cdn.jsdelivr.net/npm/ractive#0.10.3/ractive.js"></script>
<script id="app" type="text/ractive">
<pubSub />
<something />
</script>
<script id="something" type="text/ractive">
<button on-click="#.fire('customEvent')">Fire Custom Event</button>
</script>
Ractive doesn't prescribe a convention for data sharing/cross-component communication. However, it does give you the facilities to do it. A common practice I've seen is to create a "dummy instance" and use its ractive.fire() and ractive.on() methods.
// The dummy instance, make it visible to components.
const pubsub = Ractive()
const SourceComponent = Ractive.extend({
template: '<button click="send()">Click me</button>',
send(){
pubsub.fire('message')
}
})
const ListeningComponent = Ractive.extend({
onInit(){
pubsub.on('message', () => {
console.log('called')
})
}
})
Alternatively, if all you want is to share state across all components, modify them anywhere you want, and have everyone re-render on change, you can put that state in #shared.
I am using Meteor with React. Consider this simple component below. There is a local mini-Mongo collection CommentsCollection. The component will insert a row in it when componentWillMount will be called. getMeteorData will return the first record in the collection and we'll be able to modify the title. Problem: if I place my cursor at the start of the title and start typing, after the first character update the cursor will jump to the end of the string and the rest of my typing will be placed there. How do I work around this?
CommentsCollection = new Meteor.Collection(null); // Local mini-mongo collection
EventTestComponent = React.createClass({
mixins : [ReactMeteorData],
componentWillMount(){
CommentsCollection.insert({title:"test title", message:"some test message"});
},
getMeteorData(){
return {
comment: CommentsCollection.findOne()
};
},
handleTitleChange(e){
CommentsCollection.update({_id: this.data.comment._id}, {$set:{title: e.target.value}});
},
render(){
if(this.data.comment) {
return (
<div>
<input type="text" value={this.data.comment.title} onChange={this.handleTitleChange}/>
</div>
);
}else{
return <div>Loading...</div>
}
}
});
I came up with this solution right after I posted the question:
<input type="text"
defaultValue={this.data.comment.title}
onKeyUp={this.handleTitleChange}/>
So: change value to defaultValue, and onChange to onKeyUp. Works like a charm!