I've got a component which can contain any number of child components, and I want these child components, which contain input fields, to set those input fields to "disabled" when the state in the parent is changed. I've been attempting to use React.cloneElement with React.Children.map to modify the properties of the child elements, but it doesn't seem to be affecting anything.
Here's a simplified version:
# parent component
# assume that there's a #setState call in here somewhere
DisableableItem = React.createClass
getInitialState: ->
disabled: false
render: ->
<div>
{
React.Children.map #props.children, (child) =>
React.cloneElement child, isDisabled: #state.disabled
}
</div>
# child component
ParameterizedOption = React.createClass
getInitialState: ->
disabled: #props.isDisabled
render: ->
<div className="parameterized-option">
<label>{#props.text}</label>
<div className="input-group input-group-sm">
<input type={#props.inputType or "text"} className="form-control"
placeholder={#props.initialInput or "00.00"}
disabled={#state.disabled}></input>
</div>
</div>
And rendering it like:
React.render <DisableableItem>
<ParameterizedOption text="hey" isDisabled=false />
<ParameterizedOption text="hey" isDisabled=false />
</DisableableItem>, document.body
So I'm console.logging the output, and the state in the parent is getting modified when I want it to, but the children's props are not getting modified as I thought the React.cloneElement call would modify them (they're always false as they were at start). I've taken a look at these questions, but I don't think they really apply, since DisableableItem doesn't know what its children are until they're given to it in the call to React.render, so it can't put disabled={#state.disabled} as one of the children's properties.
How would I reach in and modify these children's state? Am I misunderstanding what cloneElement is supposed to do?
You're copying the props to the state, which is some bad mojo. (https://facebook.github.io/react/tips/props-in-getInitialState-as-anti-pattern.html)
Just bind props.disabled directly to the element instead of copying it over to state.
Your current code:
getInitialState: ->
disabled: #props.isDisabled
This will only work if you ensure you update state.disabled in componentWillReceiveProps to reflect the new props.disabled passed in.
If you're deadset on doing it your weird way: https://jsfiddle.net/1t8ew85x/
Related
i have a menu with many items. on item click I create a new Vue element (with Vuex store). The question is:
on close witch is outside the vue instance do I have to call this.$destroy or v-if="false" the root element.
<template>
<div v-if="closeVar">
....
</div>
</template>
<script>
export default {
...,
data() {
return {
closeVar: true
};
},
methods: {
onWindowsClose() {
this.$destroy() OR this.closeVar = false; ????
}
},
created() {
window[id + 'onWindowsClose'] = this.onWindowsClose;
}
}
</script>
From the $destroy docs:
In normal use cases you shouldn’t have to call this method yourself. Prefer controlling the lifecycle of child components in a data-driven fashion using v-if and v-for.
So the answer from the docs is that it's preferred to use v-if. Furthermore, you should place that v-if on the component's tag in the parent, not within the component on the root element:
Parent
<child v-if="closeVar"></child>
Otherwise, you'd only be removing the child's content rather than the entire component.
The difference there is, for example, the child's created hook would not be called again if you repopulated that particular instance with content (which would be possible because the component wouldn't have been destroyed.)
I have parent and child component communication, I want to fire up a method in child component that should open up a modal and pass the data. When I call a function from parent using ViewChild, the variable in the child method component returns null, however should have data inside variable. When I fire same function within the child component, the data is available, but its not a case when called from parent component. What is missing and why I don't see data in child component, from fire up from the parent component?
parent.html
<button (click)="openModal()">New</button>
<child><child>
parent.ts
import { FilterComponent } from './../filter/filter.component';
#ViewChild(FilterComponent) filterComponent: FilterComponent;
openModal(){
this.filterComponent.openModal(); // when fired from here, the function in the child component fires, but the variable inside it returns null
}
child.html
<button (click)="openModal()">New</button>
<modal><modal>
child.ts
openModal(){
modalVisible = true;
console.log(this.filter) returns null; when fired from parent component
console.log(this.filter) returns "filter data"; when fired within the component
}
From what I can tell you are currently selecting ALL FilterComponents inside your parentComponent. In order to use this you need to be more specific. You can change your ViewChild to select only one FilterComponent if that fits your use case.
Example:
parent.html
<button (click)="openModal()">New</button>
<!-- notice the #child attribute -->
<child #child><child>
Then in parent.component.ts
import { FilterComponent } from './../filter/filter.component';
#ViewChild("child") filterComponent: FilterComponent;
openModal(){
this.filterComponent.openModal(); // now that you're only selecting one element this function call should have access to this values.
}
You can also reference the relevant stack blitz i made here: https://stackblitz.com/edit/angular-lqmc8x
Good luck!
I think you need an EventEmitter.
parent.html
<child (messageEvent)="openModal($event)"></child>
child.ts
import { Component, Output, EventEmitter } from #angular/core;
export class...
#Output() messageEvent = new EventEmitter<string>();
openModal(val) { this.messageEvent.emit(val); }
child.html
<button (click)="openModal(val)">Click</button>
or something like that.
I think you should rethink your architecture here.
If the method is triggered from the parent component only, then put into the parent component
If the the method is triggered from both parent AND child component, then put into a service
I have two components, one inside the other one. I have a click event on the parent component that should change the data value of the child component.
<template>
<div>
.....
.....
<my-component
:options="options">
</my-component>
</div>
.....
.....
</template>
<script>
...
...
data(){
}
methods:{
clickEvent(array_from_child){
this.array = array_from_child; //array is in my-component
}
}
components:{
....
}
</script>
I want to trigger the clickEvent method on child's element change. how to do that?
It seems like you're asking two different questions.
First, accessing a child's data from its parent:
If possible, you should pass the array to the child component using the child's props. Then simply change the array in the parent and any changes will be reflected in the child. If the array really needs to be in the child, then you can define a method to retrieve it.
<template>
<child-component ref="child">
</child-component>
</template>
methods: {
onClick() {
const myArray = this.$refs.child.getMyArray();
}
}
And then, in the child
methods: {
getMyArray() {
return this.myArray;
}
}
Second, triggering a change in the parent from the child
In this case, Flame's answer is most idiomatic. Emit a custom event in the child and listen for that event in the parent.
When you are going from a child to a parent, you should use events:
{
methods: {
clickEvent()
{
this.$emit('click', mydata);
}
}
So in your parent element, you can then attach your own callback to the emitted event like so:
<template>
<my-child-component #click="theParentMethod" />
</template>
You could also use some reactivity by passing an object reference from the parent to the child, so if you change the object in the child, the parent can detect the changes. However this comes with some reactivity gotcha's, see https://v2.vuejs.org/v2/guide/reactivity.html .
New to VueJS so hoping to get some advice on the best approach to take.
I have a parent containing multiple child components.
Once the child components have been created there is an event handler attached to each child
this.$parent.$on('save', this.save);
So that whenever I emit the following in the parent we call the child save method
this.$emit('save', block);
The problem that I'm seeing is that every child listen to events from this parent is reacting to this event.
Is there a better way to get a specific child to react to a parent click event?
Maybe you could try passing a prop with .sync modifier to a child, and then in the child component you should use computed property. This is an option but I don't know what are you trying to achieve, is there any specific reason that you must emit an event?
Read the vue.js dosumentation for props: https://v2.vuejs.org/v2/guide/components.html#Props
For what you are attempting, if possible, I'd use a Child Component ref and call a method on the child from the parent:
Vue.component('my-comp', {
template: "#my-comp-template",
props: ['name'],
methods: {
saveMyComp() {
console.log('Saved:', this.name);
}
}
})
new Vue({
el: '#app',
data: {
people: [{name: 'Bob'}, {name: 'Nelson'}, {name: 'Zed'}]
},
methods: {
saveChild(index) {
this.$refs.myComps[index].saveMyComp();
}
}
});
<script src="https://unpkg.com/vue#2.5.13/dist/vue.min.js"></script>
<div id="app">
<div v-for="(person, index) in people">
<button #click="saveChild(index)">saveMyComp</button>
<my-comp :name="person.name" ref="myComps"></my-comp>
</div>
</div>
<template id="my-comp-template">
<span> {{ name }} </span>
</template>
Remember the parent is already coupled to the child component -- since it knows it exists in the template declaration.
I have multiple parent components and within each parent component are nested children components. I'd like to have the class in the nested childrens' component change when a link in that parent component is clicked (a show/hide toggle type thing). I'm guessing this can be done by setting and changing state in the parent component on the click, but am not sure. Is this the correct way to handle this?
Is it usually best practice to hold state in the root component (I should note that the parent component explained above is not the root).
Any and all help is greatly appreciated. Thanks!
Generally, the higher up in your component hierarchy you can push state, the better. Then as the state changes in your parent/root component, new props will trickle down to child components. This makes your child components a lot simpler because they don't have to manage as much of their own state, if any.
In your case, you're exactly right. Handle the click event in your parent which will change your state, then render your child component with the correct className based on that state.
var Child = React.createClass({
render: function () {
return <div {...this.props}></div>;
}
});
var Parent = React.createClass({
handleLinkClick: function (e) {
this.setState({toggle: !this.state.toggle});
},
render: function () {
return <Child className={this.state.toggle ? 'yes' : 'no'} />;
}
});