Vue JS - How to get props value in parent component - javascript

I have three components component-1, component-2, and an App component, I pass a Boolean props from component-1 to component-2 then using #click event I change the props value from true to false and vice versa
App.vue
<template>
<div id="app">
<img alt="Vue logo" src="./assets/logo.png" width="20%" />
<component1 />
</div>
</template>
component-1.vue
<template>
<div>
<component2 :have-banner="true" />
</div>
</template>
<script>
import component2 from "./component-2";
export default {
components: {
component2,
},
};
</script>
component-2.vue
<template>
<div>
<button #click="AssignBanner = !AssignBanner">Click me</button>
<p>{{ AssignBanner }}</p>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
haveBanner: Boolean,
},
data() {
return {
AssignBanner: this.haveBanner,
};
},
};
</script>
I want to get the value of the props in component-1, that is, I want to track the changing value in component-1, since I want to write some logic, but I can’t keep track of the value in component-1.
You can see the given code in codesandbox

Looks like you want to achieve two-way binding for the prop haveBanner. You can achieve this with the .sync modifier if you are using Vue 2.3+.
component-1.vue
<template>
<div>
<component2 :have-banner.sync="haveBanner" />
</div>
</template>
<script>
import component2 from "./component-2";
export default {
components: {
component2,
},
data() {
return {
haveBanner: true,
}
},
};
</script>
component-2.vue
<template>
<div>
<button #click="assignBanner = !assignBanner">Click me</button>
<p>{{ assignBanner }}</p>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
haveBanner: Boolean,
},
data() {
return {
assignBanner: this.haveBanner,
};
},
watch: {
assignBanner(value) {
// propagate to parent component
this.$emit('update:haveBanner', value)
},
},
};
</script>

Related

VueJS: Pass any unknown props to child component just like v-bind="$props"

I want to receive any props bind by the parent component into the child component without mentioning in props:[] because I don't know which props will bind.
Parent component
<template>
<div id="parentComponent">
<child-component v-bind="anyPropsToPass"></child-component>
</div>
</template>
<script>
import ChildComponent from './components/child-component/child-component'
export default {
name: 'app',
components: {
ChildComponent
},
data () {
return {
anyPropsToPass: {
name: 'John',
last_name: 'Doe',
age: '29',
}
}
}
}
</script>
Child component
<template>
<div>
<p>I am {{name}} {{last_name}} and i am {{age}} old</p>
<another-child v-bind="$props"></another-child> <!-- another child here and we pass all props -->
</div>
</template>
<script>
import AnotherChild from "../another-child/another-child";
export default {
components: {AnotherChild},
props: [], // I know if I mentioned props here I can receive but it's unknown, I
//just want to pass it down until it received in right component to use
created() {
console.log("Props", this.$props);
// Gets null
// Expected : anyPropsToPass Object
}
}
</script>
If props are mentioned in the props of child then it works but there should be some way to know which are the props passed or bind from the parent even though we are not interested in child.
e.g. Working fine!
Child component
<template>
<div>
<p>I am {{name}} {{last_name}} and i am {{age}} old</p>
<another-child v-bind="$props"></another-child>
</div>
</template>
<script>
import AnotherChild from "../another-child/another-child";
export default {
components: {AnotherChild},
props: ['name', 'last_name'],
created() {
console.log("Props", this.$props);
// Gets expected props here
}
}
</script>
You can use this.$attrs to get all v-bind props, including undeclared props.
<!-- parent -->
<template>
<div id="app">
<ChildComp :prop1="114" prop2="514" class="homo" />
</div>
</template>
<script>
// child component
export default {
created() {
console.log('all props: ', this.$attrs) // { "prop1": 114, "prop2": "514" }
}
}
</script>
This should be possible with this.$attrs
Child.vue:
<template>
<div>
<p>I am {{getValue('name')}} {{getValue('last_name')}} and i am {{getValue('age')}} old</p>
</div>
</template>
<script>
export default {
methods:{
getValue(propertyName){
return this.$attrs[propertyName];
}
},
components: {},
props: [],
created() {
console.log("Props", this.$attrs);
}
}
</script>
Drilling down the props is a bad pattern, which can lead to inconsistency, try to use the provide/inject pattern to pass data from grandparent component to the grandchild one :
<template>
<div id="parentComponent">
<child-component></child-component>
</div>
</template>
<script>
import ChildComponent from './components/child-component/child-component'
export default {
name: 'app',
components: {
ChildComponent
},
provide () {
return {
user: this.user
}
},
data () {
return {
user: {
name: 'John',
last_name: 'Doe',
age: '29',
}
}
}
}
</script>
in grandchild component :
<template>
<div>
<p>I am {{user.name}} {{user.last_name}} and i am {{user.age}} old</p>
<another-child></another-child>
</div>
</template>
<script>
import AnotherChild from "../another-child/another-child";
export default {
components: {AnotherChild},
inject:['user'],
created() {
console.log("injected user", this.user);
}
}
</script

