Reputation: 575
I'm using @vue/vli 4.1.1 with single file components. A simplified structure of my application looks like this:
src
├── App.vue
├── assets
├── components
│ ├── PrintRecipe.vue
│ └── Recipe.vue
└── main.js
Inside the Recipe component, I would like to include a print button and its functionality, as implemented in the PrintRecipe component. When the button is pressed, the #recipe
element from the Recipe component should be printed.
Here is a dummy version of Recipe.vue:
<template>
<div>
<div id="other-content">
...
</div>
<div id="recipe">
...
</div>
<PrintRecipe/>
</div>
</template>
<script>
import PrintRecipe from './PrintRecipe.vue'
export default {
components: {
PrintRecipe
},
...
}
</script>
and this is a dummy version of PrintRecipe.vue:
<template>
<div>
<button @click="printRecipe">Print recipe</button>
</div>
</template>
<script>
import { Printd } from 'printd';
export default {
mounted() {
this.d = new Printd()
},
methods: {
printRecipe () {
this.d.print( this.$el, [this.cssText])
}
}
}
</script>
Currently this opens a print view, where the document to be printed only contains "Print recipe".
Is there a way that I can access the elements from the template of Recipe.vue in order to be able to print them?
I have done some research and so far I only see information on how to access data from the parent component, not elements.
Upvotes: 1
Views: 2517
Reputation: 222910
Either Recipe
parent component can handle the interaction, PrintRecipe
becomes presentation component:
Recipe.vue
...
<div ref="recipe">...</div>
<PrintRecipe v-on:print="printRecipe" ref="printRecipe" />
...
...
methods: {
printRecipe() {
this.d.print( this.$refs.recipe.$el, [this.cssText])
}
}
...
PrintRecipe.vue
...
methods: {
printRecipe() {
this.$emit('print');
}
}
...
Or PrintRecipe
child get receive necessary data from Recipe
parent to handle the interaction, such as a reference to DOM element:
...
<div ref="recipe">...</div>
<PrintRecipe />
...
PrintRecipe.vue
...
methods: {
printRecipe() {
this.d.print(this.$parent.$refs.recipe.$el, [this.cssText])
}
}
...
Whether components have to communicate through $parent
, props or event bus depends on the case.
Upvotes: 2
Reputation: 1544
Send a custom Vue event on clicking the button back to the Recipe.vue
component
export default {
mounted() {
this.d = new Printd()
},
methods: {
printRecipe () {
this.$emit('recipeClicked'); // custom event
}
}
}
From the Recipe.vue
you can receive the event and write your logic to show the content. Here is my logic;
<template>
<div>
<div id="other-content">
...
</div>
<div id="recipe" v-show="showContent">
...
</div>
<PrintRecipe @recipeClicked="showContent=true"/>
</div>
</template>
<script>
import PrintRecipe from './PrintRecipe.vue'
export default {
components: {
PrintRecipe
},
data:()=>{
return {
showContent:false
}
}
...
}
</script>
Upvotes: 1
Reputation: 575
As it turns out, Vue slots was what I needed.
The PrintRecipe component should have a slot, to where the element that should be printed is inserted.
PrintRecipe.vue
<template>
<div>
<slot/>
<button @click="printRecipe">Print recipe</button>
</div>
</template>
<script>
import { Printd } from 'printd';
export default {
mounted() {
this.d = new Printd()
},
methods: {
printRecipe () {
this.d.print( this.$el, [this.cssText])
}
}
}
</script>
The insertion is done inside Recipe.vue by expanding the PrintRecipe
element in the following way:
<template>
<div>
<div id="other-content">
...
</div>
<PrintRecipe>
<div id="recipe">
...
</div>
</PrintRecipe>
</div>
</template>
<script>
import PrintRecipe from './PrintRecipe.vue'
export default {
components: {
PrintRecipe
},
...
}
</script>
Upvotes: 2