How to override the vue component props with condition check? - vue.js

What's the recommended way to override/extends a component?
E.g. I want to override the vuetify component VTextField:
<script>
import { VTextField } from 'vuetify/lib/components';
export default {
name: 'RTextField',
extends: VTextField,
props: {
outlined: {
default: true,
},
hideDetails: {
default: true,
},
},
};
</script>
Above I used the extends to override some default props values.
I want to ask how to conditional set the value for hideDetails, for example based on a specific prop value hint, if there is a hint prop in RTextField, and the hideDetails should be set to false
`<r-text-field hint="xxx detail description">`

Instead of extending, you can wrap VTextField, that way you have complete control over the props and how they should be forwarded to the wrapped component.
Untested:
<template>
<v-text-field
:outlined="outlined"
:hide-details="$attrs.hint ? false : hideDetails"
v-bind="$attrs"
v-on="$listeners"
/>
</template>
import { VTextField } from 'vuetify/lib/components'
export default {
name: 'RTextField',
components: {
VTextField,
},
// We'll pass attrs as props via v-bind
inheritAttrs: false,
props: {
outlined: {
type: Boolean,
default: true,
},
hideDetails: {
type: Boolean,
default: true,
},
},
}

Related

how can I mock a component when I make a storyBook?

I'm making a storyBook out of a component that we'll call ParentComponent, it has a child called ChildComponent.
Inside ChildComponent there is a function that makes a call to the code of another repository so this is where the storybook fails.
components: {
DataProvider: () => import("common-data-provider"),// --> problem this line
}
How can I mock ChildComponent and not make this request?
StoryBook of Parent Component
import articlesFunds from "../__fixtures__/articles.json";
export default {
component: ParentComponent,
argTypes: {
getConfigFilesDataProvider: {
control: "object",
},
},
args: {
getConfigFilesDataProvider: () => new Promise((resolve) => setTimeout(() => resolve({ data: articlesFunds }))),
},
};
export const Default = (args) => ({
components: { ParentComopnent },
props: Object.keys(args),
template: `
<ParentComopnent
:data-provider="getConfigFilesDataProvider"
/>
`,
});
ChildComponent
<template>
<DataProvider :data="articles" />
</template>
<script>
export default {
name: "ChildComponent",
components: {
DataProvider: () => import("common-data-provider"),// --> problem this line
ComponentB
},
props: {
articles: {
type: Array,
required: true,
},
}
};
</script>
Storybook doesn't specifically provide the support for module mocking, but it's possible to use Webpack aliases for that.
Dependency injection of any kind can be used to modify the behaviour of a component in uncommon conditions, e.g. the ability to provide custom component:
props: {
articles: {
type: Array,
required: true,
},
dataProvider: {
type: null,
default: () => import("common-data-provider"),
}
}
And used like dynamic component:
<component :is="dataProvider" :data="articles"/>
Then custom implementation can be provided through dataProvider prop.

string which is sent to other compononent with router.push is undefined

Well, I have two components. From one component I would like to send a string to another component where the string should be displayed on the console. This is the component where the data is pushed to the other component:
this.$router.push({path: "/editService", params:{data: 'test'}})
This is the second component. In here the string should be displayed:
export default {
data(){
return{
ServiceData: []
}
},
created() {
this.ServiceData=this.$route.params.data
console.log(this.ServiceData)
}
}
If you are using Vue Router, then you should have a route defined similar to
{
path: '/editService/:routeData',
component: User,
props: true
}
then add a prop to your user component:
export default {
data(){
return{
ServiceData: this.routeData
}
},
props: {
routeData: {
type: String, // at least for test
required: true
}
},
created() {
console.log(this.ServiceData)
}
}

How to make this vue component reusable with it's props?