how to edit the data value of the parent component from the child component in vue js

I want to show or hide the item by clicking the button or clicking the item itself, for example:
<template>
<div>
<button #click="show?show = false:show = true">
{{show?"Hide":"Show"}}
</button>
<div #click="show?show = false:show = true" v-if="show">
Vue Js - click here to Hide
</div>
</div>
</template>
<script>
export default {
data() {
return {
show: null,
};
}
};
</script>
but i want to import the item from another component, so i do this:
the parent component:
<template>
<div>
<button #click="show?show = false:show = true">
{{show?"Hide":"Show"}}
</button>
<item :show="show" />
</div>
</template>
<script>
import item from "item.vue"
export default {
components: {
item
},
data() {
return {
show: null,
};
}
};
</script>
the child component:
<template>
<div #click="show?show = false:show = true" v-if="show">
Vue Js - click here to Hide
</div>
</template>
<script>
export default {
props: {
show: Boolean,
}
};
</script>
but of course, it doesn't work well.
When i click on the item it disappears but the show value in the parent component doesn't change and I get an error saying 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: "show".
so how to edit the data value of the parent component from the child component?
(I use Vue 2.6.14)
The solution is to use event emitting (Youtube Tutorial) to send updated data up to the parent component from the child component.
so the code becomes like this:
the parent component:
<template>
<div>
<button #click="show?show = false:show = true">
{{show?"Hide":"Show"}}
</button>
<item :show="show" #state="update($event)" />
</div>
</template>
<script>
import item from "item.vue"
export default {
components: {
item
},
data() {
return {
show: null,
};
},
methods: {
update(value) {
this.show = value;
}
}
};
</script>
the child component:
<template>
<div #click="action" v-if="show">
Vue Js - click here to Hide
</div>
</template>
<script>
export default {
props: {
show: Boolean,
},
methods: {
state() {
if (this.show) {
return false;
} else {
return true;
}
},
action() {
this.$emit("state", this.state())
}
}
};
</script>
Thank you #D.Schaller for helping

vue - How a normal link could act like <route-link>?

