I have a vue 3 script setup component (composition api), and another file (for printing) that has some logic I want to inject into my main component.
My main file looks like
<template>
<v-app>
<v-main>
<v-table-mobile :headers="headers" :rows="rows" :items-per-page="itemsPerPage" no-data-text="No data to display" pagination-text="{0} - {1} of {2}" :sort-by="sortBy" :group-by="groupBy">
<template #item.a="{ item }">
{{ item.a }}
</template>
</v-table-mobile>
</v-main>
</v-app>
</template>
<script lang="ts">
import * as Print from './utils/print';
export Print;
</script>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import VTableMobile, { TableHeader, TableColumnSort, TableColumnGroup } from './components/MobileTable.vue';
const sortBy = ref({key:'a', order:'asc'} as TableColumnSort);
//const sortBy = ref({} as TableColumnSort);
const groupBy = ref({key:'a', expanded:[1] as any[]} as TableColumnGroup);
//const groupBy = ref({} as TableColumnGroup);
const itemsPerPage = ref(3);
const headers = ref([
{ key: "a", title: "a" },
{ key: "b", title: "b" }
] as TableHeader[]);
const rows = ref([
{ a:1, b:2 },
{ a:3, b:4 },
{ a:1, b:6 },
{ a:7, b:8 },
{ a:9, b:10 },
{ a:11, b:12 },
{ a:13, b:14 },
{ a:15, b:16 },
{ a:17, b:18 },
{ a:19, b:20 }
]);
onMounted(() => {
setTimeout(() => {
rows.value = rows.value.concat([ { a:21, b:22 } ]);
itemsPerPage.value = 4;
}, 3000);
})
</script>
The print file looks like
import { Ref, nextTick } from 'vue';
export default {
mounted() {
alert(1);
},
beforeUnmount() {
alert(2);
},
methods: {
async Print(print_mode:Ref<boolean>) {
print_mode.value = true;
await nextTick();
window.print();
},
DisablePrintMode(print_mode:Ref<boolean>) {
print_mode.value = false;
}
}
}
I want to add the mounted, beforeMount and the 2 methods to be injected into my main component. But this is not working. How can I fix it?
Right now I get an error
Declaration or statement expected.ts(1128) where the export Print is.
Thanks
Vue composables are designed for this.
Composables | VueJS
<!-- print file -->
<script setup>
import { onMounted, onUnmounted, nextTick } from 'vue'
/*
Other logic code
*/
async function Print(print_mode) {
print_mode.value = true;
await nextTick();
window.print();
},
function DisablePrintMode(print_mode) {
print_mode.value = false;
}
onMounted(() => {
// content of this hook
})
onUnmounted(() => {
// content of this hook
})
</script>
<!-- main file -->
<script setup>
import { usePrint } from './print.js'
const printFunc = usePrint()
</script>
I have written a sample code, you can take this reference to your code
Related
I made two components and tried to show these in vue3 app.
my code in html
<div id="app">
<image_preview>
URL: [[image]]
</image_preview>
<file_uploader>
Counter:[[counter]]
</file_uploader>
</div>
in javascript
const ImagePreview = {
data(){
return {
image:"test.png"
}
},
mounted() {
},
delimiters: ['[[', ']]']
}
const Counter = {
data() {
return{counter: 0}
},
mounted() {
setInterval(() => {
this.counter++
}, 1000)
},
delimiters: ['[[', ']]']
}
Vue.createApp({
components:{
"image_preview":ImagePreview,
"file_uploader":Counter
}
}).mount('#app')
However nothing appears in html
Where am I wrong?
please re-read vue's documentation on components you know if you're going to need a template to render components and I bet you haven't read vue's documentation on components
follow my example and it takes care of your problem:
// </script><script type="module">
import { createApp, ref, onMounted } from 'https://unpkg.com/vue#3/dist/vue.esm-browser.js'
const ImagePreview = {
template: '#image_preview',
setup() {
return {
image: 'test.png'
}
}
}
const Counter = {
template: '#file_uploader',
setup() {
const counter = ref(0)
onMounted(() => setInterval(() => counter.value++, 1_000))
return { counter }
}
}
const app = createApp({
components:{
"image_preview": ImagePreview,
"file_uploader": Counter
}
})
.mount('#app')
<div id="app">
<image_preview>
URL: [[image]]
</image_preview>
<file_uploader>
Counter:[[counter]]
</file_uploader>
</div>
<template id="image_preview">
URL: {{ image }}
</template>
<template id="file_uploader">
Counter: {{ counter }}
</template>
I am trying to get $refs in Vue 3 using Composition API. This is my template that has two child components and I need to get reference to one child component instance:
<template>
<comp-foo />
<comp-bar ref="table"/>
</template>
In my code I use Template Refs: ref is a special attribute, that allows us to obtain a direct reference to a specific DOM element or child component instance after it's mounted.
If I use Options API then I don't have any problems:
mounted() {
console.log("Mounted - ok");
console.log(this.$refs.table.temp());
}
However, using Composition API I get error:
setup() {
const that: any = getCurrentInstance();
onMounted(() => {
console.log("Mounted - ok");
console.log(that.$refs.table.temp());//ERROR that.$refs is undefined
});
return {};
}
Could anyone say how to do it using Composition API?
You need to create the ref const inside the setup then return it so it can be used in the html.
<template>
<div ref="table"/>
</template>
import { ref, onMounted } from 'vue';
setup() {
const table = ref(null);
onMounted(() => {
console.log(table.value);
});
return { table };
}
On Laravel Inertia:
<script setup>
import { ref, onMounted } from "vue";
// a list for testing
let items = [
{ id: 1, name: "item name 1" },
{ id: 2, name: "item name 2" },
{ id: 3, name: "item name 3" },
];
// this also works with a list of elements
let elements = ref(null);
// testing
onMounted(() => {
let all = elements.value;
let item1 = all[0];
let item2 = all[1];
let item3 = all[2];
console.log([all, item1, item2, item3]);
});
</script>
<template>
<div>
<!-- elements -->
<div v-for="(item, i) in items" ref="elements" :key="item.id">
<!-- element's content -->
<div>ID: {{ item.id }}</div>
<div>Name: {{ item.name }}</div>
</div>
</div>
</template>
<template>
<your-table ref="table"/>
...
</template>
<script>
import { ref, onMounted } from 'vue';
setup() {
const table = ref(null);
onMounted(() => {
table.value.addEventListener('click', () => console.log("Event happened"))
});
return { table };
}
</script>
Inside your other component you can interact with events you already registered on onMounted life cycle hook as with my example i've registered only one evnet
If you want, you can use getCurrentInstance() in the parent component like this code:
<template>
<MyCompo ref="table"></MyCompo>
</template>
<script>
import MyCompo from "#/components/MyCompo.vue"
import { ref, onMounted, getCurrentInstance } from 'vue'
export default {
components : {
MyCompo
},
setup(props, ctx) {
onMounted(() => {
getCurrentInstance().ctx.$refs.table.tempMethod()
});
}
}
</script>
And this is the code of child component (here I called it MyCompo):
<template>
<h1>this is MyCompo component</h1>
</template>
<script>
export default {
setup(props, ctx) {
const tempMethod = () => {
console.log("temporary method");
}
return {
tempMethod
}
},
}
</script>
I've setup vue3 and Oruga but am running into some difficutly when attempting to get the component to render. The table component is displayed within a router-view (view/Vehicles)
I have:
/view/Vehicles.
<template>
<h1>Vehicles</h1>
<o-table :data="vehicles" :columns="columns"></o-table>
</template>
<script>
import { Table } from '#oruga-ui/oruga-next'
import { API } from 'aws-amplify'
import { listVehicles } from '../graphql/queries'
export default {
name: "Vehicles",
components: {
'o-table': Table
},
data() {
return {
vehicles: [],
columns: [
{
field: 'id',
label: 'ID',
width: '40',
numeric: true
},
{
field: 'name',
label: 'Name'
},
{
field: 'description',
label: 'Description'
},
{
field: 'address',
label: 'Address',
position: 'centered'
}
]
}
},
methods: {
/*
* Load async data
*/
async listVehicles() {
console.log("Getting Vehicles...")
const vehicles = await API.graphql({ query: listVehicles})
this.vehicles = vehicles.data.listVehicles.items
},
/*
* Handle page-change event
*/
onPageChange(page) {
this.page = page
this.listVehicles()
},
/*
* Handle sort event
*/
onSort(field, order) {
this.sortField = field
this.sortOrder = order
this.listVehicles()
},
/*
* Type style in relation to the value
*/
type(value) {
const number = parseFloat(value)
if (number < 6) {
return 'is-danger'
} else if (number >= 6 && number < 8) {
return 'is-warning'
} else if (number >= 8) {
return 'is-success'
}
}
},
mounted() {
this.listVehicles()
}
}
</script>
<style>
</style>
main.js - bootstrapping entire oruga library, doesn't seem to make a difference if I use single component or not...
import { createApp } from "vue";
import App from "./App.vue";
import Amplify from "aws-amplify";
import awsconfig from "./aws-exports";
import Oruga from "#oruga-ui/oruga-next";
import "./index.css";
import {
applyPolyfills,
defineCustomElements,
} from "#aws-amplify/ui-components/loader";
import router from "./router";
applyPolyfills().then(() => {
defineCustomElements(window);
});
Amplify.configure(awsconfig);
createApp(App).use(router).use(Oruga).mount("#app");
Looks like it was two things, need to add async to mounted as also the blocking warning was coming from eslinter set to
"rules": {
"vue/no-unused-components": "off"
}
I define tabs as prop, then I want to use in function inside setup() as tabs.value..., but it does not recognize property. It is throwing error:
Cannot find name 'tabs', tabs is not defined
Code:
<script lang="ts">
import {
defineComponent,
ref,
computed,
PropType,
toRefs,
} from '#vue/composition-api'
import i18n from '#/setup/i18n'
export default defineComponent({
name: 'ProgramModal',
props: {
tabs: Array as PropType<Array<any>>,
},
setup() {
const changeTab = (selectedTab: { id: number }) => {
tabs.value.map((t) => {
t.id === selectedTab.id ? (t.current = true) : (t.current = false)
})
}
return {
tabs,
changeTab,
ariaLabel,
}
},
})
</script>
How can I
You need to pass props to setup function:
setup(props) {...
So I have added a second bus to my code that runs on create, but no matter in which order I call the Busses the second bus (eventBus2) is never called and then returns no data. By printing some console logs I get the feeling that that eventBus2.$on is never executed. Is there some Vue rule that I'm not aware of, any suggestions?
Item.vue
<template>
<div>
<table>
<tr
v-for="item in info"
:key="item.id"
#click="editThisItem(item.id)"
>
<td>{{ item.name}}</td>
<td>{{ item.number}}</td>
<td>{{ item.size}}</td>
</tr>
</table>
</div>
</template>
<script>
import Something from "./Something.vue";
import axios from "axios";
import { eventBus } from "../main";
import { eventBus2 } from "../main";
export default {
components: { Something },
name: "Item",
data() {
return {
selected_item_id: 0,
info: null,
};
},
methods: {
editThisItem(bolt) {
this.selected_item_id = bolt;
eventBus2.$emit("itemWasSelected", this.selected_item_id);
eventBus.$emit("newTabWasAdded", "edit-item");
},
},
mounted() {
axios
.get("http://localhost:8080/items")
.then((response) => (this.info = response.data._embedded.artikli));
},
};
</script>
EditItem.vue
<script>
import Something from "./Something.vue";
import axios from "axios";
import { eventBus2 } from "../main";
export default {
components: { Something},
name: "Edit-item",
data() {
return {
info: null,
select_number: 0,
select_name: "",
selected_item_id: -1,
priv_item: {
id: 0,
size: "big"
},
};
},
mounted() {
if (this.selected_item_id != -1) {
axios
.get("http://localhost:8080/items/" + this.selected_item_id)
.then((response) => (this.priv_item = response.data));
}
},
created() {
eventBus2.$on("itemWasSelected", (data) => {
this.selected_item_id = data;
console.log(" + " + data);
//this console log does not even print the "+", the data is empty
});
console.log(this.selected_item_id);
},
};
</script>
main.js
export const eventBus = new Vue();
export const eventBus2 = new Vue();
you're expecting itemWasSelected and emitting WasSelected they should be the same.
PD: that can be done in one line.
import { eventBus } from "../main";
import { eventBus2 } from "../main";
import { eventBus, eventBus2 } from "../main";