I have a class component that I use in another parent component. I'm trying to call a method in the child component from the parent component. I'm getting a typeerror saying that the method I am calling is not a function.
Here's the simplified version of my code.
Child Component -
<template>
// HTML Template
</template>
<script>
import { Vue, Component, Ref } from "nuxt-property-decorator";
#Component
export default class ChildComponent extends Vue {
doSomething(){
//do something here
}
}
</script>
Parent Component -
<template>
// HTML template
<child-component refs="child" />
</template>
<script>
import { Vue, Component, Ref } from "nuxt-property-decorator";
import ChildComponent from '/....'
#Component({
components: { ChildComponent }
})
export default class ParentComponent extends Vue {
#Ref("child") readonly child!: ChildComponent;
doAnotherThing(){
//do something here
this.child.doSomething() //this will return typerror that says it;s not a function
}
}
</script>
Related
I'm having trouble printing out the data from Twig template on my Symfony app. I am using Vue.js with TypeScript enabled.
main.ts:
import HeaderNav from './components/HeaderNav.vue'
new HeaderNav({el: '.headnav'});
HeaderNav.vue:
<template>
<div class="headnav headnav-fixed z-depth-1">
{{test}}
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { Component, Emit, Prop, Watch } from 'vue-property-decorator';
#Component({
props: {
test: {
type: String,
default: "{}"
}
},
mounted: function () {
console.log(this.test);
}
})
export default class HeaderNav extends Vue {}
</script>
header.html.twig:
<div class="headnav" data-test="hello"></div>
I get this error while compiling:
Property 'test' does not exist on type 'Vue'.
Any ideas that am I doing wrong? I am thinking that calling the component HeaderNav first might be the problem, because it instantly overrides the element with class 'headnav', but I am trying to create the components only for separate elements on my site, I don't want to put whole application into Vue.js.
I hope the problem is with you defined props.
In Vue with typescript you need to use props like this:
import { Component, Prop, Vue } from 'vue-property-decorator';
#Component({
name: "HeaderNav"
})
export default class HeaderNav extends Vue {
#Prop({ default: '' }) test!: string;
created(){
console.log(this.test)
}
}
I hope this will help you.
For more information checkout this article: https://blog.logrocket.com/how-to-write-a-vue-js-app-completely-in-typescript/
I have this code:
App.vue
<template>
<v-container>
<div>
<schedule-table
v-if = "schedule.length > 0"
:exercises="schedule[0].exercises"
/>
</div>
</v-container>
</template>
<script lang="ts">
import Vue from "vue";
import { Component } from "vue-property-decorator";
import ScheduleTable from '#/components/ScheduleTable.vue'
#Component({
components: {
ScheduleTable,
}
})
</script>
ScheduleTable.vue
<template>
<v-container>
<schedule-week
:exercises="exercises"
/>
</v-container>
</template>
<script lang="ts">
import Vue from "vue";
import { Component, Prop } from "vue-property-decorator";
import { IExercise } from "#/interfaces"
import ScheduleWeek from '#/components/ScheduleWeek.vue'
#Component({
name: 'ScheduleWeek',
components: {
ScheduleWeek,
}
})
#Component
export default class ScheduleTable extends Vue {
#Prop( {required: true, type: Array } ) readonly exercises!: IExercise;
}
</script>
ScheduleWeek.vue
<template>
<v-container
:ex="exercises"
>
here is tables (but this tables dosn't show and any other text dosn't show)
</v-container>
</template>
<script lang="ts">
import Vue from "vue";
import { Component, Prop } from "vue-property-decorator";
import { IExercise } from "#/interfaces"
#Component
export default class ScheduleWeek extends Vue {
#Prop( {required: true, type: Array } ) readonly exercises!: IExercise;
}
</script>
And have vue warn:
Unknown custom element: < schedule-week > - did you register the
component correctly? For recursive components, make sure to provide
the "name" option.
How to fix this problem? How to register component correctly?
There are multiple ways to declare a vue-component when you use typescript. The class-based SFC approach (the one you are using) needs to follow a slightly different syntax. You used the typescript decorator twice in your schedule-week component
App.vue
<v-container>
<div>
<schedule-table
v-if = "schedule.length > 0"
:exercises="schedule[0].exercises"
/>
</div>
</v-container>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator"; // you can import vue here
import ScheduleTable from '#/components/ScheduleTable.vue'
#Component({
components: {
ScheduleTable,
}
})
export default class App extends Vue {} // the #Component() decorator needs to be followed by the exported class
Correspondingly your other components:
ScheduleTable.vue
<template>
<v-container>
<schedule-week
:exercises="exercises"
/>
</v-container>
</template>
<script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator"; // see above
import { IExercise } from "#/interfaces"
import ScheduleWeek from '#/components/ScheduleWeek.vue'
#Component({
name: 'ScheduleTable',
components: {
ScheduleWeek,
}
}) // >>>don't use the decorator twice<<<
export default class ScheduleTable extends Vue {
#Prop( {required: true, type: Array } ) readonly exercises!: IExercise;
}
</script>
ScheduleWeek.vue
<template>
<v-container
:ex="exercises"
>
here is tables (but this tables dosn't show and any other text dosn't show)
</v-container>
</template>
<script lang="ts">
import { Component, Vue, Prop } from "vue-property-decorator"; // see above
import { IExercise } from "#/interfaces"
#Component
export default class ScheduleWeek extends Vue {
#Prop( {required: true, type: Array } ) readonly exercises!: IExercise;
}
</script>
##EDIT:
From the officiel TypeScript Documentation:
With the introduction of Classes in TypeScript and ES6, there now exist certain scenarios that require additional features to support annotating or modifying classes and class members. Decorators provide a way to add both annotations and a meta-programming syntax for class declarations and members.
Decorators are basically TypeScript(JavaScript) functions which are used to add additional information to the following class (your component) or class members. This also means a decorator cannot stand alone. The #Component() decorator takes an Object as parameter which is 'as-is' translated into component-options.
TL;DR
To register the components SubOne and SubTwo in MyComponent, pass it to the #Component decorator:
import { Component, Vue } from "vue-property-decorator";
#Component({
components: {
SubOne,
SubTwo,
}
})
export default class MyComponent extends Vue {
…
}
And make sure you also decorate the SubComponents like so;
import { Component, Vue } from "vue-property-decorator";
#Component
export default class SubOne extends Vue {
…
}
Error in #MarcRo's answer
Setup and resulting Error
In the Component ScheduleTable.vue, the decorator #Component gets the parameter name set to the Component it is about to import. This leads to a recursive call of said Component and hence an Error.
Fix
Set name: 'ScheduleTable' - or even drop it. The class name seems to be used (which is what we are doing).
Aside
I could not find a doc on what can actually passed to the decorator, but it's used like this in #MarcRo's answer in App.vue as well as here. (The link in the comments there seems to be old).
I don't have enought reputation to comment and a fixing edit got rejected, so sorry for the extra post.
Thank you #MarcRo for teaching me how to use this and the facepalm moment after solving the recursion error after a way too long time ;)
I'm new to React and would like to keep all my components in one file. How do I export more than one component and keep them in the same file?
import React, { Component } from "react";
class MyText extends Component {
render() {
return (<div>
<h1>
Component</h1>
<p>This class component is named MyText. I can re-use it's code by simply Rendering (fancy name for calling) it by it's class name and I won't have to re type it, however many times I will need it.</p>
</div>);
};
}
class GreetName extends Component {
render() {
return (
<div>
<h1>Hello, {this.props.name}</h1>;
</div>
);
};
}
export default MyText;
You can do as Jess Kenney said or use naming export at the bottom of your file:
SomeComponent.js
import React, { Component } from "react";
class MyText extends Component {
render() {
return (<div>
<h1>
Component</h1>
<p>This class component is named MyText. I can re-use it's code by simply Rendering (fancy name for calling) it by it's class name and I won't have to re type it, however many times I will need it.</p>
</div>);
};
}
class GreetName extends Component {
render() {
return (
<div>
<h1>Hello, {this.props.name}</h1>;
</div>
);
};
}
export { MyText, GreetName };
And then use it like:
import { MyText, GreetName } from './SomeComponent'
I advice you to use one component per file, so you can keep your project modular. In ReactJS it's a convention to export one component from a file, and to export it is as the default export.
If it's helper component which used only in the particular you can put it to the same file as functional component.
Instead of using the export default line at the bottom, you can put export before each class definition, like export class GreetName... then to include your classes in another file you can use import {MyText, GreetName} from 'your/file.js'
export default secretContext;
export {taskData};
for export two function component
do this in app.js
export default App;
export{New_component};
do this in index.js
import App from './App';
import {Hari} from './App';
<App name={people.name}/>
<New_component msg={people.msg}/>
make sure component name start with capital letter
i have defined a Parent component and a Child component. i'm getting an error when i associate them.
Parent.jsx
import React, {Component, PropTypes} from 'react';
import {Child} from '/imports/ui/components/Child';
export default class Parent extends Component {
constructor(props) {
super(props);
}
render() {
return (
<Child />
);
}
}
Child.jsx
import React, {Component, PropTypes} from 'react';
export default class Child extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>child</div>
);
}
}
i have registered the parent with Blaze:
Template.registerHelper("Parent", function() {
return Parent;
});
... and i'm using it like this:
<div>
{{> React component=Parent }}
</div>
i'm getting this error in the browser console:
Warning: React.createElement: type should not be null, undefined,
boolean, or number. It should be a string (for DOM elements) or a
ReactClass (for composite components). Check the render method of
Parent.
i do have other React components working in this project, but none of them have this simple parent/child relationship. what am i doing wrong?
You should
export Child instead of export default Child
or
import Child instead of import {Child}
I have a generic component. This component has N buttons, where N can be set by the parent. A parent component can have multiple instances of this generic component. When a button is clicked on the generic component, I need to notify the parent component that this button has been pressed, and some way to identify which button has been pressed. I then need to have that parent component be able to call some function on the nested component. Here's a really rough example of what i'm looking to do:
#Component({
selector: 'parent-component',
...
})
export class ParentComponent{
public OnGenericComponentButtonPress(someId){
if (someId === "foo"){
genericComponentInstance.closeComponent();
}else{
doOtherThing();
}
}
}
#Component({
selector: 'generic-component',
...
})
export class GenericComponent{
public closeComponent(){}
}
I need some way to communicate back and forth like this. Assuming a parent component can have multiple instances of GenericComponent, is this possible?
You need 2 communication forms:
Child to Parent
Parent to child
Child to Parent
Communicating from the child to the parent can be done by adding EventEmmiter on the child and subscribing to the event on the parent.
Calling the parent is done by calling emit on the EventEmitter
Child component code:
import { Component, EventEmitter, Output } from '#angular/core';
#Component({
selector: 'my-child',
template: '<h2>{{myText}}</h2><button (click)="onClick()">do something</button>'
})
export class ChildComponent {
private myText = "Child Component";
#Output() clicked: EventEmitter<any> = new EventEmitter();
public onClick() {
this.clicked.emit();
}
}
Parent component code:
import { Component} from '#angular/core';
import { ChildComponent } from "./child.component";
#Component({
selector: 'my-app',
template: `<h1>Parent component</h1>
<my-child (clicked)="doSomething()"></my-child>`,
directives: [ChildComponent]
})
export class AppComponent {
public doSomething() {
do something...
}
}
Parent to Child
To call a child component we first need to get a reference to the component, this is done by using ViewChildren if we have multiple components of the same type.
Parent component code:
import { Component, ViewChildren, QueryList} from '#angular/core';
import { ChildComponent } from "./child.component";
#Component({
selector: 'my-app',
template: `<h1>Parent component</h1>
<my-child></my-child>`,
directives: [ChildComponent]
})
export class AppComponent {
#ViewChildren(ChildComponent) children:QueryList<ChildComponent>;
public callChild(){
this.children.toArray()[0].doSomething();
}
}
Example in this plnkr
Note: There are other ways to communicate between components such as using a common service.