I am trying to make this component reusable so later can install it in any project and via props add needed values e.g. images and function parameters (next, prev, intervals...) inside any component.
<template>
<div>
<transition-group name='fade' tag='div'>
<div v-for="i in [currentIndex]" :key='i'>
<img :src="currentImg" />
</div>
</transition-group>
<a class="prev" #click="prev" href='#'>❮</a>
<a class="next" #click="next" href='#'>❯</a>
</div>
</template>
<script>
export default {
name: 'Slider',
data() {
return {
images: [
'https://cdn.pixabay.com/photo/2015/12/12/15/24/amsterdam-1089646_1280.jpg',
'https://cdn.pixabay.com/photo/2016/02/17/23/03/usa-1206240_1280.jpg',
'https://cdn.pixabay.com/photo/2015/05/15/14/27/eiffel-tower-768501_1280.jpg',
'https://cdn.pixabay.com/photo/2016/12/04/19/30/berlin-cathedral-1882397_1280.jpg'
],
timer: null,
currentIndex: 0,
}
},
mounted: function() {
this.startSlide();
},
methods: {
startSlide: function() {
this.timer = setInterval(this.next, 4000);
},
next: function() {
this.currentIndex += 1
},
prev: function() {
this.currentIndex -= 1
}
},
computed: {
currentImg: function() {
return this.images[Math.abs(this.currentIndex) % this.images.length];
}
}
}
</script>
styles...
So later it would be <Slider... all props, images loop here/> inside other components.
How can be it be achieved?
Just move what needs to come from another component to props. That way other component can pass the relevant info it needs.
export default {
name: 'Slider',
props: {
images: Array,
next: Function
prev: Function,
// and so on
},
...
The parent component would call it like:
<Slider :images="imageArray" :next="nextFunc" :prev="prevFunc" />
EDIT
You can pass an interval value via props:
export default {
name: 'Slider',
props: { intervalVal: Number },
methods: {
startSlide: function() {
this.timer = setInterval(this.next, this.intervalVal);
},
}
You can also pass function from parent to child via props.
export default {
name: 'Slider',
props: { next: Function },
methods: {
someMethod: function() {
this.next() // function from the parent
},
}
I don't really understand your use case 100% but these are possible options.

Is it possible to dynamically add chart type in the extends: property, based on props from parent component?

I have a vue chartjs component which imports the whole vue-chartjs library. My idea is, is it possible to somehow pass the type of the chart which I want and add it to the 'extends: VueCharts.charttype?.' In the example I provide it extends the VueCharts.Line, I need this property to be dynamically interpolated, passed from props. Is it possible this charttype to come from a parent props dynamically and how?
<script>
import { VueCharts } from "vue-chartjs";
export default {
extends: VueCharts.Line,
props: ["chartdata", "options"],
mounted() {
this.renderChart(this.chartdata, this.options);
}
}
</script>
<style scoped>
</style>
since extends the same as mixins, you need to pass a dynamic mixin, in order to do that you need two components, imagine we have component ChartWrapper :
<template>
<div>
<div>{{ chartType }}</div>
<chart :chart-data="datacollection"/>
</div>
</template>
<script>
import Chart from "./Chart";
import { VueCharts, mixins } from "vue-chartjs";
const { reactiveProp } = mixins;
export default {
name: "ChartWrapper",
components: {
Chart
},
props: {
chartType: {
type: String,
required: true
}
},
data() {
return {
datacollection: {
labels: [this.getRandomInt(), this.getRandomInt()],
datasets: [
{
label: "Data One",
backgroundColor: "#f87979",
data: [this.getRandomInt(), this.getRandomInt()]
},
{
label: "Data One",
backgroundColor: "#f87979",
data: [this.getRandomInt(), this.getRandomInt()]
}
]
}
};
},
methods: {
getRandomInt() {
return Math.floor(Math.random() * (50 - 5 + 1)) + 5;
}
},
created() {
if (this.chartType) {
Chart.mixins = [reactiveProp,VueCharts[this.chartType]];
}
}
};
</script>
this component takes chartType as a prop, and I import all charts as VueCharts in top of the script ==> 1
second component:
<script>
export default {
props: ["options"],
mounted() {
// this.chartData is created in the mixin.
// If you want to pass options please create a local options object
this.renderChart(this.chartData, this.options);
}
};
</script>
the second component just has options props, and renderChart function invoked.
==> 2
What is happening?
the ChartWrapper component receives the chart type by chartType prop, in the created hook, if chartType exist, assign the chart(resolved by VueCharts[this.chartType]) to Chart component as a mixin in addition to reactiveProp,
I also pass the chart data to Chart component.
in the end, call the ChartWrapper component:
<ChartWrapper chartType="Bar"/>
Live example on code sandbox: https://codesandbox.io/s/vue-template-w9r8k
You can also choose for the option to just extend the Line chart and update the config of the chart with the chart type you want and give it an update so it changes type.
<script>
import { Line, mixins } from 'vue-chartjs';
const { reactiveProp } = mixins;
export default {
extends: Line,
name: "LineChart",
mixins: [reactiveProp],
props: {
options: { type: Object },
chartType: { type: String }
},
mounted () {
this.renderChart(this.chartData, this.options);
},
watch: {
options: {
deep: true,
handler () {
this.$data._chart.options = this.options;
this.updateChart();
}
},
chartType (newVal) {
this.$data._chart.config.type = newVal;
this.updateChart()
}
},
methods: {
updateChart () {
this.$data._chart.update();
},
}
}
</script>

Props not passed to the dynamic component

I'm rendering a dynamic component:
<component :is="element.name == 'text' ? element.component : false" v-bind="elementProps"></component>
with:
computed: {
element() {
return {
name: this.elementObject.type,
component: {
components: { TextInput },
template: `<text-input :form-id="formId"
:section-id="sectionId"
:element-id="elementId"
id="test2"
></text-input>`
},
}
},
elementProps() {
const props = {
formId: this.formId,
sectionId: this.sectionId,
elementId: this.elementId,
id: this.generateId()
};
return props;
},
}
.. but I get an error Property or method "formId" is not defined on the instance but referenced during render. although I am passing in the props. What am I doing wrong?
You forgot to define the props when you create the component in the element function, try:
component: {
components: { TextInput },
template: `<text-input :form-id="formId"
:section-id="sectionId"
:element-id="elementId"
id="test2"></text-input>`,
props: ['formId', 'sectionId', 'elementId', 'id']
},
formId, sectionId and elementId in the template have to be defined somewhere in the component either as props, data or computed property.