How to pass object's attribute as prop to vue.js component - javascript

Given the following component:
<script>
export default {
name: 'MyComponent',
props: {
blueprint: {
type: Object,
default () {
return {
attribute: 0,
otherAttribute: 5
}
}
}
},
data () {
return {
attribute: this.blueprint.attribute,
otherAttribute: this.blueprint.otherAttribute
}
}
}
</script>
I want to use the blueprint prop to populate data fields with some default values which could also be defined when using the component.
But how can I pass only one field of prop blueprint?
When I do this:
<my-component :blueprint="{attribute: someVar}" />
the otherAttribute of the default blueprint will be gone, of course.
Can I only set the one field of the prop and merge it with the default of the other, something like this:
<my-component :blueprint.attribute="someVar" />
<!-- It doesn't work like this, but maybe you get the idea what I want -->
Sadly the blueprint prop has too many fields to pass each field on it's own.

yeah, your Answer is fine. Here is my solution
<script>
export default {
name: 'MyComponent',
props: {
blueprint: {
type: Object
}
},
data () {
return {
blueprintDefault: {
attribute: 0,
otherAttribute: 5
}
}
},
mounted () {
this.blueprint = {...this.blueprintDefault, ...this.blueprint}
}
}
</script>

I found a solution that seems to work for me. My component now looks like this:
<script>
export default {
name: 'MyComponent',
props: {
blueprint: {
type: Object
}
},
data () {
return {
attribute: this.blueprint.attribute ?? 0,
otherAttribute: this.blueprint.otherAttribute ?? 5
}
}
}
</script>
I removed the default part of the prop and now set the default values directly in the data instead. That way if my blueprint prop does not include all attributes, the other default values will still be there.

Related

Building a form with Vuejs. Pass data from children to parent

