Pass action to a component with the parent's context in Aurelia - javascript

How can you pass an action from a parent view/component to a child component and still maintain the context of the parent. The problem shown in the below plunkr shows that if the action is executed in the component it is in the context of the component and not the parent who passed the action. Basically the typical "that = this" issue.
Yes you can use eventAggregator to do this, but how would you without it. Would you have to pass the whole viewModel into the component?
http://plnkr.co/edit/n1xPUdR5nhXwwIivawBj?p=preview
// app.js
import { inject } from 'aurelia-framework';
import { MyService } from './my-service';
#inject(MyService)
export class App {
constructor(myService) {
this.myService = myService;
this.message = "Hello World!";
}
doThing() {
console.log('do a thing');
this.myService.foo();
}
}
<!--app.html-->
<template>
<require from="./my-component"></require>
<p>${message}</p>
<button click.delegate="doThing()">Do the thing - in app.html</button>
<my-component do.bind="doThing"></my-component>
</template>
// my-component.js
import { bindable } from 'aurelia-framework';
#bindable('do')
export class MyComponentCustomElement {
}
<!-- my-component.html -->
<template>
<button click.delegate="do()">Do the thing - in my-component</button>
</template>
// my-service.js
export class MyService {
foo() {
alert("pity da foo");
}
}

If you REALLY wanted to do this (there may be cleaner ways to go about it), you would need to get access to your parent's view-model from your child view-model and then, when calling the method in your child view's click binding, use .call() to change the scope/context of the do() method when it's called.
So in your child view-model, first gain access to your parent's view-model by adding the following bind method:
bind( bindingContext ) {
// bindingContext is your parent view-model
this.parent = bindingContext;
}
After you have access to your parent view-model you can update the click binding in your child view to be as follows:
<button click.delegate="do.call( parent )">Do the thing - in my-component</button>
This will call the do() method from the parent view-model's context.
You can use either .call( scope/context, list, of, args, ... ) or .apply( scope/context, [array of args]). For more info on the .call() method check out Mozilla's explanation

Related

calling grandparent method from inside the grandchild component

i have 3 nested components
<GrandParent />
<Parent />
<Child />
i have a button in my Child component and on double click i would like to call a function in GrandParent component
<button #dblclick="this.$parent.callGrandParentFunction(model)"> call grandparent </button>
using this.$parent i can only access Parent methods ... is there any way to go one level higher and call a GrandParent method ?
there is a similar question on SO but it's about vue2
VueJS Grandchild component call function in Great grandparent component
Try to use provide/inject pattern :
GrandParent :
export default {
methods: {
someMethod(){
//
}
},
provide() {
// use function syntax so that we can access `this`
return {
someMethod: this.someMethod
}
}
}
in GrandChild component :
export default {
inject: ['someMethod'],
created() {
//run the method
this.someMethod()
}
}

How to call parent method inside a javascript child class

