Pass props between two components in vue router - javascript

I know there are a lot of questions who answer my question but I can't get single of them run. I want to pass a string from component A to component B by using vue router. In the following example how can I output the value of 'name' from component A in component B.
Component A template:
<form >
<input placeholder="type your name" v-model="name">
<button v-on:click="sayHello" type="submit" >Submit</button>
<script>
sayHello() {
this.$router.push({ name: 'ComponentB', params: {
name: '{this.name}'}, props:true });
}
</script>
Component B:
<template>
<div class="container">
<h1> Hello {{$route.params.name}}!</h1>
</div>
<script>
export default {
data(){
return{
props: ['name']
}
}
}
</script>
router
{
path: '/ComponentB',
name: "CompB",
component: CompB,
props: true
}
Still don't know how I can achive this without using url params.
If I change the path of CompB to ComponentsB/:name it works.

Your router properties are in this.$route which you have declared in Component A
So in your case:
Component A:
<script>
sayHello() {
this.$router.push({ name: 'ComponentB', query: {
name: this.name }, props:true });
}
</script>
Component B:
<template>
<div class="container">
<h1> Hello {{ $route.query.name }}!</h1>
</div>
</template>
I would suggest reading https://router.vuejs.org/guide/essentials/passing-props.html to decouple your component using props instead.

Related

How to Access Component HTML output in Vue outside the template tag?

I know that we can simply show the component output with <ComponentName/> inside the template,
but how do we access ComponentName html output outside the template like in data, methods, or during mounted
e.g. components/Test.vue
<template>
<div>I'm a test</div>
</template>
in another vue file pages/ViewTest.vue
import Test from '~/components/Test.vue'
export default {
components: {Test},
data() {
return {
test: Test
}
},
mounted: function() {
console.log( Test ) // Output is Test Component Object
console.log( this.test ) // Output is Test Component Object
}
}
The object from console log output seems to contain a lot of information and I can even see a render property from the object although when I try console.log( Test.render() ) its giving me error
So My question is how can I get the <div>I'm a test</div> from outside the template?
Appreciate any help or guidance
EDIT
I'm using vue-material-design-icons package for generating different svg icons,
and I can use it like below
<template>
<MapMarkerRadius/>
</template>
<script>
import MapMarkerRadius from 'vue-material-design-icons/MapMarkerRadius'
export default {
components: {MapMarkerRadius}
}
</script>
Now here's my main issue,
I have this component that generates an html
<template>
<div :class="'card'">
<div v-if="title" :class="'card-title'">
{{ title }}
</div>
<div :class="'card-content'">
<slot />
</div>
</div>
</template>
<script>
export default {
name: 'card',
props: {
title: {},
}
};
</script>
Then I'm using that card component like this on a different vue file
<template>
<card :title="'Title ' + MapMarkerRadius">
Test Content
</card>
</template>
<script>
import card from '~/components/Card'
import MapMarkerRadius from 'vue-material-design-icons/MapMarkerRadius'
export default {
components: {card, MapMarkerRadius}
};
</script>
and my problem here is that the output of the card title is Title [object]
Try to use ref in the root of the Test component like :
<template>
<div ref="test">I'm a test</div>
</template>
in other component do :
mounted: function() {
console.log( this.$refs.test )
}
No need to import the component.
The repo that you are using are single-file components that generates html through a single tag, so using
import MapMarkerRadius from 'vue-material-design-icons/MapMarkerRadius'
will enable you to use it in template as <map-marker-radius/>
That is why appending the string title and an object like "My Icon"+MapMarkerRadius will return the literal [object] as you've seen: "My Icon [object]"
You have 3 options to go through what you want:
Search for other repos that enable you to use easily material icons in other means;
You have access to the card component? You can use the class names of this repo instead rather than the svg version or the component itself: https://github.com/robcresswell/vue-material-design-icons/issues/12, add the class names to the props and add it to your component:
<card :title="'Title'" :icon_class="map-marker-radius">
Test Content
</card>
<div v-if="title" :class="'card-title'">
{{ title }} <div :class="icon_class"></div>
</div>
props: {
title: {},
icon_class: '',
}
You can use the MapMarkerRadius component directly in card component but only appears when you pass a certain criteria on the card component, such like:
main.vue
<template>
<card :title="'Title'" :icon="true" :icon_typename="'map-marker-radius'">
Test Content
</card>
</template>
<script>
import card from '~/components/Card'
export default {
components: {card}
};
</script>
with icon_typename as any name/keyword you'd like to use.
card.vue
<template>
<div :class="'card'">
<div v-if="title" :class="'card-title'">
{{ title }} <span v-if="icon_mmr"><map-marker-radius/></span>
</div>
<div :class="'card-content'">
<slot />
</div>
</div>
</template>
<script>
import MapMarkerRadius from 'vue-material-design-icons/MapMarkerRadius'
export default {
name: 'card',
props: {
title: {},
icon: { default: false },
icon_typename: '',
icon_mmr: false,
},
mounted(){
if (this.icon && this.icon_typename === 'map-marker-radius') this.icon_mmr = true
},
components: { MapMarkerRadius },
};
</script>
The code is far from perfect but you can go from there to optimize further.

