Modifying a component's state from another component - smalltalk

I'm trying to set the instance variable of a child component from it's parent component inside a callback. Using the debugger I can see that the instance variable is set correctly in the callback, but on rendering the child component, the child component does not reflect the changes made.
So, is it illegal to modify a component's state from another component in seaside or am I doing something else wrong?
Example code:
MyParentComponent>> initialize
super initialize.
child := MyChildComponent new.
MyParentComponent>> renderContentOn: html
html render: child.
html anchor
callback: [
child property: 'Something'.
] ; with 'Navigate'.
MyParentComponent>> children
^ Array with: child

You miss some super initialize in the parent component I guess.
I also suggest you don't work this way.
Do a MyParentComponent>>child with
^ child ifNil: [ child := MyChildComponent new ]
Also, don't do a html render: child but html render: self child.
That way you'll be able to swap components easily.
That way you are sure that child has been properly initialized.

After a little experimenting, I found the problem. In one of the rendering methods, I was creating a new component each time the page was rendered instead of reusing the one created in the initialize method.
That other component was used for navigation, where I set which main component was to be displayed based on the menu chosen.
So apparently, modifying state is not illegal in Seaside.

Related

Vue component updated (rerendered) without reason

I have a component which is being updated (rerendered) without reason when parent's data item is changed even though it has nothing to do with component in question.
This is a simple reproduction https://codesandbox.io/s/gracious-cannon-s6jgp?file=/public/index.html if anyone can shed some light on this. (clicking button will fire update event in component)
If I remove element whose class is changed by vue (or remove dynamic class) it works as expected.
Because at each render you define new objects for the something property:
<hello-world :something="[{prop: 'vvv'},{prop: 'rrr'}]"></hello-world>
By clicking the button, you update the global data. The root element is rerendered.
During render, a new array with new objects is created as assigned at the something property in your component. While the objects created at each render are equal, they are different (i.e. they map to a different memory point)
Your component finds that the property something changes its reference, so it re-renders.
If you create an items property in your data and you pass that reference as the prop, the component is not re-rendered again:
main.js:
data: {
active: false,
items: [{ prop: "vvv" }, { prop: "rrr" }]
},
index.html:
<hello-world :something="items"></hello-world>
Note that this behavior occurs because you are passing an array 8and it would be the same with an object. It would not happen with a constant variable of the primitive types (such as string, int, boolean, float), such as :something="'string'"

At what point in the component lifecycle does a prop get injected into a component?

I have a Parent component that is passing an array, let's call it 'bucket' that contains a bunch of stuff, to a Child component.
In my parent component, I have the following:
<child-component
:bucket=bucket
></childcomponent>
In my child component, I have a props section, that accepts a prop called bucket, and has it defined as an array, like so:
props: {
bucket: Array
}
Now in the mounted section of my child component, I want to take this bucket of stuff, and do something with it. However, for some reason, it shows up as empty. So when I do this in my child component...
mounted() {
console.log(this.bucket.length)
}
... I get a 0. When I check Vue dev tools, I can see that there are items in the bucket array in the Child component's props section. Furthermore, if in the console, with the child component set as $vm0, when I type $vm0.bucket.length, I get the correct size.
What on Earth!? Is the prop not injected yet when mounted is called in the Child component? If so, when does this actually happen? How do I get around this? Super confused.
Thanks!
try to add directive v-if
<child-component :bucket="bucket" v-if="bucket.length"></childcomponent>
if you can get this.bucket.length on component mounted this time, maybe #Stephen Thomas is right.
As you said in the comments, you are pushing new items to the bucket array.
Instead pushing items into the array, you should create a new array with the new items, example:
this.bucket = [...this.bucket, newItem];
Vue reactivity works when the value is changed, pushing a new item into an array is doesn't change the variable's value.
When you attribute an Object or Array to a variable, it's memory address that is given to the variable, so changing it's internal attributes or pushing new items won't change it's memory address.

How to send communication to parent component from child component?

