I am trying to both forward an event and let a value be set externally using Svelte. If a button is clicked, the greeting should have from "Hello" to "Goodbye".
App.Svelte
<script>
import Component from "./Component.svelte";
const handleMessage = (event, greeting) => {
if (greeting === "hello") {
greeting = "goodbye";
} else {
greeting = "hello";
}
};
</script>
<main>
<Component on:message={handleMessage}/>
</main>
Component.Svelte
<script>
import { createEventDispatcher } from "svelte";
const dispatch = createEventDispatcher();
export let name = "Friend";
export let greeting = "hello";
const func = () => {
dispatch("message", {});
};
</script>
<p> {greeting}, {name} </p>
<button on:click={dispatch}> switch greeting</button>
However, whenever I click the button, the desired effect is not achieved. What needs to happen such that clicking a button on the nested component (Component) changes the greeting?
Assuming the component hierarchy is as you describe, I wouldn't use dispatch for this. I would pass a click handler to the child component.
Relevant tutorial doc: https://svelte.dev/tutorial/dom-event-forwarding
App.svelte
<script>
import Component from "./Component.svelte";
let greeting = "hello"
let name = "world"
const onClick = () => {
if (greeting === "hello") {
greeting = "goodbye";
} else {
greeting = "hello";
}
}
</script>
<main>
<Component on:click={onClick} {greeting} {name}/>
</main>
Component.svelte
<script>
export let greeting;
export let name;
</script>
<p> {greeting}, {name} </p>
<button on:click> switch greeting</button>
If you really wanted to use dispatch:
App.svelte
<script>
import Component from "./Component.svelte";
let greeting = "hello"
let name = "world"
function handleMessage(e) {
if (e.detail.name !== "updateGreeting") return;
if (greeting === "hello") {
greeting = "goodbye";
} else {
greeting = "hello";
}
}
</script>
<main>
<Component on:message={handleMessage} {greeting} {name}/>
</main>
Component.svelte
<script>
import {createEventDispatcher} from "svelte";
export let greeting;
export let name;
const dispatch = createEventDispatcher();
const click = () => {
dispatch("message", {name: "updateGreeting"});
}
</script>
<p> {greeting}, {name} </p>
<button on:click={click}> switch greeting</button>
Related
I'm still in the process of learning React, I'm trying to implement an onClick function on a button element. The list of buttons are being rendered here:
<div className="buttons">
{ colours.map((colour, index) => (
<Button
key={ index }
onClick={() => this.checkChoice(colour)}
className="button"
>
{colour}
</Button>
))}
</div>
And here is the onClick function I have already defined.
checkChoice(col) {
const {correctIndex, colours} = this.state;
const newMessage = '';
if (col == colours[correctIndex]){
// correct colour chosen so update message
newMessage = 'Correct!'
} else {
newMessage = 'Wrong!'
}
console.log(newMessage);
}
I'm currently receiving no errors at all, however when I do click on a button, nothing is being printed to the console. Even when I try to call the function in the render method before returning, it still returns with nothing. I'm surely missing something silly, and would greatly appreciate any help.
Thanks!
Edit Here is the Component code:
const Button = ({ onClick, className = '', children }) =>
// {console.log(onClick)}
<button
onClick = { onClick }
className = { className }
type = "button"
>
{ children }
</button>
You should declare "newMessage" variable using let or var. You can't reassign value to a variable declared with const. I have assumed correctIndex state as 1.
import React from "react";
const Button = ({ onClick, className = "", children }) => (
// {console.log(onClick)}
{children}
);
class Car extends React.Component {
constructor() {
super();
this.state = {
correctIndex: 1,
colours: ["red", "green", "white"],
};
}
checkChoice(col) {
const { correctIndex, colours } = this.state;
let newMessage = "";
if (col == colours[correctIndex]) {
// correct colour chosen so update message
newMessage = "Correct!";
} else {
newMessage = "Wrong!";
}
console.log(newMessage);
}
render() {
const { colours } = this.state;
return (
{colours.map((colour, index) => (
this.checkChoice(colour)}
className="button"
>
{colour}
))}
);
}
}
export default Car;
I have this code here,
it basically fetch from firestore in the setup(), then display the information with the Categoria component. It also should update the Categoria components when the <span> is pressed. However, something don't work. My snippet successfully update the database but does not reload the component... any ideas?
<template>
<div class="header">
<span class="mdi mdi-home icona" />
<h1>Modifica menĂ¹</h1>
</div>
<Categoria
v-model="categorie"
v-for="categoria in categorie"
v-bind:key="categoria"
:Nome="categoria.nome"
/>
<div class="contenitore_aggiungi">
<span #click="crea()" class="mdi mdi-plus aggiungi" />
</div>
</template>
<script>
import Categoria from "#/components/edit/Categoria-edit.vue";
import { useRoute } from "vue-router";
import { creaCategoria, ottieniCategorie } from "#/firebase";
export default {
name: "Modifica",
components: { Categoria },
async setup() {
const route = useRoute();
let idRistorante = route.params.id;
let categorie = await ottieniCategorie(idRistorante);
console.log(categorie);
return { idRistorante, categorie };
},
methods: {
crea() {
let nuovaCategoria = "Nuova categoria";
creaCategoria(this.idRistorante, nuovaCategoria);
this.categorie.push({ nome: nuovaCategoria });
console.log(this.categorie);
},
},
};
</script>
Thanks for your answers!
You need to declare categorie as a reactive property. Also you can write methods in setup() itself instead of methods:
import { ref } from 'vue'
export default {
setup() {
const route = useRoute();
let idRistorante = route.params.id;
const categorie = ref({}) // <-- add default value of properties
const getData = async () => {
const data = await ottieniCategorie(idRistorante);
categorie.value = data
}
getData() // or void getData()
const crea = () => {
let nuovaCategoria = "Nuova categoria";
categorie.value.push({ nome: nuovaCategoria });
console.log(categorie.value);
},
return { idRistorante, categorie, crea };
}
}
Make sure the default value of categorie is set in ref(). If it's an array set it to ref([]).
I am working on trying to get this counter for pintsLeft to work. This is my first project with React and I feel that I am either not passing the property of the array correctly or my function code is not set correctly.
^^^^KegDetail.js^^^^
import React from "react";
import PropTypes from "prop-types";
function KegDetail(props){
const { keg, onClickingDelete} = props
return (
<React.Fragment>
<hr/>
<h2>{keg.name} Made By {keg.brewery}</h2>
<p>abv {keg.abv}</p>
<h3>price {keg.price}</h3>
<p>{keg.pintsLeft} total pints left</p> {/* Make this a percentage */}
<hr/>
<button onClick={ props.onClickingEdit }>Update Keg</button>
<button onClick={()=> onClickingDelete(keg.id) }>Delete Keg</button>
<button onClick={()=> this.onSellingPint()}>Sell A Pint!</button>
</React.Fragment>
);
}
KegDetail.propTypes = {
keg: PropTypes.object,
onClickingDelete: PropTypes.func,
onClickingEdit:PropTypes.func,
onSellingPint:PropTypes.func
}
export default KegDetail;
That was my KegDetail.js
import React, {useState} from "react";
import NewKegForm from "./NewKegForm";
import DraftList from "./DraftList";
import KegDetail from "./KegDetail";
import EditKegForm from "./EditKegForm";
class DraftControl extends React.Component {
constructor(props){
super(props);
this.state = {
kegFormVisibleOnPage: false,
fullDraftList: [],
selectedKeg: null,
editing: false,
pints: 127,
};
this.handleClick = this.handleClick.bind(this);
this.handleSellingPint = this.handleSellingPint.bind(this);
}
handleClick = () => {
if (this.state.selectedKeg != null){
this.setState({
kegFormVisibleOnPage: false,
selectedKeg: null,
editing: false
});
} else {
this.setState(prevState => ({
kegFormVisibleOnPage: !prevState.kegFormVisibleOnPage,
}));
}
}
handleSellingPint = () => {
this.setState({
pints:this.state.pints-1
})
};
render() {
let currentlyVisibleState = null;
let buttonText = null;
if (this.state.editing){
currentlyVisibleState = <EditKegForm keg = {this.state.selectedKeg} onEditKeg = {this.handleEditingKegInDraftList} />
buttonText = "Return to the Draft List"
}
else if (this.state.selectedKeg != null){
currentlyVisibleState = <KegDetail keg = {this.state.selectedKeg} onClickingDelete = {this.handleDeletingKeg}
onClickingEdit = {this.handleEditClick} onSellingPint = {this.handleSellingPint}/>
buttonText = "Return to the Keg List"
My DraftControl.js code
I don't know what I am doing wrong. I cant get the keg.pintsLeft to pass a number when I console.log, So I may be targeting it incorrectly.
Thanks again!
Try it like this:
handleSellingPint = () => {
this.setState(prevState => {
return {
pints: prevState.pints-1
}
})
};
edit
Also, you invoke the onSellingPint() in a wrong way.
It's not a class component, so React doesn't know what does this refer to.
The function itself is passed in as a prop, so you should reference it like this: <button onClick={() => props.onSellingPint() />
handleSellingPint = (id) => {
const clonedArray = [...this.state.fullDraftList]
for (let i = 0; i < this.state.fullDraftList.length; i++){
if (clonedArray[i].id === id){
clonedArray[i].pintsLeft -= 1
}
}
this.setState({
fullDraftList: clone
});
}
Is what I came up with.
Since you are alteriting a state within an array, you need to clone the array and work on that array, not the "real" one.
Thanks for all your help!
How to trigger the clicked method on app2 from ComponentA in this example.
const app = Vue.createApp({});
app.component('ComponentA', {
template: `<button #click="clicked" class='btn'>Click</button>`
});
const app2 = Vue.createApp({
methods: {
clicked() {
this.clickCount += 1;
}
},
render() {
return Vue.h(app.component('ComponentA'), options);
}
}).mount("#App");
From the button click event handler try to emit an event called clicked, in render function define it by prefixing it by on and upper casing the first letter like onClicked: (e) => {...} inside the body of this function run this.clicked
let options = {}
options.baseUrl = "someurl.com";
const app = Vue.createApp({})
app.component('ComponentA', {
template: `
<button #click="$emit('clicked')" class='btn'>Click</button>
`
});
const app2 = Vue.createApp({
methods: {
clicked() {
console.log("clicked !!!")
this.clickCount += 1;
}
},
render() {
return Vue.h(app.component('ComponentA'), {
onClicked: (e) => {
this.clicked()
}
}, options)
}
}).mount("#app");
<script src="https://unpkg.com/vue#3.0.0-rc.11/dist/vue.global.prod.js"></script>
<div id="app" someVariable='some value'>
</div>
I am trying to get the index of a p tag so that when the user clicks on it the p tag will either change color or text size(maybe both). I am using reactjs, but I am trying to do it in jquery. I gave every p tag a data and name attribute. Here is my code. How do I make it so only the clicked element gets a css change so the user knows it is selected. The function running the p tags is in the HTML showChapters function.
$(onReady)
function onReady() {
$("#voteSearchInput").submit(function (e) {
e.preventDefault();
return false;
});//end of voteSearchInput
$('#root').on('click', '.bookChapters' ,() => {
console.log('clicked')
//I want to change the color here
});
}//end of onReady
import React from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {Link} from 'react-router-dom';
import Header from './Header.jsx';
import axios from 'axios';
class Book extends React.Component {
constructor(props) {
super(props);
this.state = {
book:Object,
user:Object,
chapters:[],
chapterCount:0,
ready:false,
};//end of state
}//end of constructor
start() {
console.log(this.state.book)
}
showChapters() {
let chapterCount = this.state.chapterCount;
let chapters = this.state.book.length;
let chatpersArr = this.state.chapters;
for(let i = 0; i < chapters; i++) {
chatpersArr.push([])
}
return (
chatpersArr.map((ch, id) => {
chapterCount++;
return (
<p key={id} className='bookChapters' data-index={id}>{this.state.book.book} Chapter:{chapterCount}</p>
)
})
)
}//end start
componentWillMount() {
//Always gettting the last book out of the array
//AUTHENTICATES THE USER
axios.get('/login').then(res => {
let userInfo = res.data;
if(userInfo === "Not Authenticated") {
window.location = "/?#/";
}
});//end of axios GET
//GETS THE CORRECT BOOK FROM THE REDUCER
if(this.props.book.editBookReducer.length - 1 === -1) {
window.location = "/?#/user";
}
else {
let lastBook = this.props.book.editBookReducer.length -1;
let book = this.props.book.editBookReducer[lastBook].data.book;
let user = this.props.book.editBookReducer[lastBook].data.user;
this.setState({book, user, ready:true});
}
}//end componentWillMount
render() {
if(!this.state.ready){
return false;
}
else {
return (
<div>
<Header/>
<button onClick={this.start.bind(this)} >start</button>
<div className="container">
<div className="row" style={{"textAlign": "center"}}>
<div className="col-md-12">
<h1>{this.state.book.book}</h1>
</div>
</div>
<div className="row" style={{"textAlign": "center"}}>
<div className="col-md-6">
{this.showChapters()}
</div>
<div className="col-md-6">
<textarea name="" id="" cols="50" rows="60"></textarea>
</div>
</div>
</div>
</div>
);
}
}
}//end class Book
function mapDispatchToProps(dispatch) {
return bindActionCreators({
},
dispatch)
}
function mapStateToProps(state) {
return {
book:state
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Book);
React has event handling system built in that you can leverage. For more info see the docs.
Here I assign an onClick to the p element and use the data-index that you defined.
class Book extends React.Component {
handleChapterClicked = (event) => {
// you can get the index like this
let pIndex = event.target.getAttribute("data-index");
// your logic for the handler here
}
showChapters() {
let chapterCount = this.state.chapterCount;
let chapters = this.state.book.length;
let chatpersArr = this.state.chapters;
for(let i = 0; i < chapters; i++) {
chatpersArr.push([])
}
return (
chatpersArr.map((ch, id) => {
chapterCount++;
return (
<p key={id} className='bookChapters' onClick={this.handleChapterClicked} data-index={id}>{this.state.book.book} Chapter:{chapterCount}</p>
)
})
)
}//end start
}
You can get rid of the data-index attribute and bind the index to be the first argument that is passed to handleChapterClicked:
<p key={id} className='bookChapters' onClick={this.handleChapterClicked.bind(this, id)} data-index={id}>{this.state.book.book} Chapter:{chapterCount}</p>
And then the definition of the function would be changed to be:
handleChapterClicked = (index, event) => {
// your logic for the handler here
}