Creating/Destroying the Vue Component based on text search

I have the following in App.vue
<template>
<div id="app">
<input type="text" v-model="term">
<hello-world text="Button 1" v-if="term === ''"></hello-world>
<hello-world v-else text="Button 2"></hello-world>
</div>
</template>
<script>
import HelloWorld from '#/components/HelloWorld'
export default {
name: 'app',
data() {
return {
term: ''
}
},
components: {
HelloWorld
}
}
</script>
And here's the HelloWorld.vue:
<template>
<div>
<button>{{ text }}</button>
</div>
</template>
<script>
export default {
props: {
text: String
},
created() {
console.log('Created')
},
destroyed() {
console.log('Destroyed')
}
}
</script>
So, when I type something the first component should be destroyed and the second component should be created. However, nothing like that happens. The component neither gets destroyed nor gets created.
It's as if the v-if didn't trigger the created() & destroyed() function. Please help me with this.
Vue uses virtual dom approach. So, it is comparing the virtual tree and it is not identifying changes on structure (oldNode.type === newNode.type). When it occurs, Vue updates the same component instead of destroying the old node and creating a new one.
Try to force Vue to detect virtual tree changes avoiding use siblings with the same tag name and controlled by v-if directive.
Reference:
https://medium.com/#deathmood/how-to-write-your-own-virtual-dom-ee74acc13060
Vue.component('hello-world', {
props: {
text: String
},
created() {
console.log('Created')
},
destroyed() {
console.log('Destroyed')
},
template: "<button>{{ text }}</button>"
});
var app = new Vue({
el: "#app",
data() {
return {
term: ''
}
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<input type="text" v-model="term">
<span><hello-world v-if="!term" text="Button 1"></hello-world></span>
<span><hello-world v-if="term" text="Button 2"></hello-world></span>
</div>
I am not sure what you are trying to achieve, but testing your code logs created from both components
https://codesandbox.io/s/8l0j43zy89
Since you are actually showing conditionally the same component, I don't think it will get destroyed.

How to pass params from div to single page component in Vue.js?

I have such code on different pages:
<div id="contact-us" class="section md-padding bg-grey">
<div id="contact"></div>
<script src="/dist/build.js"></script>
</div>
I have main.js:
import Vue from 'vue'
import Contact from './Contact.vue'
new Vue({
el: '#contact',
render: h => h(Contact)
})
And Contact.vue with a template
I want to know from which page component was used. So I need to pass param from div like <div id="contact" page="main"></div> . How can I implement this?
How to pass params from div to single page component in Vue.js?
You can't pass params from a div since it's a html tag and a not custom component, you should define your own component that accepts the properties you want to pass.
So first you should define your component and define the property is allow to receive, then you use your component, take a look to the below example, and you may find more information about passing props here.
Vue.component('your-component', {
props: ['property'],
template: '<h3>{{ property }}</h3>'
})
new Vue({
el: '#app'
})
<script src="https://cdn.jsdelivr.net/npm/vue#2.5.16/dist/vue.js"></script>
<div id="app">
<your-component :property="'Hello props'" />
</div>
Example using Single File Component structure.
Parent component:
<template>
<ChildComponent :property="propValue" />
</template>
<script>
import childComponent from './childComponent.vue';
export default {
components: {
ChildComponent: childComponent
},
data() {
return {
propValue: 'Hello prop'
}
}
}
</script>
Children component:
<template>
<h3>{{ property }}</h3>
</template>
<script>
export default {
props: ['property'] // You can add more properties separeted by commas
}
</script>

VueJS pass all props to child component

I am quite new to VueJS. In react you can easily use rest params for passing props to children. Is there a similar pattern in Vue?
Consider this parent component that has a few children components:
<template>
<header class="primary-nav">
<search-bar :config="searchBar"><search-bar>
// ^^^^ this becomes the key on the props for SearchBar
header>
</template>
export default {
data(){
return {
... a few components ...
searchBar : {
model: '',
name: 'search-bar',
placeholder: 'Search Away',
required:true,
another: true
andanother: true,
andonemoreanotherone:true,
thiscouldbehundredsofprops:true
}
}
}
}
<template>
<div :class="name">
<form action="/s" method="post">
<input type="checkbox">
<label :for="config.name" class="icon-search">{{config.label}}</label>
<text-input :config="computedTextInputProps"></text-input>
//^^^^ and so on. I don't like this config-dot property structure.
</form>
</div>
</template>
export default {
props: {
name: String,
required: Boolean,
placeholder: String,
model: String,
},
computed: {
computedtextInputProps(){
return justThePropsNeededForTheTextInput
}
}
...
What I don't like is that the props are named-spaced with the key config, or any other arbitrary key. The text-input component ( not shown ) is a glorified input field that can take a number of attributes. I could flatten the props when the component is created, but is that generally a good idea?
I am surprised this question hasn't been asked before.
Thanks.
Edit: 10-06-2017
Related: https://github.com/vuejs/vue/issues/4962
Parent component
<template>
<div id="app">
<child-component v-bind="propsToPass"></child-component>
</div>
</template>
<script>
import ChildComponent from './components/child-component/child-component'
export default {
name: 'app',
components: {
ChildComponent
},
data () {
return {
propsToPass: {
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: ['name', 'last_name', 'age'],
}
</script>
Another Child component inside the above component
<template>
<h1>I am saying it again: I am {{name}} {{last_name}} and i am {{age}} old</h1>
</template>
<script>
export default {
props: ['name', 'last_name', 'age']
}
</script>
Parent component
You can pass as many props as you want to child component
Child Component
Once you are satisfied with all the props, then you can use v-bind="$props" inside your child component to retrieve all the props.
Final Result:
Done:)
In Vue 2.6+, in addition to attributes passing,
events/listeners can be also passed to child components.
Parent component
<template>
<div>
<Button #click="onClick">Click here</Button>
</div>
</template>
<script>
import Button from "./Button.vue";
export default {
methods:{
onClick(evt) {
// handle click event here
}
}
}
</script>
Child component
Button.vue
<template>
<button v-bind="$attrs" v-on="$listeners">
<slot />
</button>
</template>
Update [2021-08-14]:
In Vue 3, $listeners will be removed. $attrs will have both listeners and attributes passing to the child component.
Child component
Button.vue
<template>
<button v-bind="$attrs">
<slot />
</button>
</template>
Migration Guide
In the child component, you can bind $attrs and $props.
<template>
<button v-bind="[$props,$attrs]" v-on="$listeners">
<slot />
</button>
</template>
v-bind="[$attrs, $props]"
This one works for me. $attrs doesn't include props.

How do I use data from Vue.js child component within parent component?

I have a form component where I use a child component. I want to use data from the child component within the parent.
My component in html:
<candidates-form endpoint='/candidates/create' buttontext='Add Candidate'></candidates-form>
Then here is the Vue instance:
CandidatesForm.vue
<template>
<div class='row'>
<div class='form-group'>
<label>Name:</label>
<input type='text' class='form-control' v-model='name'>
</div>
<div class='form-group'>
<location-input></location-input>
</div>
<button class='btn btn-primary'>{{buttontext}}</button>
</div>
</template>
<script>
export default {
data() {
return {}
},
props: ['endpoint', 'buttontext'],
ready() {}
}
</script>
I utilize the locationInput component in there and it renders to the screen nicely. That component implements Google Maps typeahead functionality for the input field and looks like this:
LocationInput.vue
<template>
<place-input
:place.sync="placeInput.place"
:types.sync="placeInput.types"
:component-restrictions.sync="placeInput.restrictions"
class='form-control'
label='Location: '
name='location'
></place-input>
<pre>{{ placeInput.place | json }}</pre>
</template>
<script>
import { PlaceInput, Map } from 'vue-google-maps'
export default {
data() {
return {
placeInput: {
place: {
name: ''
},
types: [],
restrictions: {'country': 'usa'}
}
}
},
props: ['location'],
components: {
PlaceInput
},
ready() {
}
}
</script>
<style>
label { display: block; }
</style>
I want to submit the name value and information from placeInput.place to the server.
I register both components in my main app file like so:
Vue.component('locationInput', require('./components/LocationInput.vue'));
Vue.component('candidatesForm', require('./components/CandidatesForm.vue'));
const app = new Vue({
el: 'body'
});
How do I pass the placeInput.place data from location-input component to candidates-form component?
I want to send the placeInput.place and name data from the candidates-form component to the server, most likely using vue-resource.
Hey no need for a store or Vuex. Pass data using props!
Final solution:
Blade template:
#extends('layouts.app')
#section('content')
<div class='col-md-6 col-md-offset-3'>
<h1>Add New Candidate</h1>
<hr>
<candidates-form endpoint='/candidates/create' buttontext='Add Candidate'></candidates-form>
</div>
#stop
Parent Vue component:
<template>
<div>
<div class='form-group'>
<label>Name:</label>
<input type='text' class='form-control' v-model='name'>
</div>
<div class='form-group'>
<location-input :location="locationData"></location-input>
</div>
<button class='btn btn-primary'>{{buttontext}}</button>
<pre>{{ locationData | json }}</pre>
</div>
</template>
<script>
export default {
data() {
return {
locationData: {
place: {
name: ''
},
types: [],
restrictions: {'country': 'usa'}
}
}
},
props: ['endpoint', 'buttontext']
}
</script>
Child Vue component:
<template>
<place-input
:place.sync="location.place"
:types.sync="location.types"
:component-restrictions.sync="location.restrictions"
class='form-control'
label='Location: '
name='location'
></place-input>
</template>
<script>
import { PlaceInput, Map } from 'vue-google-maps'
export default {
props: ['location'],
components: {
PlaceInput
}
}
</script>
<style>
label { display: block; }
</style>
Aaaaand the overall app.js file (this is within a Laravel 5.3 app btw)
import { load } from 'vue-google-maps'
load({
key: '<API_KEY>',
v: '3.24', // Google Maps API version
libraries: 'places', // for places input
});
Vue.component('locationInput', require('./components/LocationInput.vue'));
Vue.component('candidatesForm', require('./components/CandidatesForm.vue'));
Vue.component('company-list', require('./components/CompanyList.vue'));
const app = new Vue({
el: 'body'
});
This article from alligator.io also helped simplify things for me also. I was overthinking it!
Shout out to #GuillaumeLeclerc for the vue-google-maps component: https://github.com/GuillaumeLeclerc/vue-google-maps
Check out my answer here VueJS access child component's data from parent , pretty same questions.
If you are working with large scale application, the best option is to use Vuex, it would save you from a lot of troubles.
Otherwise if it's not a bug app, then you can go with my approach, or using Event Bus.
I would recommend the data store approach which is in the VueJS documentation here: https://v2.vuejs.org/v2/guide/state-management.html
Essentially you would have a store.js file that exports a data object for your application. This data object is shared with all of your components.
From the page linked above:
const sourceOfTruth = {}
const vmA = new Vue({
data: sourceOfTruth
})
const vmB = new Vue({
data: sourceOfTruth
})
and if you need components to have private state along with shared state:
var vmA = new Vue({
data: {
privateState: {},
sharedState: store.state
}
})
var vmB = new Vue({
data: {
privateState: {},
sharedState: store.state
}
})

Categories

Resources