I'm looking for accessing props from constructor in VueJS.
I tried the react way like you can see below but without any success at that time.
Parent :
<nb-add-card
test="test props">
</nb-add-card>
Child:
<template>
<div>
test : {{ test }}
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import Component from 'vue-class-component';
#Component({
components: { },
props: {
test: {
type: String,
default: "",
required: true
},
},
})
export default class AddCard extends Vue {
constructor(props: any) {
super(props);
console.log('TEST :', props); // => Undefined
console.log('TEST1 :', this); // => Refer to AddCard
}
}
</script>
The log "TEST" still "undefined".
Being stuck on this for hours now, on react we just simply pass "props" in constructor args, but in vuejs, it's seems not working... :/
I also test to access the props value by writing the props name of < nb-add-card > but it's not working either...
constructor(test: any) {
super(test);
console.log('TEST :', test); // => Undefined
console.log('TEST1 :', this); // => Refer to AddCard
}
Anyone have an idea ? Thank's
The constructor is of no use with vue-class-component. What you're exporting is a constructor itself. Your 'test' var is immediately accesable within' the component. Use the 'created' or 'mounted' hook to log it.
Related
In the vue docs, the part of "beforeCreate", I read the following:
Called immediately when the instance is initialized, after props resolution, before processing other options such as data() or computed.
Does this means that I can get props in beforeCreate hooks? if so, how can I get it?
In my child component, I try like this to get the message passed by parent component, but failed.
export default {
name: 'Child',
props: ['message'],
beforeCreate() {
console.log(this.message)
}
}
Please check the following snippet, looks like your code is fine:
const app = Vue.createApp({
data() {
return {
msg: 'aaa',
};
},
})
app.component('Child', {
template: `<div>{{ message }}</div>`,
props: ['message'],
beforeCreate() {
console.log('before create: ', this.message)
}
})
app.mount('#demo')
<script src="https://unpkg.com/vue#3.2.29/dist/vue.global.prod.js"></script>
<div id="demo">
<child :message="msg"></child>
</div>
I have a button component which calls an API, and I want to push the returned response up to the parent, where it will become the 'translatedText' prop, however, I believe I'm using the $emit incorrectly, due to the error: `Uncaught (in promise) TypeError: Cannot read properties of undefined (reading '$emit'). How do I best capture the response data and pass it to my parent prop, and is using $emit the best use in this instance?
TranslationButton.vue
<template>
<b-button type="is-primary" #click="loadTranslations()">Übersetzen</b-button>
</template>
<script>
export default {
name: "TranslationButton",
props: {
translatedText: ''
},
methods: {
loadTranslations() {
fetch('http://localhost:3000/ccenter/cc_apis')
.then(function(response) {
return response.text();
})
.then(function(data) {
console.log(data);
this.$emit('translatedText', this.data);
console.log(data)
})
},
},
};
</script>
Parent Component Props:
props: {
data: Array,
translatedText: '',
showAttachments: {
type: Boolean,
default: false,
}
},
How Child Component is called in Parent Component:
<translation-button #translatedText="loadTranslations()" />
Best practise when passing data from child to parent is emitting events.
this.$root.$emit('translatedText', this.data);
than
this.$root.$on('translatedText', () => { // do stuff })
by emits you pass value to parent component,
#translatedText="loadTranslations()" - its event listner, fireing on your child comp emit
do #translatedText="loadTranslations" instead of #translatedText="loadTranslations()"
and add this loadTranslations as a method to parent comp
BTW
if you dont use arrow funcs, and you use this.data it's pointing to object passed to .then, it will be undefined i guess...
The problem is with the usage of this. It does no longer point to your component inside the promise then() method.
You should create a new variable and initialize it with the value of this and use that variable to emit the event.
E.g.
loadTranslations() {
const _this = this;
fetch().then(response => _this.$emit(response));
}
if you want to pass data from child to parent, you need to use $emit like the below code
child:
<template>
<b-button type="is-primary" #click="loadTranslations">Übersetzen</b-button>
</template>
<script>
export default {
name: "TranslationButton",
props: {
TranslatedText: ''
},
methods: {
loadTranslations() {
const self= this; // change added
fetch('http://localhost:3000/ccenter/cc_apis')
.then(function(response) {
return response.text();
})
.then(function(data) {
console.log(data);
self.$emit('changeTitle', data) // change added
})
}
}
</script>
parent:
<template>
<translation-button #changeTitle="ChangeT" />
</template>
......
methods:{
ChangeT(title)
{
console.log(title)
},
}
vue is throwing this message:
Vue received a Component which was made a reactive object. This can
lead to unnecessary performance overhead, and should be avoided by
marking the component with markRaw or using shallowRef instead of
ref.
<template>
<component v-for="(el, idx) in elements" :key="idx" :data="el" :is="el.component" />
</template>
setup() {
const { getters } = useStore()
const elements = ref([])
onMounted(() => {
fetchData().then((response) => {
elements.value = parseData(response)
})
})
return { parseData }
}
is there a better way to do this?
First, you should return { elements } instead of parseData in your setup i think.
I solved this issue by marking the objects as shallowRef :
import { shallowRef, ref, computed } from 'vue'
import { EditProfileForm, EditLocationForm, EditPasswordForm} from '#/components/profile/forms'
const profile = shallowRef(EditProfileForm)
const location = shallowRef(EditLocationForm)
const password = shallowRef(EditPasswordForm)
const forms = [profile, location, password]
<component v-for="(form, i) in forms" :key="i" :is="form" />
So you should shallowRef your components inside your parseData function. I tried markRaw at start, but it made the component non-reactive. Here it works perfectly.
you could manually shallowCopy the result
<component v-for="(el, idx) in elements" :key="idx" :data="el" :is="{...el.component}" />
I had the same error. I solved it with markRaw. You can read about it here!
my code :
import { markRaw } from "vue";
import Component from "./components/Component.vue";
data() {
return {
Component: markRaw(Component),
}
For me, I had defined a map in the data section.
<script>
import TheFoo from '#/TheFoo.vue';
export default {
name: 'MyComponent',
data: function () {
return {
someMap: {
key: TheFoo
}
};
}
};
</script>
The data section can be updated reactively, so I got the console errors. Moving the map to a computed fixed it.
<script>
import TheFoo from '#/TheFoo.vue';
export default {
name: 'MyComponent',
computed: {
someMap: function () {
return {
key: TheFoo
};
}
}
};
</script>
I had this warning while displaying an SVG component; from what I deduced, Vue was showing the warning because it assumes the component is reactive and in some cases the reactive object can be huge causing performance issues.
The markRaw API tells Vue not to bother about reactivity on the component, like so - markRaw(<Your-Component> or regular object)
I also meet this problem today,and here is my solution to solve it:
setup() {
const routineLayoutOption = reactive({
board: {
component: () => RoutineBoard,
},
table: {
component: () => RoutineTable,
},
flow: {
component: () => RoutineFlow,
},
});
}
I set the component variant as the result of the function.
And in the ,bind it like compoennt()
<component
:is="routineLayoutOption[currentLayout].component()"
></component>
I try to use spyOn to spy the functions and it's implementation. However, i got this error. "Cannot spyOn on a primitive value; undefined given".
I already read the documentation of jest.spyOn in https://jestjs.io/docs/en/jest-object . But it keeps showing the same errror... is there anything that i should add and improve?
below is the code
<template>
<div>
<form #submit.prevent="onSubmit(inputValue)">
<input type="text" v-model="inputValue">
<span class="reversed">{{ reversedInput }}</span>
</form>
</div>
</template>
<script>
import axios from 'axios';
export default {
props: ['reversed'],
data: () => ({
inputValue: '',
results: [],
}),
methods: {
onSubmit(value) {
const getPromise = axios.get(
'https://jsonplaceholder.typicode.com/posts?q=' + value,
);
getPromise.then(results => {
this.results = results.data;
});
return getPromise;
},
},
};
</script>
while the test code is
import axios from 'axios'; // axios here is the mock from above!
import { shallowMount } from '#vue/test-utils';
import Form from '#/components/Form.vue';
describe('Form.test.js', () => {
const wrapper;
describe('Testing Submit events', () => {
wrapper = shallowMount(Form);
it('calls submit event', () => {
const onSubmit = jest.spyOn(Form.prototype, 'onSubmit') // mock function
// updating method with mock function
wrapper.setMethods({ onSubmit });
//find the button and trigger click event
wrapper.findAll('form').trigger('submit');
expect(onSubmit).toBeCalled();
})
});
})
Can you also vrief me what and how to use spyOn to test the method?
Thank you so much
Best regards
Lughni
Component definition suggests that Form is an object. Form.prototype === undefined because Form is not a function. Since Vue class components aren't in use, nothing suggests the opposite.
It can be spied as:
jest.spyOn(Form.methods, 'onSubmit')
This should be done prior to component instantiation. And spyOn with no implementation provided creates a spy, not a mock.
I want to test a Vue single file component which receives a prop as input. When I mock the prop, which is an object, I get an error that the object is undefined, The error comes from the HTML where the values of the object are used. If I make the prop to be a string for example (and I remove answer.value and :class="{'active': answer.selected}" from HTML), everything works fine.
Component:
<template>
<div class="answer-container" #click="setActiveAnswer()" :class="{'active': answer.selected}">
<div class="answer">
<p>{{answer.value}}</p>
</div>
</div>
</template>
<script>
export default {
name: 'Answer',
props: {
answer: Object,
},
methods: {
setActiveAnswer() {
this.$emit('selectedAnswer', this.answer);
}
}
}
</script>
Test file:
import { mount } from '#vue/test-utils'
import Answer from './../../src/components/Answer'
describe('Answer', () => {
it('should receive "answer" as prop', () => {
const answer = {
value: 'testAnswer',
selected: true
};
const wrapper = mount(Answer, {
propsData: {
answer: answer
}
});
expect(wrapper.props().answer.value).toBe('testAnswer');
})
})
The error I get is:
TypeError: Cannot read property 'selected' of undefined
Please advise what am I doing wrong. Thanks!
I managed to fix this by adding a v-if="answer" on <div class="answer-container" ..., which is quite strange (as this is not async data) since the code works fine when checking the application in the browser - the problem only appeared while unit testing the component. I suppose there is also a fix in a Jest/Unit testing way, something like declaring the prop after the component finished rendering/mounting...