Following is the code for a vue component js file.
(assuming that the vue component js file is a Class)
export default { -----> this is the parent, it is a component & it doesn't have a name!
name: "mapping",
components: {},
props: ["data"],
data() {
},
methods: {
parentMethod() {} ---->> this is the parent method. I want to call this inside the Rect class
},
mounted() {
class Rect { -----> this is the child class,
constructor() {
this.parentMethod() // -> this is the parent method. how can I do this?
}
// call parent methods (i.e. component's) inside this class
//or something like this.
this.parentMethod() // -> this is the parent method. how can I do this?
}
}
As you can see I'm creating a class called Rect inside the mounted hook inside the vue js component class.
What I want is to call methods of the parent component inside this Rect class.
How can I achieve that?
UPDATE
I'm not extending the parent component class inside itself. I'm just defining a new class called Rect inside the parent component class.
So I don't think I can call super().
Not sure though!!
UPDATE
As I go through answers, I came to see that most of them suggests extending the class. But here the parent class doesn't have a name. It's just export default {} in vue.
& also I'm not sure whether I will be able to extend a parent inside itself to create a new class within inside itself.
NOTE
The requirement is to call the parent method from/inside a class which is the child of parent class (ie defined within the parent => defined within the parent body) Hope that makes sense!!
When you create a new class you change who this is inside that class. So you need to give that class a reference to its parent:
mounted() {
const parentRef = this; // reference to the parent instance
class Rect {
constructor() {
parentRef.parentMethod()
}
...
parentRef.parentMethod() // -> this is the parent method
}
}
Check extends and super keywords:
//Inheritance
//Parent class (superclass)
class Animal {
constructor(name) {
this._name = name;
this._behavior = 0;
}
get name() {
return this._name;
}
get behavior() {
return this._behavior;
}
incrementBehavior() {
this._behavior++;
}
}
//Child class (subclass)
class Cat extends Animal { // The extends keyword makes the methods of the animal class available inside the cat class.
constructor(name, usesLitter) {
super(name); // The super keyword calls the constructor of the parent class.
this._usesLitter = usesLitter;
}
get usesLitter() {
return this._usesLitter;
}
}
const bryceCat = new Cat('Bryce', true);
console.log(bryceCat.name); //Output: Bryce
console.log(bryceCat.usesLitter); //Output: true
In Vue you want to emit events from child components that are handled by the parent component rather than calling parent component methods directly from the child.
See https://v2.vuejs.org/v2/guide/components.html#Listening-to-Child-Components-Events

How to replace this.$parent.$emit in Vue 3?

I have migrated my application to Vue 3.
Now my linter shows a deprecation error, documented here: https://eslint.vuejs.org/rules/no-deprecated-events-api.html.
The documentation shows how to replace this.$emit with the mitt library, but it doesn't show how to replace this.$parent.$emit.
In your child component:
setup(props, { emit }) {
...
emit('yourEvent', yourDataIfYouHaveAny);
}
Your parent component:
<your-child #yourEvent="onYourEvent" />
...
onYourEvent(yourDataIfYouHaveAny) {
...
}
With script setup syntax, you can do:
<script setup>
const emit = defineEmits(['close', 'test'])
const handleClose = () => {
emit('close')
emit('test', { anything: 'yes' })
}
</script>
No need to import anything from 'vue'. defineEmits is included.
Read more here: https://learnvue.co/2020/01/4-vue3-composition-api-tips-you-should-know/
Due to the composition api, it allows you to use the $attrs inherited in each component to now fulfill this need.
I assume that you are using this.$parent.emit because you know the the child will always be part of the same parent. How do I simulate the above behavior with $attrs?
Lets say I have a table containing row components. However I wish to respond to row clicks in table's parent.
Table Definition
<template>
<row v-bind="$attrs" ></row>
</template>
Row Definition
<template name="row" :item="row" #click=onClick(row)>
Your Row
</template>
export default {
emits: {
row_clicked: () =>{
return true
}
},
onClick(rowData){
this.$emit('row_clicked',rowData)
}
}
Finally, a component containing your table definition, where you have a method to handle the click.
<table
#row_clicked=clicked()
>
</table
Your table component should effectively apply #row_clicked to the row component thus triggering when row emits the event.
There is similar way of doing it by using the context argument that is passed in second argument inside the child component (the one that will emit the event)
setup(props, context){
context.emit('myEventName')
}
...then emit it by calling the context.emit method within the setup method.
In your parent component you can listen to it using the handler like so:
<MyParentComponent #myEventName="handleMyEventName" />
Of course, in the setup method of the MyParentComponent component you can declare the handler like this
//within <script> tag of MyParentComponent
setup(props){
const handleMyEventName() => {
...
}
return { handleMyEventName }
}

Get calling component in React

How do you get access to the calling component in react?
function MyCallingComponent() {
return <MyReadingComponent/>
}
function MyReadingComponent() {
console.log(callingComponent.state)
}
You don't. The parent component can pass data and functions to the child via props, though. So for instance, if the parent needed a callback from the child in some circumstance:
function MyCallingComponent() {
function callback() {
// ...
}
return <MyReadingComponent callback={callback}/>;
}
function MyReadingComponent({callback}) {
// ...use `callback` where appropriate...
}
react uses unidirectional data flow, so if you need to access parent's state in your child component you need to pass it down as a prop.
class ParentComponent ...{
state={ foo : 'A' }
render(){
return <ChildComponent foo={this.state.foo}/>
}
}
as #t-j-crowder has said if you need to pass data from child to parent you may use callback method.

How do I reference a Svelte component's parent component?

Per the Svelte documentation on Props I am using props to pass a reference to the parent component to a child.
Props, short for 'properties', are the means by which you pass data down from a parent to a child component
That's exactly what I want to do. Here is a Svelte REPL with my code, that is also copied below:
My parent is App.html:
<div class='widget-container'>
<Widget foo bar="static" {baz}/>
</div>
<script>
import Widget from './Widget.html';
export default {
data: function(){
return {
baz: 'click me and check the console'
}
},
components: {
Widget
}
};
</script>
The child component is Widget.html:
<p>foo: {foo}</p>
<p>bar: {bar}</p>
<p>baz: {baz}</p>
<script>
export default {
oncreate: function(){
window.document.body.addEventListener('click', function(event){
console.log(`Clicked!, ${baz}`)
});
}
}
</script>
Thanks to the props, the HTML <p> elements can clearly reference the parent. However how can I reference the values in the parent component in the child component's JavaScript?
Inside lifecycle hooks and methods, access state with this.get():
<p>foo: {foo}</p>
<p>bar: {bar}</p>
<p>baz: {baz}</p>
<script>
export default {
oncreate: function(){
window.document.body.addEventListener('click', () => {
const { baz } = this.get();
console.log(`Clicked!, ${baz}`)
});
}
}l
</script>

Categories

Resources