How to find which parent component set input in children at Angular 14? - input

I got 2 different parent components, lets say Parent1 and Parent2.
Have a child named Child1 and this child component gets an input named Input1.
In this Input's setter function, how should I determine which parent set this input in template?
--Parent1 component template:
<Parent1>
<Child1 [Input1]="Inputvalue">
</Parent1>
--Parent2 component template:
<Parent2>
<Child1 [Input1]="Inputvalue">
</Parent2>
Input setter in Child1 component
#Input() public set Input1(value: string) {
// I want to know who just set this input. Parent1 or Parent2?
}
Here is a visualized explanation from another question on Stackoverflow:
I tried to set additional parent information in template but this is a big project and nearly 200 component is using this Child. Too much work to do.
Possible clear and easy methods appreciated.

Related

Understanding when to use : , # and without any in vue

I have been going through an already existing code and trying to understand different parameters passed inside a tag.
<some-element
placeholder ="show first name"
:someElement = "true"
#error = "showErrorAlert"
>
so what exactly is the difference between these three parameters passed and when to pass them.
I am very new to vue so I am struggling a bit.
If any one needs any further information please let me know.
to communicate data between components in vuejs we have two concept: props and events.
vue props
vue custom events
read the two links above to learn about them from vue documentation but in short say we have a component structure like this:
<parent-component>
---- | <child-component>
data flow from parent to child is done with the help of props. that is when you're defining child-component you can set in its vue instance object that this component can accept some prop like this.
props: {
text: { type: String },
value: { type: Boolean },
}
then when you use this component in the parent one you can pass the defined props to the component like this:
<child-component text="some text" :value="false" />
notice the : notation is just a shorthand for v-bind:value="false", if you don't use this binding the false will be sent to the child component as a string. for any other value type you should use binding.
please read the docs to learn more about binding in vue
attribute binding
v-bind shorthand
now, the data flow from child to parent is done via events so in your child component you can emit an event for the parent one to listen to like this:
this.$emit('event-name', payload)
then in the parent component where you used child component you can listen to this event like this
<child-component #event-name="doSomethingFun($event)" />
where doSomethingFun() is a method in parent component and we are passing the payload sent from child component to this method with $event
notice # notation is shorthand for v-on like v-on:event-name="doSomethingFun($event)"
: is shorthand for v-bind: and this is used for dynamic rendering for the attributes such as src, href etc.
# is shorthand for v-on:click which is event handler for function calls.
You can read more Event handling shorthand syntax

Passing objects to next sibling components

My structure in AureliaJS of components is:
<parent>
<child1>
<child2>
</parent>
I have an object in child1 which I get with ajax requests:
export class Child1 {
fechedObject = {}:
}
I need this property with two-way binding and observable in the second component
export class Child2 {
// I need this fechedObject here
}
What is the best approach to get it?
I believe the best approach here is using two-way binding on both child models, to bind the model via two-way binding in the parent.
In your parent.html, you would need this:
<child1 fetched-object.two-way="fetchedObject"></child1>
<child2 fetched-object.two-way="fetchedObject"></child2>
And in both child view-models, you'd declare the variable as a bindable:
bindable()
public fechedObject;
This way any edits that happen in either children, will get passed on to the other child. If you want to prevent edits in child2 from affecting the object in child1, you can simply bind one-way using fechedObject.one-way or fechedObject.bind on your child2.
You can get a hold of <child1/> view model reference and bind it to <child2/>:
<child1 view-model.ref='child1'></child1>
<child2 data.bind='child1.fetchedObject'></child2>
So child.data just needs to be bindable:
export class Child2 {
#bindable
data
}

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);
}

vue: passing props down to all descendants

I have a parent component with the following line
<router-view :product-id="productId" :data-source="attributes"></router-view>
depending on the context it renders one of the two components defined in the router config
path: 'parent',
component: Parent,
children:
[
{
path: 'edit',
component: Edit,
children:
[
{
path: 'attribute/:id',
component: Attribute,
}
]
},
{
path: 'grid',
component: Grid,
}
]
The thing is that the product-id and data-source props are available only in the Edit and Grid components. I'd like to have them available in the Attribute component as well as the Edit component is just a background with some static text (common for many components).
As a workaround I've created a propertyBag prop in the Edit component that passes an object down. That's the way I use it in the parent component
<router-view :property-bag="{ productId:productId, dataSource:dataSource, ...
and the Edit component
<router-view :property-bag="propertyBag"></router-view>
Is there a simpler way to achieve it ?
Vue $attrs is the new way to propagate props
From the Docs:
vm.$attrs
Contains parent-scope attribute bindings (except for class and style) that are not recognized (and extracted) as props. When a component doesn’t have any declared props, this essentially contains all parent-scope bindings (except for class and style), and can be passed down to an inner component via v-bind="$attrs" - useful when creating higher-order components.
For more information, see Vue.js API Reference - $attrs
Have you looked at vuex. It's really quite easy to use and allows you to store data for your entire app in a single data store. This means you don't have to keep passing data through props, you can just access variables set in the store.
Here is a link to vuex docs (What is Vuex)
https://vuex.vuejs.org
You have to declare the props and bind them to pass them to the child.
Have a look at https://v2.vuejs.org/v2/api/#v-bind for available options
specifically, this may be of interest
<!-- binding an object of attributes -->
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
<!-- but you can also... -->
<div v-bind="allProps"></div>
This means you can pass down the object, and have the child parse the appropriate props. This means that the child has to have the props defined in order to catch them. So what you may be able to do is, in the case of the parent, have :propBag="propBag" and inside edit, pass down v-bind="propBag", and that will use the correct props at the child level
for vue > 2.0
v-bind="$attrs" it's sufficient, or you can declare them at data(), with [this.$attrs]
Solution possible from Vue 2.2.0
provide / inject
This pair of options are used together to allow an ancestor component to serve as a dependency injector for all its descendants, regardless of how deep the component hierarchy is, as long as they are in the same parent chain.
https://fr.vuejs.org/v2/api/index.html#provide-inject
How to pass multiple Props Downstream to Components
Use case: Props that you only need on the n-th child components, you can simply forward downstream without having to define the same props all over again in each component.
Correction: v-bind="$attrs" works just fine. Just make sure the parent also uses v-bind="$attrs" and not v-bind="$attr" ('s' was missing) which was the error that made me think v-bind="{ ...$attrs }" was needed.
However, I think you should still be able to use v-bind="{ ...$attrs }" to access all previous attributes, even if parents didn't explicitly propagated them.
How to:
Based on Alexander Kim's comment, it must be v-bind="{ ...$attrs }".
... is needed to pass the attributes of the previous component (parent) as $attrs only passes the attributes of the current component.
v-bind="{ ...$attrs }"
You must pass all data via props to children components. You don't have to pass it as an object but Vue.js does require all data to be passed to children. From their documentation:
Every component instance has its own isolated scope. This means you cannot (and should not) directly reference parent data in a child component’s template. Data can be passed down to child components using props.
So you are doing it in the correct manner. You don't have to create an object, you are able to pass as many props as you would like but you do have to pass the from each parent to each child even if the parent is a child of the original "parent".

Modifying a component's state from another component

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.