I have split up the components only for the reason the base or the parent component code is growing in size and for code organization. so split the components based on the sections of the parent component.
So the each child component are the each section of the same parent component. But my question how to access the child component objects from parent component? Because most of the examples i see are based on the click event from parent component to view the child component (like dialog) and the value is passed back to the parent html click event and captures the values using emitter and output parameter.
Whereas in my case there is no such click action to trigger the child component. So how to communicate the values from child component to parent component?
In parent component
onDocumentSelect( documentData: IDocument) {
console.log(documentData);
}
<app-document [showDocument] = 'displayDocument' (selectDocument)=onDocumentSelect($event)></app-document>
In Child component:
#Output() selectDocument: EventEmitter<e.IDocument>;
#Input() showDocument: boolean;
I am triggering emit in the function where the data for the DocumentSource is computed.
this.selectDocument.emit(this.DocumentSource.data);
here this.DocumentSource is the dataTable which has value in Child component, that needs to be accessed in parent component.
Apart from this I have similar data for another field of different type needs to be passed to parent component from child component.
I get the below error while page loads
TypeError: Cannot read property 'subscribe' of undefined
In the above code snippet, by changing the type to any solved the issue.
On child component:
#Output() public selectDocument: EventEmitter<any> = new EventEmitter<any>();
On parent component:
onDocumentSelect( documentData: any) {
console.log(documentData);
}

Changing a property of an object in child component using VueJs

I have a simple question but I can't find the answer to it.
This code (that belongs to a child component? is a good practice?
export default {
name: 'Wish',
props: ['wish'],
data () {
return {
isEditing: false
}
},
methods: {
completeWish () {
this.wish.done = true
},
...
Or I should update the done property of wish object in the parent using emit?
Thanks
As Amade mentioned is a bad practice.
You should use events in order to accomplish it properly from a design point of view.
<script>
export default {
name: 'Wish',
props: ['wish'],
methods: {
completeWish () {
this.$emit('wish-is-completed')
}
}
}
</script>
And then in parent you have v-on:wish-is-completed="handleCompletedWish" like:
// Parent template
<template>
<wish v-on:wish-is-completed="handleCompletedWish"></wish>
</template>
EDIT:
I understand the answer was downvoted because you actually are able to mutate properties of props (not a direct prop reference) and don't get a warn when you do that.
Does it mean you should do that?
No.
Props are created for one-directional data flow purpose. When you mutate props to notify a parent of something, it quickly leads to having hard to maintain state changes, because they are not implicit. Use events for child->parent communication as official documentation suggest in such cases.
All props form a one-way-down binding between the child property and
the parent one: when the parent property updates, it will flow down to
the child, but not the other way around. This prevents child
components from accidentally mutating the parent’s state, which can
make your app’s data flow harder to understand.
Vue docs advise against mutating the props in the child component:
All props form a one-way-down binding between the child property and the parent one: when the parent property updates, it will flow down to the child, but not the other way around. This prevents child components from accidentally mutating the parent’s state, which can make your app’s data flow harder to understand.
In addition, every time the parent component is updated, all props in the child component will be refreshed with the latest value. This means you should not attempt to mutate a prop inside a child component. If you do, Vue will warn you in the console.
So based on this I would tend to say it's a bad practice.
documentation:
Due to the limitations of modern JavaScript (and the abandonment of Object.observe), Vue cannot detect property addition or deletion. Since Vue performs the getter/setter conversion process during instance initialization, a property must be present in the data object in order for Vue to convert it and make it reactive
if you want add or change property use Vue.set like this:
this.$set(this.wish, 'done', true)
see example App:
https://3xyx386q65.codesandbox.io/setProps
see example code (open components/setProps/childComponent.vue):
https://codesandbox.io/s/3xyx386q65
note! wish init as Object! See components/setProps/setProps.vue in example code

How to observer child vue finish render

I am working on a chat tool using vue single file components.I am facing a problem,root .vue has child .vue and grandchild .vue, I want to observer children's rendering,to get the root div's height,to change scrollbar position,
i used the $nextTick() http://rc.vuejs.org/api/#vm-nextTick,but it cant observer children's render,so ,is there any way can I try? thanks a lot.
You can use parent child communication of vue to get to know when you child has rendered. Vue provided following event interface:
Listen to an event using $on(eventName)
Trigger an event using
$emit(eventName)
A parent component can listen to the events emitted from a child component using v-on directly in the template where the child component is used.
So you can emit a event in your child component's lifecycle hook like following:
mounted () {
this.$emit('childRendered')
}
In parent component, you can listen to this event or create a method, like following:
this.$on('childRendered', function () {
// change scrollbar position
})
An example of parent child communication: fiddle
This is just for everyone to easily see the workaround that was mentioned by Saurabh in his comment to his answer. I also used the updated lifecycle to wait for the DOM to finish rendering and patching before manipulating it again.
Since updated does not guarantee that all child components have also been re-rendered, use vm.$nextTick inside of updated.
updated: function () {
this.$nextTick(function () {
// Code that will run only after the
// entire view has been re-rendered
})
}