I wanted to create a component that exposes a function in the parent.
While the Svelte docs point to context="module", that script gets called only once, which would break functionality if I have several instances of the same component.
I found several examples on the internet and they all point to a very handy workaround, defining the export in the component and calling it with dot notation:
// Child.svelte
<script>
let element
export function changeColor() {
console.log(element)
element.style.backgroundColor = "#" + Math.floor(Math.random() * 16777215).toString(16)
}
</script>
<button bind:this={element}>A button</button>
I created a very simple use case, which works when I bind the component to a variable and call it with child.function, but what if I want to call the function from a click on the component itself?
// App.svelte
<script>
import Child from "./Child.svelte"
let child
</script>
<button on:click={() => child.changeColor()}>Click</button> // This works
<Child bind:this={child} on:click={() => child.changeColor()} /> // This doesn't work
I understand the logic behind the first option, however I don't understand why the second option doesn't work!
Should it be the same? I binded the component to a variable, and I am calling it with the same syntax. How can i make it work, without using a second element that calls the function?
<Child bind:this={child} on:click={() => child.changeColor()} />
doesn't work because on:click is not defined, you can easily see this when changing it to on:click={() => console.log('test')}
The reason it doesn't work is because in Svelte events do not 'bubble' out of components, if you want to do that you have to explicitly indicate this in Child.svelte
<button bind:this={element} on:click>A button</button>
Related
I'm using Vue 3 and in a v-for loop, I'm creating multiple button elements. The buttons are being made in another component named wb-button. So I call wb-button in every v-for loop.
I add #click event listener to the wb-button that calls a method inside the current component, simple:
<div v-for="(item,key) in items" :key="key">
<span>{{item.name}}</span>
<wb-button #click="deleteItem(item)">
Delete item!
</wb-button>
</div>
This works how I want, the problem starts when I want to pass the wb-button just like a ref to the deleteItem method. The purpose is to make changes to another ref inside the wb-button. So what I basically want to do is this:
export default{
name: 'listComponent',
methods:{
async deleteItem(item,wbButtonRef){
// The next line is what I want to do
wbButtonRef.$refs.btnElement.putInLoadingStatus()
// do the delete item action
}
}
}
I know I can create a ref on each wb-button and pass an ID or something to the method, but I don't think it is the cleanest way to do it.
If there was something to just pass the wb-button element as the method parameter it would be great. Something like this:
<!-- I want to know if there is something I can put in
the second argument of the 'wb-button' #click event -->
<wb-button #click="deleteItem(item, /* what to put here?*/)">
<!-- "this","$this","$event.target" etc ... -->
I have tried $event.target but it returns the root element of wb-button, what I need is the wb-button itself just like a $ref.
Simply put, you can't. And since this logic is relevant only for the button component itself, it's best to keep this logic within it. Adding a prop and render something based on that, like you suggested yourself in the comments, is a good way to go about it.
Considering Other Options
Although I used #paddotk 's answer, 'the props way' to solve my problem, I'm just adding this answer so anyone who reads this question afterward would have a complete answer.
As far as I have found out, there are two more ways of doing this:
1- As #MrFabio_25 mentioned in the comments, I can create a custom event on the child component and $emit with 'this' as a parameter, so I can handle that in the parent:
// wbButton.vue file
<inside-wb-component ref="btnElement" #click="handleClick">
<button>
<slot></slot>
</button>
</inside-wb-component>
//and in the script tag
//...
methods:{
handleClick(){
this.$emit('buttonClick',this)
}
}
//...
And simply in the parent component:
<wb-button #buttonClick="handleButtonClick">
A text here
</wb-button>
// in the script tag
methods:{
handleButtonClick(elem){
elem.$refs.btnElement.putInLoadingStatus()
}
}
2- The second way to do so, without the child component being involved, is to use an array of refs, as explained here:
Vue 3 Documentation - refs inside v-for
<div v-for="(item,key) in items" :key="key">
<wb-button ref="wbButtonRefs" #click="handleButtonClick(item,key)">
A text here
</wb-button>
</div>
// and in scripts tag
//...
methods:{
handleButtonClick(item,index){
const buttonRef=this.$refs.wbButtonRefs[index]
// Now do whatever with buttonRef
buttonRef.$refs.btnElement.putInLoadingStatus()
}
}
//...
<template>
<button #click="test($event)">Test</button>
</template>
methods:{
test(e){
const comp = e.target.__vueParentComponent
const props = comp.props
}
}
I want to build a custom element using Svelte.
Thus in rollup.config.js I set customElement: true, and then I have to use the to refer to my child components.
But I found that in this way, the bind will not work. Here is the code example
HelloWorld.svelte (child)
<script>
import Hello from './components/Hello'
import World from './components/World'
export let value;
</script>
<svelte:options tag={'x-app-helloworld'}/>
<input type="text" bind:value={value} >
<input>
<x-app-hello />
<x-app-world />
App.svslte(parent) part of it.
<x-app-helloworld bind:value={value}/>
Then the parent will show an error: 'value' is not a valid binding on <x-app-helloworld> elements.
How could I solve this bind problem?
Bindings work on regular elements because Svelte knows which event corresponds to each binding — for example, it knows that the value of an <input> changes when the element fires a change or input event.
With custom elements, there's no way to know what event (if any) the parent should be listening for. And there isn't currently a neat way to dispatch events from inside the element. So the best option is to pass in a callback to the custom element, and call it whenever the value changes:
<x-app-helloworld onValueChange="{(x) => value = x}"/>
<script>
export let onValueChange;
export let value;
$: onValueChange(value);
</script>
I am trying to make a component in React where you could iterate +1 or -1 on click. Please look at jsfiddle and tell me where I am missing the point.
Many thanks for all possible help.
Looking forward,
class App extends React.Component {
constructor(props) {
super(props);
this.state = {clickCount: 0};
console.log(this.state)
}
handleClickInc(){
this.setState({count:this.state.clickCount + 1})
}
handleClickDec(){
this.setState({count:this.state.clickCount - 1})
}
render(){
return
<div>
<div>
{this.props.clickCount}
</div>
<button onClick={this.handleClickInc}>{"+"}</button>
<button onClick={this.handleClickDec}>{"-"}</button>
</div>
}
}
ReactDOM.render(
<App/>,
document.getElementById('container')
);`
html part
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
Your problems were:
1) your return function was not wrapped in parens, causing a syntax error.
2) your click handlers were not bound to this (i.e. you needed to call this.handleClickInc.bind(this) or use fat arrow syntax as mentioned above.
3) As mentioned, you were updating the state of count but you meant to update clickCount.
Here is a working fiddle.
https://jsfiddle.net/6z3cuLys/5/
Looks like you could be missing .bind() on there. Without it, this has the wrong context as it fires the function.
try
<button onClick={this.handleClickInc.bind(this)}>{"+"}</button>
<button onClick={this.handleClickDec.bind(this)}>{"-"}</button>
or, fat arrow functions generally do this work for you
<button onClick={() => this.handleClickInc()}>{"+"}</button>
<button onClick={() => this.handleClickDec()}>{"-"}</button>
Well first of all all render elements must have return right? So your return is missing the () wrapping the div.
Second, to use state in a function you have to bind the function. I use to put the statement inside the constructor.
You can put like this:
this.handleClickInc = this.handleClickInc.bind(this);
Make this for the other function and it will work.
Okay, I've been working at this for 2 days now and I need some help. I previously made a loot logging program for a game I play, and now I want to make it into a Webapp. I'm using React JS to build the app.
I need to make a button component that includes an onClick function and can take parameters. My app renders buttons equal to the length of the loot table for the selected monster (so only the number of buttons needed are made) using a for loop. These buttons need to have an onClick method that takes a value parameter (the value is the index of an array if it matters. The array stores the loot table).
I thought I could just generate the button name by referencing {this.props.name} and the value it needs to pass by using {this.props.value}.
var Button = React.createClass({
render: function() {
return <button type="button" onClick={this.onClick.bind(this,
{this.props.id})}>{this.props.name}</button>
},
onClick: function(value) {
alert(value);
}
});
React.render((<Button name="test" id={1} />),
document.getElementById('example'));
For testing purposes, the button is just drawn in a div with class 'example'.
I've tried to find the examples of something like this working, but I can't find anyone who wants to dynamically create buttons that also pass parameters like I need to do for my webapp. Thank you for any assistance!
Look at the following code you wrote:
return <button type="button" onClick={this.onClick.bind(this,
{this.props.id})}>{this.props.name}</button>
There is an error in the onClick binding. it should look like this:
return <button type="button" onClick={this.onClick.bind(this,
this.props.id)}>{this.props.name}</button>
There are redundant brackets around this.props.id
You could just create a closure for each button's onClick while generating them:
const Button = ({ name, onClick }) => {
return (
<button onClick={onClick}>{ name }</button>
)
}
const List = () => {
return (
<div>
{ [0,1,2,3,4,5,6,7,8,9].map((value, index) => {
const onClick = () => { /* use index here */ }
return <Button onClick={onClick} name={value}/>
}) }
</div>
)
}
as an example. keep in mind I'm using ES6/JSX instead of React.createClass (for my sanity and because it's what I'm familiar with).
Besides, it doesn't make sense for the button to be responsible for handling the onClick logic - it should only be responsible for passing the event back to the relevant "subscribers"
How can I access my key property value inside my dumb component?
I have this dumb component:
const TagSummary = ({ tags, highlightTag }) => {
if (!tags) {
return <div />;
}
return (
<div>
{Object.keys(tags).map((tag) => {
return (
<div key={ tag }>
<button type="button" onClick={ highlightTag }>
<pre><{ tag }></pre>
</button>
<p>{ tags[tag] }</p>
</div>
);
})}
</div>
);
};
The method that I pass into it is this:
highlightTag(event) {
event.preventDefault();
console.log(event.target);
}
I want to be able to retrieve the key property in order to perform some other type of logic. How can I retrieve it onClick?
It isn't the best way to do it, instead you should have button be a seperate component where you can pass the onclick and the key as props and then in the button component merge the two together. The quick and dirty way is as follows
<button type="button" onClick={ highlightTag.bind(this, tag) }>
that will make sure that that argument is always provided to the highlightTag function.
The problem with this though is when React checks to see if anything has changed with the component it will always return true because of the function binding in the render method. If you aren't worried about performance you can leave it that way but that is the pitfall of using the binding in the render method
I'm not sure if you need to get the event back in the highlightTag function, but I would do
<button type="button" onClick={ev => {
ev.preventDefault();
highlightTag(tag);
}}>
This will make your function highlightTag more reusable (call this function programmatically, not from a user interaction for instance). It will also decouple implementation detail of TagSummary with its parent.