How do I access v-model in vue-class-component - vue.js

I want to use the new Vue syntax from https://github.com/vuejs/vue-class-component. Unfortunately, I have trouble accessing the value that's passed with v-model. I can instead declare a #value property to pass the value but that seems a bit inconsistent given that there are a lot of existing uses of v-model in my codebase.
<template>
<div>
{{value}}
</div>
</template>
<script lang="ts">
import Vue from "vue"
import Component from "vue-class-component"
import { Prop } from "vue-property-decorator"
#Component
export default class Display extends Vue {
#Prop() value!: string
// lifecycle hook
mounted() {alert("The value is " + this.value)}
}
</script>
The current code can be accessed with:
<Display :value="myVar"/>
I'd like to access via:
<Display v-model="myVar"/>

Related

Does defineProps in script setup automatically create a local property of the defined prop?

When we pass a prop to a component and define that prop from child component with defineProps a property somehow is created and accessible from child components template.
parentComponent.vue
<template>
<child-component v-model="product">
</template>
<script setup>
import childComponent from "./childComponent.vue"
</script>
childComponent.vue
<template>
{{ product }}
</template>
<script setup>
const props = defineProps(['product'])
</script>
Here in childComponents template, the product can be accessed without needing to use props.product or toRef it. I know that script setup automatically injects the used props but I could not find any info (in docs) that the defineProps does some too. Is there any info about that.
According to this section :
The script is pre-processed and used as the component's setup() function, which means it will be executed for each instance of the component. Top-level bindings in <script setup> are automatically exposed to the template. For more details
Knowing that props are unwrapped directly inside the template and also the refs are used without .value.
If you want to reference some prop inside the script you should use props.product like in this example :
<script setup>
const props = defineProps(['product'])
const total=computed(()=>props.product.quantity*props.product.unity_price))
</script>
if the prop is only accessed by template you could get rid off const props just call the macro defineProps :
<template>
{{ product }}
</template>
<script setup>
defineProps(['product'])
</script>

Vue3 props access inside setup() returns nothing

I have a weird situation with my Vue3 component. I am trying to get a value from the props inside the setup() function. But this returns nothing.
<template>
<div class="border p-2 space-y-2">
<h2 class="text-center">Makers</h2>
{{ product.makers }}
</div>
</template>
<script>
import { ref, onMounted, toRef, toRefs } from 'vue'
import ProductStore from '../store/ProductStore'
export default {
props: {
product: Object
},
setup(props) {
console.log(props.product.makers)
},
}
</script>
The value is sent from another component like the one below
<ProductMakers :product="product"/>
But I always get undefined as my response. Any clue to resolve this problem? Am I missing something?
To my surprise, the template is always showing the value correctly. The problem is only inside the setup(). Any clue?
This means that product is reactively updated after the creation of component instance.
Updated value is supposed to be accessed in a watcher or a computed. In case a side effect like logging is needed, this is the case for a watcher:
setup(props) {
watchEffect(() => {
if (props.product.makers)
console.log(props.product.makers)
});
},

Defining types of Vue 2.x components

I use Vue 2.6 in my project
I want to define props of component for specific cases (for dynamic component in child component with passed props)
Here is some sample code
// child component
<template>
<component :is="someProp.passedComponent" />
</template>
import { Component, Vue, Prop } from 'vue-property-decorator'
#Component({
name: 'SomeComponent'
})
export default class SomeComponent extends Vue {
#Prop() readonly someProp!: { passedComponent: Vue }
}
// parent component
<template>
<some-component :some-prop="someDataForProp">
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import DummyComponent from './components/DummyComponent.vue'
#Component({
name: 'ParentComponent'
})
export default class ParentComponent extends Vue {
private someDataForProp = { passedComponent: DummyComponent }
}
</script>
// DummyComponent.vue
<template>
<div>{{ title }}</div>
</template>
<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
#Component({
name: 'DummyComponent'
})
export default class DummyComponent extends Vue {
private title = 'This is dummy component for example'
}
</script>
But when I define Component type as Vue, It makes some error like this
Type 'typeof DummyComponent' is missing the following properties from type 'Vue': $el, $options, $parent, $root, and 39 more.
I wonder if there is one type that can encompass all components defined with #Component.
passedComponent is expected to be a component itself that is used <component>, while Vue type refers to Vue component instance, i.e. component ref.
It should be:
#Prop() readonly someProp!: { passedComponent: typeof Vue }

