So let's just say that I have a HelloWorld component that I would like to import multiple times and assign some props to each one (as each instance will be doing their own thing). Usually you'll do something like this:
To keep it simple I haven't used proper syntax.
import HelloWorld from "./components/HelloWorld";
<HelloWorld v-if="which" title="0" key="1"/>
<HelloWorld v-else title="1"/>
However, I was wondering if there was a way to import them with props already assigned so something like:
import HelloWorld from "./components/HelloWorld";
import HelloWorld1 from "./components/HelloWorld";
HelloWorld.props = { title: "1" } // this doesn't work
HelloWorld1.props = { title: "2" } // this doesn't work
In order to use <component>
E.g.
<component :is="which"/>
which = "HelloWorld" || "HelloWorld1"
A component's props can only be set in the component definition object.
It looks like you're attempting to pass different prop values based on the value of which. You can do that by passing an object to v-bind:
<HelloWorld v-bind="which ? { title: '0', key: '1' } : { title: '1' }"/>
Related
When passing a prop to a child component in Vue, the documentation says:
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.
The prop is used to pass in an initial value; the child component wants to use it as a local data property afterwards. In this case, it’s best to define a local data property that uses the prop as its initial value:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
We are using typescript. The syntax for "defining a local data property" is as follows (to my understanding):
<script lang="ts">
import Vue from 'vue'
import { Component } from 'vue-property-decorator'
#Component
export default class App extends Vue {
// Data property
myDataProperty: string;
</script>
And the syntax for a prop is:
#Component
export default class App extends Vue {
// Makes a "exampleProperty" a component prop with the default value of 'Example'
#Prop({default: 'Example'})
exampleProperty: string
}
So, we tried to follow the documentation, and ended up with:
parentComponent.vue
<template>
<childComponent testProperty='test' />
</template>
childComponent.vue
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
#Component
export default class childComponent extends Vue {
#Prop(
{
default: 'notTest',
validator: (component) => {
return [
'notTest',
'test',
].indexOf(component) > -1;
},
},
)
testProperty!: string;
testProperty = this.testProperty;
</script>
That, predictably, errored with `Duplicate identifier testProperty.
So, we tried
...
testProperty!: this.testProperty;
...
which resulted in
Duplicate identifier 'testProperty'.
Property 'testProperty' has no initializer and is not definitely assigned in the constructor.
Subsequent property declarations must have the same type. Property 'testProperty' must be of type 'this', but here has type 'any'.
So, I decided to try the "vue-class-component" decorator.
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
#Component({
data: function(){
return {
testProperty: this.testProperty,
}
}
})
export default class childComponent extends Vue {
#Prop(
{
default: 'notTest',
validator: (component) => {
return [
'notTest',
'test',
].indexOf(component) > -1;
},
},
)
testProperty!: string;
testProperty = this.testProperty;
</script>
This resulted in the error Property 'testProperty' does not exist on type 'Vue'.
I would like to, in a handler, do this.testProperty = 'newProperty' at some point, but cannot, because that would be directly modifying a prop.
How can I define a local data property that uses a prop as its initial value in Typescript?
EDIT:
If I do none of the above, and simply define the prop, with no attempt to define a local data property that uses the prop as its initial value, and then do
this.testProperty = 'test'
in a handler, this error is displayed in the chrome console:
vue.runtime.esm.js[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "testProperty"
I will summarise my comments into a single coherent answer: the problem you are seeing is that you have already defined this.testProperty by declaring it as a prop: doing testProperty = this.testProperty is a circular reference at best. Using the #Prop decorator alone will do the mapping of the attribute in the template to the variable.
<script lang="ts">
import { Component, Prop, Vue } from 'vue-property-decorator';
#Component
export default class childComponent extends Vue {
#Prop(
{
default: 'notTest',
validator: (component) => {
return [
'notTest',
'test',
].indexOf(component) > -1;
},
},
)
testProperty!: string;
// Map prop to local data property
testPropertyLocal = this.testProperty;
</script>
Also, remember this caveat: VueJS properties must be kebab-case in templates and camelCase in JS. So, you need to update your child component reference to:
<template>
<childComponent test-property='test' />
</template>
I have a Vue component that imports and registers other components locally. I want to loop through components object and render them dynamically. I'm trying to achieve this like the following (inside .vue file):
<template>
<div v-for="component in components" class="component">
<component v-bind:is="component"></component>
</div>
</template>
<script>
import CarouselDefault from './carousel/CarouselDefault'
import AlertError from './alerts/AlertError'
import AlertInfo from './alerts/AlertInfo'
import AlertSuccess from './alerts/AlertSuccess'
import AlertWarning from './alerts/AlertWarning'
export default {
name: 'App',
components: {
CarouselDefault,
AlertError,
AlertInfo,
AlertSuccess,
AlertWarning
}
}
</script>
and I get this error message:
Property or method "components" is not defined on the instance but referenced during render. Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
The components property aren't avalibale in template builder of vue, you have to defien a computed or a property of data .
example:
computed:{
components(){
return [
CarouselDefault,
AlertError,
AlertInfo,
AlertSuccess,
AlertWarning
];
}
}
or
data(){
return {
components:[
CarouselDefault,
AlertError,
AlertInfo,
AlertSuccess,
AlertWarning
]
}
}
Hay,
I want to create reuseable compoenent in React like for example DialogBox.
It has required fields like type, message and title.
Type can be one of 'yesNo' string or 'ok' and it defines how much buttons should be shown (yes & no, ok).
Message and title are simple text that are displayed inside of dialog box.
Simple view of this:
IMG
And I created DialogBox component that I can use like:
<DialogBox type={'yesNo'} message={'Message'} title={'Title'} />
But I want to use predefined const like that:
<DialogBox type={DialogBox.TYPE.YES_NO} message={'Message'} title={'Title'} />
With simple one import DialogBox:
import Dialogbox from './DialogBox.js'
To achieve DialogBox.TYPE.YES_NO
In my component DialogBox I created static object TYPE with predefined elements:
...
static TYPE = {
YES_NO: 'yesNo',
OK: 'ok'
}
...
And everything was beautiful to the time when I wanted to use that statics to check props in child component:
ButtonArea.propTypes = {
type: PropTypes.oneOf(Object.values(DialogBox.TYPE))
};
And I got a circular dependencies error and my DialogBox.TYPE inside of props definition is undefined. That is how I can see it:
In DailogBox.js :
import ButtonArea from './BA';
export default class DialogBox extends Component {
static TYPE = {
YES_NO: 'yesNo',
OK: 'ok'
}
render() {
<ButtonArea type={this.props.type} />
}
}
In ButtonArea.js:
import DialogBox from './DB';
export default class ButtonArea extends Component {...}
ButtonArea.propTypes = {
type: PropTypes.oneOf(Object.values(DialogBox.TYPE))
};
And on checking propTypes DialogBox is an undefined cause of circular dependencies.
The question is.
Is there a way to create component to use it like component and which has inside const object definitions and avoid circular dependencies. Like :
<Test type={Test.TYPE.SUPER_TEST}/>
I don't want to import Test and TestConst and use it like that:
<Test type={TestConst.TYPE.SUPER_TEST}/>
You need to update the propsType for button , you need to check for the KEYS instead of VALUES, like below
ButtonArea.propTypes = {
type: PropTypes.oneOf(Object.keys(DialogBox.TYPE))
};
I'm using single file components and I'd like to know is there any way to extend imported component
For example I have Products.vue and Filter.vue component. Products.vue component contains string with table name it gets data from. Filter.vue to work should know table name it filters. Normaly I should pass that data with props from Products.vue to Filter.vue. But the problem is that there is multiple filters that becomes repetitive and I'd like to avoid it.
Will be good if there is some construction for extending imported component like:
Products.vue:
<template>
...
</template>
<script>
import Filter from './Filter.vue'
export default {
components: {
// something like
'extendedFilter': Filter.extend({ data() { return { table: this.table_name } } })
},
data() {
return {
table_name: 'test_table'
}
}
}
</script>
I have a component that should display data from the store, but the component is reusable, so I would like to pass the name of the store module and property name via props, like so:
<thingy module="module1" section="person">
Then, in the component:
<template>
<h2>{{ title }}</h2>
<p>{{ message }}</p>
</template>
<script>
import { mapState } from 'vuex';
import get from 'lodash.get';
export default {
props: [
'module',
'section',
],
computed: mapState(this.module, {
title: state => get(state, `${this.section}.title`),
message: state => get(state, `${this.section}.message`),
})
}
</script>
The problem is, it seems the props are undefined at the time when mapState() is executed. If I hardcode the prop values, the component works. Also, if I log the props in the created() hook, I get the expected values. So it seems like a race condition.
Am I going about this the wrong way here?
Update
The module namespace must be passed from within the mapping function, like so:
computed: mapState({
title() {
return get(this.$store.state, `${this.module}.${this.section}.title`);
},
message() {
return get(this.$store.state, `${this.module}.${this.section}.message`);
}
})
(note that get() is a lodash, not a vue function)
This can be further abstracted into a mixin.
Note the comment in the mapState example:
// to access local state with `this`, a normal function must be used
countPlusLocalState (state) {
return state.count + this.localCount
}
You are using arrow functions.
As for this.module, I think you're going to have to forego the binding helper notation and explicitly put the module reference into the definitions. I'm guessing that looks like:
computed: mapState(this.module, {
title(state) {
return get(`${state}.${this.module}`, `${this.section}.title`);
},
message(state) {
return get(`${state}.${this.module}`, `${this.section}.message`);
}
})