I'm trying to build a form using "v-for" for input component and then generate a pdf file with PDFMake using data from inputs. But I didn't know how to pass the data from input component back to parent.
I read a lot of topics, but can't find a way to do this.
Here is short code without additional inputs, checkboxes etc. I plan to use around 15 inputs with different parameters to generate final PDF. Some of parameters also will be used to change final data depending of conditional statements.
Everything is work fine if code in one file, without loop and components. But not now.
Here is parent:
<template lang="pug">
.form
Input(v-for="data in form.client_info" v-bind:key="data.id" v-bind:data="data")
button(#click="pdfgen") Download PDF
</template>
<script>
export default {
components: {
Input: () => import('#/components/items/form/input')
},
data() {
return {
client_name: '',
client_email: '',
form: {
client_info: [
{id:'client_name', title:'Name'},
{id:'client_email', title: 'Email'},
{id:'foo', title: 'foo'}
],
}
}
},
methods: {
pdfgen: function () {
var pdfMake = require('pdfmake/build/pdfmake.js')
if (pdfMake.vfs == undefined) {
var pdfFonts = require('pdfmake/build/vfs_fonts.js')
pdfMake.vfs = pdfFonts.pdfMake.vfs;
}
if (this.foo) {
var foo = [
'Foo: ' + this.foo
];
} else {
foo = ''
]
}
var docDefinition = {
content: [
'Name: ' + this.client_name,
'Email: ' + this.client_email,
'\n',
foo
]
}
pdfMake.createPdf(docDefinition).download('Demo.pdf');
}
}
}
</script>
Here is a children (Input component):
<template lang="pug">
label.form_item
span.form_item_title {{ data.title }}
input.form_item_input(:v-model="data.id" type="text")
</template>
<script>
export default {
props: ['data']
}
</script>
Any ideas how to make it work?
You'll want to use a method that vue has build-in named $emit().
Before going into how to do that, a quick explanation. Because vue attempts to make data flow one-directional there is not a super quick way to just pass data back to a parent. What Vue proposes instead is to pass a method to the child component that, when called, will 'emit' the value it changed to it's parent and the parent can then do what it wants with that value.
So, in your parent component you'll want to add a method that will handle a change when the child emits. This could look something like:
onChildValueChanged(value){ this.someValue = value }
The value we passed to the function will be coming from our child component. We will need to define in our child component what this function should do. In your child component you could have a function that looks like so:
emitValueChange(event){ this.$emit('childFunctionCall', this.someChildValue) }
Next we need to tie those two functions together by adding an attribute on our child template. In this example that might look like:
<Child :parentData="someData" v-on:childFunctionCall="onChildValueChanged"></Child>
What that above template is doing is saying that when the function on:childFunctionCall is 'emited' then our function in the parent scope should fire.
Finally, in the child template we just need to add some event that calls out emiter. That could look like:
<button v-on:click="emitToParent">This is a button</button>
So when our button is clicked, the emiter is called. This triggers the function in our child component named 'emitToParent' which in turn calls the function we passed to our child component.
You'll have to tailor your use case to match the exam
I found a solution using Vuex.
So now my components look like this.
Here is parent:
<template lang="pug">
.form
Input(v-for="data in formClient" v-bind:key="data.id" v-bind:data="data")
button(#click="pdfgen") Download PDF
</template>
<script>
export default {
components: {
Input: () => import('#/components/items/form/input'),
store: () => import('#/store'),
},
computed: {
formClient() { return this.$store.getters.client }
}
}
</script>
Here is a children (Input component):
<template lang="pug">
label.form_item
span.form_item_title {{ data.title }}
input.form_item_input(v-model="data.value" :type="data.input_type")
</template>
<script>
export default {
props: ['data'],
computed: {
form: {
get () {
return this.$store.state.obj.message
},
set (value) {
this.$store.commit('updateMessage', value)
}
}
}
}
</script>
Here is a store module:
<script>
export default {
actions: {},
mutations: {},
state: {
form: {
client: [
{id:'client_name', title:'Name', value: ''},
{id:'client_email', title: 'Email', value: ''},
{id:'foo', title: 'foo', value: ''}
]
}
},
getters: {
client: state => {
return state.form.client;
}
}
}
</script>
Now I can read updated data from store directly from PDFMake function.

Vue Setting A Method Name Based on a Prop Value?

Is it possible to set the name of a Method in my Vue application based on the value of a prop? So my Vue Application looks like:
<script>
export default {
name: 'CheckboxFilter',
props: {
tax: '',
identifier: 'Category'
}
}
</script>
Then in my Methods I want to name a method based on the value of the identifier prop.
methods: {
updateSelected + this.identifier (e) {
this.$store.commit('updateSelectedCategories', e.target.value)
}
}
Is something like this possible to do where the name of the method is influenced by the value of a prop?
EDIT: the reason why I am trying to do this is I have a component that has checkbox input filters. The layout of this component will always be the same the only difference will be the values present on those checkboxes. I need to keep track of which checkboxes are select independently from each component. So my thought for doing this is to pass an identifier prop to use as a naming convention for the method where I store the value of the selected checkboxes for each component independently in my Vuex store.
Instead, pass it as an argument to your vuex store:
const store = new Vuex.Store({
state: {
attribute: {
tag: true,
bag: false
}
},
mutations: {
setAttr(state, { value, attribute }) {
state.attribute[attribute] = value;
}
}
});
Vue.component('custom-checkbox', {
props: ['attribute'],
template: '<div><input type="checkbox" v-model="checkBox">{{attribute}}</div>',
computed: {
checkBox: {
get() {
return this.$store.state.attribute[this.attribute]
},
set(value) {
this.$store.commit('updateMessage', {
value,
attribute: this.attribute
});
}
}
}
});
new Vue({
el: '#app',
store,
template: '<div><custom-checkbox attribute="tag" /><custom-checkbox attribute="bag" /></div>'
});
<html>
<body>
<div id="app"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/3.1.0/vuex.min.js"></script>
</body>
</html>
That way, you're not trying to rely on dynamic data in your static code.

Custom prop type Vue.js

Is there a way to create custom prop types (and extend it with validation) for Vue.js props?
In the below example you'll find the Object prop background. Instead of an Object, I would like to have a custom prop type Image.
Image would check if src and alt are filled, rest would be optional.
What we have now:
export default {
props: {
background: {
type: Object,
src: String,
srcset: String,
alt: String,
title: String,
},
},
};
What I'd like to have:
class customPropImage {
// magic ...
}
export default {
props: {
background: Image,
},
};
Sure you can do it. According to the Vue documentation you can set the type to the constructor of your custom type. With custom validation it could look something like this:
function CustomImage () {
// Magic
}
Vue.component('blog-post', {
props: {
myImage: {
type: CustomImage,
validator: function (value) {
return true; // or false based on value of myImage
}
}
}
})
Here is an example on codesandbox

Updating VueJS component data attributes when prop updates

I'm building a VueJS component which needs to update the data attributes when a prop is updated however, it's not working as I am expecting.
Basically, the flow is that someone searches for a contact via an autocomplete component I have, and if there's a match an event is emitted to the parent component.
That contact will belong to an organisation and I pass the data down to the organisation component which updates the data attributes. However it's not updating them.
The prop being passed to the organisation component is updated (via the event) but the data attibute values is not showing this change.
This is an illustration of my component's structure...
Here is my code...
Parent component
<template>
<div>
<blink-contact
:contact="contact"
v-on:contactSelected="setContact">
</blink-contact>
<blink-organisation
:organisation="organisation"
v-on:organisationSelected="setOrganisation">
</blink-organisation>
</div>
</template>
<script>
import BlinkContact from './BlinkContact.vue'
import BlinkOrganisation from './BlinkOrganisation.vue'
export default {
components: {BlinkContact, BlinkOrganisation},
props: [
'contact_id', 'contact_name', 'contact_tel', 'contact_email',
'organisation_id', 'organisation_name'
],
data () {
return {
contact: {
id: this.contact_id,
name: this.contact_name,
tel: this.contact_tel,
email: this.contact_email
},
organisation: {
id: this.organisation_id,
name: this.organisation_name
}
}
},
methods: {
setContact (contact) {
this.contact = contact
this.setOrganisation(contact.organisation)
},
setOrganisation (organisation) {
this.organisation = organisation
}
}
}
</script>
Child component (blink-organisation)
<template>
<blink-org-search
field-name="organisation_id"
:values="values"
endpoint="/api/v1/blink/organisations"
:format="format"
:query="getQuery"
v-on:itemSelected="setItem">
</blink-org-search>
</template>
<script>
export default {
props: ['organisation'],
data() {
return {
values: {
id: this.organisation.id,
search: this.organisation.name
},
format: function (items) {
for (let item of items.results) {
item.display = item.name
item.resultsDisplay = item.name
}
return items.results
}
}
},
methods: {
setItem (item) {
this.$emit('organisationSelected', item)
}
}
}
</script>
How can I update the child component's data properties when the prop changes?
Thanks!
Use a watch.
watch: {
organisation(newValue){
this.values.id = newValue.id
this.values.search = newValue.name
}
}
In this case, however, it looks like you could just use a computed instead of a data property because all you are doing is passing values along to your search component.
computed:{
values(){
return {
id: this.organisation.id
search: this.organisation.name
}
}
}

Passing props dynamically to dynamic component in VueJS

I've a dynamic view:
<div id="myview">
<div :is="currentComponent"></div>
</div>
with an associated Vue instance:
new Vue ({
data: function () {
return {
currentComponent: 'myComponent',
}
},
}).$mount('#myview');
This allows me to change my component dynamically.
In my case, I have three different components: myComponent, myComponent1, and myComponent2. And I switch between them like this:
Vue.component('myComponent', {
template: "<button #click=\"$parent.currentComponent = 'myComponent1'\"></button>"
}
Now, I'd like to pass props to myComponent1.
How can I pass these props when I change the component type to myComponent1?
To pass props dynamically, you can add the v-bind directive to your dynamic component and pass an object containing your prop names and values:
So your dynamic component would look like this:
<component :is="currentComponent" v-bind="currentProperties"></component>
And in your Vue instance, currentProperties can change based on the current component:
data: function () {
return {
currentComponent: 'myComponent',
}
},
computed: {
currentProperties: function() {
if (this.currentComponent === 'myComponent') {
return { foo: 'bar' }
}
}
}
So now, when the currentComponent is myComponent, it will have a foo property equal to 'bar'. And when it isn't, no properties will be passed.
You can also do without computed property and inline the object.
<div v-bind="{ id: someProp, 'other-attr': otherProp }"></div>
Shown in the docs on V-Bind - https://v2.vuejs.org/v2/api/#v-bind
You could build it like...
comp: { component: 'ComponentName', props: { square: true, outlined: true, dense: true }, model: 'form.bar' }
<component :is="comp.component" v-bind="{...comp.props}" v-model="comp.model"/>
I have the same challenge, fixed by the following:
<component :is="currentComponent" v-bind="resetProps">
{{ title }}
</component>
and the script is
export default {
…
props:['title'],
data() {
return {
currentComponent: 'component-name',
}
},
computed: {
resetProps() {
return { ...this.$attrs };
},
}
<div
:color="'error'"
:onClick="handleOnclick"
:title="'Title'"
/>
I'm came from reactjs and I found this solve my issue
If you have imported you code through require
var patientDetailsEdit = require('../patient-profile/patient-profile-personal-details-edit')
and initalize the data instance as below
data: function () {
return {
currentView: patientDetailsEdit,
}
you can also reference the component through the name property if you r component has it assigned
currentProperties: function() {
if (this.currentView.name === 'Personal-Details-Edit') {
return { mode: 'create' }
}
}
When you use the <component> inside a v-for you can change the answer of thanksd as follow:
methods: {
getCurrentProperties(component) {
if (component === 'myComponent') {
return {foo: baz};
}
}
},
usage
<div v-for="object in object.items" :key="object._your_id">
<component :is="object.component" v-bind="getCurrentProperties(object.component)" />
</div>
Let me know if there is an easier way.

Categories

Resources