Overriding a props type in child component?

I'm using a third party component, and it expects a prop to be a particular type.
I wish to use a different type.
Is there a way to override the type of a child component prop?
Or would I have to use a computed property and modify the prop in the parent component to the type the child component requires?
In general i think the best way would be to do as you proposed: To "use a computed property and modify the prop in the parent component to the type the child component requires". If you have to do so in several places, you could outsource the logic into a mixin that will be imported whenever the given third-party component is used.
If modifying the type of the property in the parent component isn't an option, the best way to fulfill your needs would be creating your own component that extends from the third party component and overriding the needed property. (https://v2.vuejs.org/v2/api/#extends)
Keep in mind however that the dependency containing the third-party might get updated over time. You should probably stick to a fixed version if following this approach.
I created a simple example on how to extend and override components (You can check it out as CodeSandbox here: https://codesandbox.io/s/qq9y7nm8n4
App.vue:
<template>
<div id="app">
<h2>ExtendedComponent: </h2>
<extended-component :message="1" />
<h2>BaseComponent: </h2>
<base-component message="This shall be a string" />
</div>
</template>
<script>
import ExtendedComponent from "./components/ExtendedComponent";
import BaseComponent from "./components/BaseComponent";
export default {
name: "App",
components: {
ExtendedComponent,
BaseComponent
}
};
</script>
BaseComponent.vue:
<template>
<div>
<h3>prop type: {{ typeof message }}</h3>
</div>
</template>
<script>
export default {
name: "BaseComponent",
props: {
message: {
type: String,
required: false
}
}
};
</script>
ExtendedComponent.vue:
<script>
import BaseComponent from "./BaseComponent";
export default {
name: "ExtendedComponent",
extends: BaseComponent,
props: {
message: {
type: Number,
required: false
}
}
};
</script>

Passing data from template to component

I have the following, which for my understanding should pass the value of html attribute to the #Prop with the same name however my console.log is always undefined. How is this accomplished?
import Vue from 'vue';
import { Component, Prop } from 'vue-property-decorator';
#Component({})
export default class RelayComponent extends Vue {
#Prop([String]) service: string;
constructor() {
super();
console.log(this.service);
...
HTML
<template>
<div service="expecting this value passed"></div>
</template>
<script src="./relay.ts"></script>
Vue props
Vue props are intended to pass data from a parent vue component or instance to a child vue component.
So you have a vue component, you set up a #Prop and then you get the prop for the html of the parent. Should you have a my-parent and my-child components, the my-parent template could be:
<template>
<my-child count="7"></my-child>
</template>
So a child component like this:
<template>
<div class="counter">{{count}}</div>
</template>
<script lang="ts">
import Vue from 'vue';
import { Component, Prop } from 'vue-property-decorator';
#Component({})
export default class myChild extends Vue {
#Prop() count: number;
}
</script>
Would get 7 as its count prop.
Now, in your case, there is only one component, and you're trying to setup the service variable of the component from the HTML. This is sort of weird because the point of Vue is to achieve declarative rendering from the component data: is the HTML who reacts to data changes, not your component who gets data from the HTML.
(Of course, you can also setup v-model and event listeners to make your components react to user input, but that's another story).
Basically, if I understood correctly what you want to do, your issue is that you're trying to get the service prop from the HTML of the very RelayComponent component.
Instead, you should setup the service prop in the component parent:
// Code of some parent component that renders the RelayComponent
<template>
<relay-component service="this would set the service prop as a string"></relay-component>
</template>
Only, when dealing with objects, you usually don't pass down a plain string, but a javascript object, and a service variable probably is an object, so changes are you're behind something like this:
<template>
<relay-component v-bind:service="serviceVariableInTheParentComponent"></relay-component>
</template>
Where the parent component has a service variable in its data.
 Constructor and lifehooks
Be wary about explicitly calling constructor in vue class components. If you modify the component state in the constructor, you can break the component.
Probably, you should consider to ever use the created() lifecycle hook instead of constructor() in every Vue component.