How to pass bound variables to a subcomponent? [duplicate] - vue.js

I'm trying to pass php/laravel data to my component from a custom global variable. I've seen examples of this going into the new Vue({}) area directly but I haven't seen any way to pass this by going into right into the component
<script>
var itemData = //json object
</script>
<custom-component item-data="ITEMDATAVAR"></custom-component>
I should specify that I do have item-data in my component props. The issue is that I'm not sure how to tell my component's html that I'm passing the value of the variable itemData and not the string "itemData"

I think you are referring to dynamic props
<custom-component v-bind:item-data="ITEMDATAVAR"></custom-component>
or use the shorthand syntax
<custom-component :item-data="ITEMDATAVAR"></custom-component>

You should add the item-data to the props array like this:
Vue.component('custom-component', {
props: ['item-data'],
...
}

You can research this Vue.js example
Create a variable
new Vue({
el: '#el',
data: yourJsonObject
})
In you component you have to write about props
Vue.component('custom-component', {
props: ['item-data']
...
}
Pass the data to the component the same way
<custom-component item-data="ITEMDATAVAR"></custom-component>
I have not tested how it will work, guided by the documentation.

Related

Set Component prop for all instances / Pass same data to all Component instances

I have a Component where each instance needs some data being passed onto it, that Component is used multiple times and all instances of the component should receive the same data/prop.
<my-component :someProp="someValue"></my-component>
<my-component :someProp="someValue"></my-component>
<my-component :someProp="someValue"></my-component>
<!-- ...and lots more... -->
This is my current method:
Which gets kind of redundant, how can I pre-populate this someProp for ALL instances of my component?
I tried Vue.extend() but cannot figure out what syntax it expects, the documentation is not clear enough on that part.
This is how I imagined it to work:
// App.vue
import MyComponent from './components/MyComponent'
const PreConfiguredComponent = Vue.extend(MyComponent, {
props: {
someProp: "someValue"
}
})
export default {
name: 'app',
data: () => ({}),
components: {
MyComponent: PreConfiguredComponent
}
}
You get the idea, I don't know how to express it better. Doesn't have to be done with props but I don't know other methods to pass data along.
There are quite a lot of different ways to approach this. I doubt this will be anything like an exhaustive list but I can give some sense of the possibilities.
1. Store the value globally
The obvious choice here is the Vuex store. Just put the relevant data in the store and then the components can grab what they need.
However, a global store doesn't have to be Vuex. If you have no other reason to introduce Vuex then you might prefer something more ad hoc.
An alternative to the store is to hold the data on $root, an approach described in the official documentation:
https://v2.vuejs.org/v2/guide/state-management.html#Simple-State-Management-from-Scratch
A further alternative is just to have a .js file that can hold the data as a singleton. There are various ways to do it but in its simplest form it might be something like this:
export default {
myData: null
}
You'd then import that object and read/write the value of myData as required. It won't necessarily be reactive but I'm assuming that the initial value would be set at the start, before the application is created, and wouldn't change after that.
2. Store the value on the Vue prototype
This is quite similar to the above but I think it warrants a separate mention. It'd look something like this:
Vue.prototype.$myComponentData = 'someValue'
Then within any component you could access the value via the property $myComponentData.
Documentation: https://v2.vuejs.org/v2/cookbook/adding-instance-properties.html
3. Provide/Inject
The provide/inject mechanism is one of the lesser known Vue features but it provides an alternative to using props to pass data down to child components. It has various pros and cons and typically you'd try to use props instead.
https://v2.vuejs.org/v2/api/#provide-inject
In short, it allows a component to provide a named value to all of its descendants without needing to explicitly pass it to each one individually. The descendant components can then choose which named properties they would like to have injected.
You wouldn't be able to use provide/inject to pass different values to descendant components but in your case the value is the same so it should work.
If you think this approach might be for you then I suggest some further reading:
https://blog.logrocket.com/how-to-make-provide-inject-reactive/
4. Refactor to remove the duplication
While this is unlikely to be the solution you go with I do think it is worth mentioning.
The starting premise for the question seems to be that passing the prop explicitly is a form of duplication. Extra noise and extra effort with the potential to make mistakes.
Potentially we can remove that duplication while still passing the prop. The key here is to refactor the template so that the child component only features once.
Obviously that would need to be within a loop so that we still get all the desired components. That loop would need a suitable data structure to drive it so that all the right components get created.
I assume the template in the question is a simplification. If you really do have several instances consecutively then refactoring to use a v-for should be pretty trivial. If, as seems more likely, the components are nested in various places within the layout then it can get unwieldy trying to encode that in data structures just to avoid a bit of template duplication.
Hopefully the idea is clear but if not you could give this a read:
https://michaelnthiessen.com/reducing-redundant-repetition
5. Dynamic components
This is what's alluded to in the question. There are various ways to do it but the core of the idea is that we change our component definitions at runtime. That doesn't necessarily mean creating a new component, it could equally mean prodding something into an existing component definition. We've already seen a variation of this idea with the Vue.prototype approach mentioned earlier.
In theory it could be done with the default value of a prop but it seems unnecessary to use a prop unless that prop is going to be set from the outside in the usual way in some cases.
We could set it as a property using data but personally I would be tempted to use a variation of the Vue.prototype trick to add the property to the component's own prototype:
MyComponent = Vue.extend({
template: `<div>{{ value }}</div>`
})
MyComponent.prototype.value = 'some value'
new Vue({
el: '#app',
components: {
MyComponent
}
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<my-component></my-component>
</div>
Using the prototype is relatively cheap from a performance perspective. There are potential problems with reactivity but they would only apply if the value can change, which it can't in this case.
Just to illustrate the prop and data approaches explicitly:
MyComponent = {
template: `<div>{{ value }}</div>`
}
PreConfiguredComponent = Vue.extend({
extends: MyComponent,
props: {
value: {
default: 'some prop value'
}
}
})
new Vue({
el: '#app',
components: {
MyComponent: PreConfiguredComponent
}
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<my-component></my-component>
</div>
and:
MyComponent = {
template: `<div>{{ value }}</div>`
}
PreConfiguredComponent = Vue.extend({
extends: MyComponent,
data () {
return {
value: 'some data value'
}
}
})
new Vue({
el: '#app',
components: {
MyComponent: PreConfiguredComponent
}
})
<script src="https://unpkg.com/vue#2.6.10/dist/vue.js"></script>
<div id="app">
<my-component></my-component>
</div>
You could equally try to tweak the imported MyComponent directly rather than extending it.

Vuejs parameters in props

Is it possible to pass props as funcitions on VueJs?
<template>
<line-chart
class="card-content"
:chartData="lineData2('Temp')"
:options="options"
:width="800">
</line-chart>
</template>
The chartData prop can be used as a method? Is there any way to pass a method on a prop?
Cheers,
It depends on what you are trying to do. If you want to pass an actual function and not the result of a function, you can do:
<line-chart class="card-content" :chartData="() => lineData2('Temp')" :options="options" :width="800"></line-chart>
Which will pass the lineData2 function itself, however, it will still be executed in the parent scope and not in the component scope, so it won't have any access to the components this context.
Here's a JSFiddle: https://jsfiddle.net/rz8c1v4L/
If you just want to pass the result of the function then what you are doing is fine.
Yes you can. As long as your function returns a value. Everything should work.
Yes you can! You can achieve that by mentioning the type of your prop, as it is clearly written in Vue's documentation. For e.g.
Vue.component('example', {
props: {
propA: {
type: function
}
}
})
Also checkout this link

Vuejs component props as string

I want to pass this prop as a string:
<list-view :avatar="pictures"></list-view>
But I think Vue thinks I am trying to call a method because I am getting these warnings:
[Vue warn]: Property or method "pictures" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
[Vue warn]: Invalid prop: type check failed for prop "avatar". Expected String, got Undefined.
How can I pass "pictures" as a string?
Vue.component('list-view', {
props: {
avatar: { type: String, required: true },
},
template: `<div>{{ avatar }}</div>`,
});
var app = new Vue({ el: '#app' });
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<list-view :avatar="pictures" ></list-view>
</div>
Right now, Vue is trying to find a variable named pictures to pass as the property value to the child component.
If you want to specify a string value in that inline expression, you can wrap the value in quotes to make it a string:
<list-view :avatar="'pictures'"></list-view>
Alternately, as #Zunnii answered below, if the value being passed is really just a static string, you can simply omit the v-bind colon shorthand:
<list-view avatar="pictures"></list-view>
This way, the avatar prop of the child component will be assigned the string value "pictures".
If you really want to pass static string, you can just pass string directly without v-bind
<div id="app">
<greeting text="world"></greeting>
</div>
then in JS file
Vue.component('greeting', {
props: ['text'],
template: '<h1>Hello {{ text }}!</h1>'
});
var vm = new Vue({
el: '#app'
});
JSFiddle => https://jsfiddle.net/dpLp4jk8/
:avatar="pictures" //vue treat pictures as a variable or computed properties
By default vue treat pictures as a variable or computed properties and vue search locally and doesn't find and throw an error. So what you have to do is tell vue externally that it is a string not a variable or computed properties. So enclose in quotes.
:avatar="'pictures'" // I guess it will work
In laravel we can pass string as shown below
function authUserCurrency(){
return '$';
}
<service :authusercurrency="'{!!authUserCurrency()!!}'"></services>
Vue.js Template
props:{authusercurrency:String},
OR
props:['authUserCurrency'],
In my case I needed to pass static string along with dynamic property value.
So did as below.
<home-card :title="'By' + post.author.name "></home-card>

In Vue 2 after passing props v-for does not get updated after items are removed

If I pass an array of objects using props in Vue 2 and on this array I use the v-for directive, view does not get updated if one of the array elements get removed.
This seems to work only if the v-for elements are declared as data, but my component needs to receive props...
In the example below you can see that the elements in the services array are indeed removed, but the v-for isn't triggered.
I'm pretty sure I'm doing here something wrong...
Vue.component('location-service-list', {
props: ['services'],
template: '<div>{{ services }}<div v-for="(service, index) in services">{{ service.id }} - {{ service.name }} <a #click.prevent="remove(index)">remove</a></div></div>',
methods: {
remove(index) {
this.services.splice(index, 1);
console.log(this.services);
},
}
});
const app = window.app = new Vue({
el: '#admin-app'
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.0.8/vue.js"></script>
<div id="admin-app">
<location-service-list :services='[{"id":1,"name":"Test 1"},{"id":2,"name":"Test 2"}]'></location-service-list>
</div>
Try defining your servicesList inside the root component as follows:
const app = window.app = new Vue({
el: '#admin-app',
data: {
servicesList: [{"id":1,"name":"Test 1"},{"id":2,"name":"Test 2"}]
}
});
And your template as:
<div id="admin-app">
<location-service-list :services='servicesList'></location-service-list>
</div>
Now it will work alright without any issues. It was not working earlier because you passed it as a constant / immutable object (JSON string in the parent template which always evaluates to the same value whenever the parent template re-renders).
Technically you are not supposed to change objects passed via props in the child component. If you do the same to a string value that is passed via props, you will get an error message like:
[Vue warn]: Avoid mutating a prop directly...
To process this remove action from parent component, you may refer to the answer under this question: Delete a Vue child component
The jsFiddle in that answer provides a way to send an event from child component to parent component, so that the appropriate child component can be deleted.

Passing entire data object as props

Is it possible in Vue to pass the whole data object as props?
For example
Vue.component('comp', {
props: ['allData'],
template: '<div>{{allData.msg}}</div>'
})
new Vue({
el: "#test",
data: {
msg: "Hello"
}
})
In my view:
<div id="test">
<comp :allData="data"></comp>
</div>
It's possible like this:
<div id="test">
<comp :allData="$data"></comp>
</div>
However, mutating allData in the component will affect the parent's state since it's an object. See the warning from Vue 1.0 docs below:
Note that if the prop being passed down is an Object or an Array, it is passed by reference. Mutating the Object or Array itself inside the child will affect parent state, regardless of the binding type you are using.
and Vue 2.0 docs
Note that objects and arrays in JavaScript are passed by reference, so if the prop is an array or object, mutating the object or array itself inside the child will affect parent state.
You can access the whole object via $data, and pass it.
But it's usually not the best idea to mess with it.