I have two pages routed:
Home > Results
Inside 'Home', I have a button/link which is not in <route-view/> and I want this button to redirect to Results.vue passing a parameter.
This parameter named activeTab, has to open the desired vue-tabs, which I can't accomplish, because it's getting nothing from variable:
code:
Home.vue
<div class="row">
<Notes refName="Relationship" />
<Notes refName="Support" />
</div>
...
<script>
import Notes from '#/components/Monthlynotes.vue'
export default {
name: 'home',
components: {
Notes
},
</script>
/components/Monthlynotes.vue
<b-card>
<p class="card-text">{{ refName }}</p>
<b-link class="right" :activeTab="refName" href="/results">More</b-link>
</b-card>
...
<script>
export default {
props: {
refName: String,
},
</script>
Results.vue
<vue-tabs type="pills" v-model="tabName">
<v-tab title="Relationship">
<RelDT msg="Results view"/>
</v-tab>
<v-tab title="Support">
<SupDT msg="Results view"/>
</v-tab>
</vue-tabs>
...
<script>
import RelDT from '#/components/DataTable.rel.vue'
import SupDT from '#/components/DataTable.sup.vue'
export default {
name: 'results',
props: {
activeTab: String
},
components:
{
RelDT,
SupDT,
},
data() {
return {
tabName: activeTab
}
}
}
</script>
App
<router-link :to="{name:'results', param:{activeTab}}">Results</router-link>
How can I make this <b-link> route if it was a <route-link />?
Even the b-link component supports the :to property. To be found here
The value of the property will be passed to router.push().
<b-link :to="{name:'results', param:{activeTab}}">Redirect me</b-link>

how to bind v-model to child component that contains input

I have some components that look like this.
<template>
<q-layout>
<v-input v-model="something" />
</q-layout>
</template>
<script>
import { QLayout } from 'quasar'
import { Input } from 'vedana'
export default {
name: 'index',
components: {
QLayout,
Input
},
data () {
return {
something: ''
}
}
}
this v-input component looks like this:
<template>
<input
:type="type ? type : 'text'"
class="v-input"/>
</template>
<script>
export default {
props: ['type'],
name: 'v-input'
}
</script>
When I enter data into the input something does not bind to whatever is in the value of the input that is inside of v-input.
How do I achieve this?
To enable the use of v-model the inner component must take a value property.
Bind the value to the inner <input> using :value, not v-model (this would mutate the prop coming from the parent). And when the inner <input> is edited, emit an input event for the parent, to update its value (input event will update the variable the parent has on v-model).
Also, if you have a default value for the type prop, declare it in props, not in the template.
Here's how your code should be
<template>
<input
:type="type"
:value="value"
#input="$emit('input', $event.target.value)"
class="v-input" />
</template>
<script>
export default {
props: {
type: {default() { return 'text'; }},
value: {} // you can also add more restrictions here
},
name: 'v-input'
}
</script>
Info about what props can have: Components / Passing data With Props.
Demo below.
Vue.component('v-input', {
template: '#v-input-template',
props: {
type: {default() { return 'text'; }},
value: {} // you can also add more restrictions here
},
name: 'v-input'
});
new Vue({
el: '#app',
data: {
something: "I'm something"
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<p>Parent something: {{ something }}</p>
<hr>
Child: <v-input v-model="something" />
</div>
<template id="v-input-template">
<input
:type="type"
:value="value"
#input="$emit('input', $event.target.value)"
class="v-input" />
</template>
https://v2.vuejs.org/v2/guide/components.html#sync-Modifier
<template>
<q-layout>
<v-input :value.sync="something" />
</q-layout>
</template>
<template>
<input
:type="type ? type : 'text'"
v-model="inputVal"
class="v-input"/>
</template>
<script>
export default {
props: ['type', 'value'],
name: 'v-input',
data:function(){
return {
inputVal: ''
};
},
watch:{
value: function(newValue){
this.$emit('update:value', newValue)
}
}
}
</script>
You need to pass your value to the input component using the .sync modifier so the changes will sync back to the parent.

Vue.js pass callback through a prop.

I've got a component that looks like this:
<template>
<div>
<pagination class="center" :pagination="pagination" :callback="loadData" :options="paginationOptions"></pagination>
</div>
</template>
<script>
import Pagination from 'vue-bootstrap-pagination';
export default {
components: { Pagination },
props: ['pagination', 'loadData'],
data() {
return {
paginationOptions: {
offset: 5,
previousText: 'Terug',
nextText: 'Volgende',
alwaysShowPrevNext: false
}
}
}
}
</script>
In another component I use that ^:
<template>
<pagination :pagination="pagination" :callback="loadData" :options="paginationOptions"></pagination>
</template>
<script>
export default {
loadData() {
this.fetchMessages(this.pagination.current_page);
}
//fetchMessages
}
</script>
But I receive the error:
Invalid prop: type check failed for prop "callback". Expected Function, got Undefined.
(found in component <pagination>)
Is it not possible in Vue.js 2.0 to pass a callback?
I think your second component may not be written accurately, your loadData callback should be in methods:
<template>
<pagination :pagination="pagination" :callback="loadData" :options="paginationOptions"></pagination>
</template>
<script>
export default {
methods: {
loadData() {
this.fetchMessages(this.pagination.current_page);
}
}
}
</script>
App.vue
<template>
<div>
<pre>{{ request }}</pre>
<pagination #callback="loadData"></pagination>
</div>
</template>
<script>
import Pagination from './Pagination.vue'
export default {
name: 'App',
components: { Pagination },
data() {
return {
request: {}
}
},
methods: {
loadData(request) {
this.request = request
}
}
}
</script>
Pagination.vue:
<template>
<div>
<button #click="$emit('callback', { currentPage: 10, whatEver: 7 })">call it back</button>
</div>
</template>
<script>
export default {
name: 'Pagination'
}
</script>
https://codesandbox.io/embed/8xyy4m9kq8

Categories