Using IONIC 3 with Vue I would like to pass my object p to my router to access metadata later. I tried using tag :meta but it does not work:
route
{
path: '/new',
meta: { requiresAuth: false },
component: () => import ('../views/New.vue')
},
app.vue
<ion-menu-toggle auto-hide="false" v-for="(p, i) in appPages" :key="i">
<ion-item
#click="selectedIndex = i"
router-direction="root"
:router-link="p.url"
lines="none"
detail="false"
class="hydrated"
:meta=p
:class="{ selected: selectedIndex === i }">
<ion-icon slot="start" :ios="p.iosIcon" :md="p.mdIcon"></ion-icon>
<ion-label class="pointer">{{ p.title }}</ion-label>
</ion-item>
</ion-menu-toggle>
</ion-list>
I can't access in template
template.vue
<ion-header :translucent="true">
<ion-toolbar>
<ion-buttons slot="start">
<ion-menu-button color="primary"></ion-menu-button>
</ion-buttons>
<ion-title>{{ $route.meta }}</ion-title>
</ion-toolbar>
</ion-header>
Related
I have a view in Vue project.
Home.vue
<template>
<TestLayout >
<Card/>
<Card/>
<Card/>
</TestLayout>
</template>
<script>
import TestLayout from "../components/TestLayout.vue"
import Card from "../components/Card.vue"
export default {
name: "Home",
props:{
isList:{
type: Boolean
}
},
components: {
TestLayout,
Card
},
}
</script>
The TestLayout has a section where we can display cards in list or grid view
TestLayout.vue
<template>
<div class="flex border-solid ">
<ListBulletIcon class="h-10 w-10 cursor-pointer shadow border-2 border-indigo-600 rounded-l p-2"
#click="listView = true" />
<TableCellsIcon #click="listView = false"
class="h-10 w-10 cursor-pointer shadow border-2 border-indigo-600 rounded-r p-2" />
</div>
<section
:class="[listView ? 'md:grid-cols-1 grid-cols-1' : 'md:grid-cols-4 grid-cols-2', 'rounded-md grid gap-5 col-span-full']">
<slot :listView="listView"></slot>
</section>
</template>
<script>
import {
ListBulletIcon,TableCellsIcon} from '#heroicons/vue/24/outline'
export default {
data: function () {
return {
listView: false,
}
},
components: {
ListBulletIcon,
TableCellsIcon,
},
}
}
</script>
I want to change the style of Card.vue based on whether user clicks grid view or list view icon.
For example, I want to add this style to Card.vue div tag in its template:
:class="[isList ? 'dark:bg-midnight' : 'dark:bg-red-300', 'min-h-80 w-full bg-gray-50 shadow-xl rounded-md flex flex-col']"
How will I check isList is clicked or not?
How can I achieve this?
You're half way there. After defining a slot prop (<slot :listView="listView"></slot>) you should access it in parent and pass it down to slot components.
<TestLayout>
<template v-slot="{ listView }">
<Card :isList="listView" />
<Card :isList="listView" />
</template>
</TestLayout>
I'm using Vuetify 3.0.0-beta.0 ~ for my project (because it is the only version that supports vue3), and having a bit weird issue
I want to implement the same thing as described there https://codepen.io/reijnemans/pen/vYNadMo?editors=1010 with v-select involved, so I was needed to use Vuetify
copied snippet
<v-select
:items="items"
label="Standard"
>
<template v-slot:selection="{ item, index }">
<img :src="item.image">{{ item.name }}</template>
</template>
<template v-slot:item="{ item }">
<img :src="item.image">{{ item.name }}</template>
</v-select>
My Component:
<template>
<div class="resourceSelectors">
<v-col cols="10" lg="4" class="mx-auto">
<div class="text-center">
<h2 class="indigo--text" style="margin-bottom: 30px">Some Test H2</h2>
</div>
<v-col class="d-flex" cols="12" sm="6">
<v-select
:items="items"
label="Standard">
<template v-slot:selection="{ item }">
<img :src="item.image">{{ item.name }}
</template>
<template v-slot:item="{ item }">
<img :src="item.image">{{ item.name }}
</template>
</v-select>
</v-col>
</v-col>
</div>
</template>
<script>
import { mapState } from "vuex";
/* eslint-disable */
export default {
name: "testComponent",
data() {
return {
// hardware Configuration Validation Rules
items: [
{ name: 'Foo', image: 'https://www.gravatar.com/avatar/b17065ea1655f1e3283aac8d8fc16019?s=48&d=identicon&r=PG'},
{ name: 'Bar', image: 'https://www.gravatar.com/avatar/b17065ea1655f1e3283aac8d8fc16019?s=48&d=identicon&r=PG'},
{ name: 'Hoo', image: 'https://www.gravatar.com/avatar/b17065ea1655f1e3283aac8d8fc16019?s=48&d=identicon&r=PG'},
{ name: 'Coo', image: 'https://www.gravatar.com/avatar/b17065ea1655f1e3283aac8d8fc16019?s=48&d=identicon&r=PG'}],
}
}}
When I'm trying to run the above component I always get this weird error Failed setting prop "type" on <select>: value text is invalid. TypeError: Cannot set property type of #<HTMLSelectElement> which has only a getter,
Did anyone faced similar issue before?
In Vuetify 3, you need some workarounds to style the items in v-select, because the item slot resets the entire styling.
You should use the menu-props, with it you can pass props through to the v-menu component. It accepts an object with anything from /api/v-menu. This allows you to close the field on click.
In the item slot, you should use a v-list-item with an #click property to set the model.
I made an example here with a selection of symbols:
<script setup>
const symbols = [
'ab-testing',
'abacus',
'account',
'account-alert',
]
const form = { symbol: '', }
</script>
<template>
<v-select
v-model="form.symbol"
:items="symbols"
label="Symbol"
:prepend-inner-icon="'mdi-'+form.symbol"
:menu-props="{
closeOnClick: true,
closeOnContentClick: true,
}"
>
<template v-slot:selection="{ item, index }">
{{ item.value }}
</template>
<template v-slot:item="{ item, index }">
<v-list-item
:title="item.title"
:prepend-icon="'mdi-'+item.title"
#click="form.symbol = item.title"
>
</v-list-item>
</template>
</v-select>
</template>
I hope it helps you.
I couldn't find correct solution but I just wanted to share what I did about scoped slot. I think we should use item.raw to access name and image. And the next problem is how to make it clickable to trigger select event that I didn't know yet :(
const { createApp } = Vue
const { createVuetify } = Vuetify
const vuetify = createVuetify()
const app = createApp({
data() {
return {
value: null,
items: [
{
name: 'Foo',
image: 'https://www.gravatar.com/avatar/b17065ea1655f1e3283aac8d8fc16019?s=48&d=identicon&r=PG'
},
{
name: 'Bar',
image: 'https://www.gravatar.com/avatar/b17065ea1655f1e3283aac8d8fc16019?s=48&d=identicon&r=PG'
},
{
name: 'Hoo',
image: 'https://www.gravatar.com/avatar/b17065ea1655f1e3283aac8d8fc16019?s=48&d=identicon&r=PG'
},
{
name: 'Coo',
image: 'https://www.gravatar.com/avatar/b17065ea1655f1e3283aac8d8fc16019?s=48&d=identicon&r=PG'
}
]
}
}
});
app.use(vuetify).mount('#app');
<link href="https://cdn.jsdelivr.net/npm/vuetify#3.0.0-beta.9/dist/vuetify.min.css" rel="stylesheet"/>
<script src="https://unpkg.com/vue#3/dist/vue.global.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify#3.0.0-beta.9/dist/vuetify.min.js"></script>
<div id="app">
<div class="resourceSelectors">
<v-col cols="10" lg="4" class="mx-auto">
<div class="text-center">
<h2 class="indigo--text" style="margin-bottom: 30px">Some Test H2</h2>
</div>
<v-col class="d-flex" cols="12" sm="6">
<v-select
v-model="value"
:items="items"
item-title="name"
item-value="name"
label="Standard">
<template v-slot:item="{item}">
<v-list-item
:prepend-avatar="item.raw.image"
:title="item.raw.name"
/>
</template>
</v-select>
</v-col>
</v-col>
</div>
</div>
I'm attempting to learn VueJS and ElementUI to port over a legacy app. I'm very new to VueJS and am trying to get to grips with the basics. Understanding reusable components is going to be key for me, so I'm trying to get an example dialog working. Can someone help me work out why the dialog won't display when I click on the button on my Test page. I've registered the component but am unsure how I trigger it. The sample code is based on a tutorial for components I've found and ElementUI form example.
Test.vue
<template>
<el-button
type="success"
#click="dialogFormVisible = true"
size="small"
icon="el-icon-plus"
>click to open the Dialog</el-button>
<Dialog v-model="dialogFormVisible" />
</template>
<script>
import Dialog from "#/components/CustomerForm.vue";
export default {
name: "App",
components: { Dialog },
data() {
return {
dialogFormVisible: false,
};
},
methods: {
submit() {
console.log("confirm");
this.dialogFormVisible = false;
},
},
};
</script>
CustomerForm.vue
<template>
<el-dialog title="Customer Name" v-model="dialogFormVisible">
<el-form :model="form">
<el-form-item label="Activity name">
<el-input v-model="form.name"></el-input>
</el-form-item>
<el-form-item label="Activity zone">
<el-select v-model="form.region" placeholder="please select your zone">
<el-option label="Zone one" value="shanghai"></el-option>
<el-option label="Zone two" value="beijing"></el-option>
</el-select>
</el-form-item>
<el-form-item label="Activity time">
<el-col :span="11">
<el-date-picker
type="date"
placeholder="Pick a date"
v-model="form.date1"
style="width: 100%"
></el-date-picker>
</el-col>
<el-col class="line" :span="2">-</el-col>
<el-col :span="11">
<el-time-picker
placeholder="Pick a time"
v-model="form.date2"
style="width: 100%"
></el-time-picker>
</el-col>
</el-form-item>
<el-form-item>
<el-button type="primary" #click="submit">Create</el-button>
<el-button>Cancel</el-button>
</el-form-item>
</el-form>
</el-dialog>
</template>
<script>
export default {
data() {
return {
dialogFormVisible: false,
form: {
name: "",
region: "",
date1: "",
date2: "",
},
formLabelWidth: "120px",
};
},
methods: {
submit() {
console.log("submit!");
},
},
};
</script>
I am using a plugin called Vue-MultiSelect and showing it twice in my component. The first multiselect has a label of "Add New Contacts" and the second multiselect has a label of "Current Listed Contacts".
How can I hide the users for the "Add New Contacts" multiselect dropdown if they are already listed in the "Current Listed Contacts"?
Screenshot of problem:
I am using props to show the users for the "Current Listed contacts" multiselect. Here's the code for my Single file component where these multiselects reside (also demo link: CodeSandBox Code Editor) Note: click on POSTS --> EDIT (Acme Widget) to see the page where I am talking about
<template>
<div>
<label>Add New Contacts:</label>
<multiselect
id="add_new_contacts_input"
v-model="NewContacts"
:options="options"
label="lastname"
placeholder="Select or search for an existing contact"
track-by="uid"
:loading="isLoading"
:custom-label="selectedNameLabel"
aria-describedby="searchHelpBlock"
selectLabel
:multiple="true"
>
<template
slot="singleLabel"
slot-scope="props"
>{{ props.option.lastname }}, {{props.option.firstname}}</template>
<template slot="option" slot-scope="props">
<strong>{{ props.option.lastname }}</strong>
, {{ props.option.firstname }} —
<small>{{ props.option.email }}</small>
</template>
</multiselect>
<!-- <small id="searchHelpBlock" class="form-text text-muted"><font-awesome-icon icon="exclamation-circle" /> If customer does not exist, you will be prompted to add a new customer</small> -->
<!-- <h3>New contacts to be added:</h3>
<ul>
<li v-for="value in values" :key="value.uid">{{value.lastname}}, {{value.firstname}}</li>
</ul>-->
<label>Current Listed Contacts</label>
<multiselect
id="current_listed_contacts_input"
v-model="CurrentListedValues"
placeholder="There are no current contacts"
label="lastname"
track-by="uid"
:options="options"
:multiple="true"
:custom-label="selectedNameLabel"
:loading="isLoading"
selectLabel
>
<!-- formatting for the drop down list -->
<template slot="option" slot-scope="props">
<strong>{{ props.option.lastname }}</strong>
, {{ props.option.firstname }} —
<small>{{ props.option.email }}</small>
</template>
</multiselect>
</div>
</template>
<script>
import Multiselect from "vue-multiselect";
// import ApiService from "#/apiService";
export default {
components: { Multiselect },
props: ["users", "contacts"],
data() {
return {
NewContacts: [],
CurrentListedValues: this.contacts,
options: this.users,
isLoading: true
};
},
created() {
this.isLoading = false;
},
methods: {
selectedNameLabel(option) {
return `${option.lastname}, ${option.firstname} -- ${option.email}`;
}
}
};
</script>
you can use computed to filter your list:
computed: {
addOptions() {
let opt = this.users;
this.CurrentListedValues.forEach(c => {
opt = opt.filter(i => i.uid !== c.uid);
});
return opt;
}
}
and in your select list change options to addOptions: :options="addOptions"
So I have a basic layout :
<template>
<q-layout view="lHh Lpr lFf">
<q-header elevated class="bg-secondary text-white">
<q-toolbar >
<slot name="toolbar"></slot>
</q-toolbar>
</q-header>
<q-page-container >
<router-view ></router-view>
</q-page-container>
</q-layout>
</template>
And a basic router:
const routes = [
{
path: '/',
component: () => import('layouts/MyLayout.vue'),
children: [
{ path: '', component: () => import('pages/Index.vue') }
]
}
]
and Index.vue:
<template>
<q-page>
<template v-slot:default> CONTENT SHOWS</template>
<template v-slot:toolbar> CONTENT DOESN'T SHOW</template>
</q-page>
</template>
What do I do wrong? How can I get the slot up the router?
Simple answer: there is no way to accomplish that using slots.
You are seeing CONTENT SHOW because q-page has the default slot, but there is no definition for the toolbar slot there. Remember, slots get passed to their direct parents, this is a Vue fundamental rule. Your practice moved me to the age of master pages.
Using Name Views is the proper solution -- should move your toolbars into separate components and define them in routes.
Another solution, check the current route and render a proper toolbar:
<template>
<q-layout view="lHh Lpr lFf">
<q-header elevated class="bg-secondary text-white">
<q-toolbar>
<template v-if="$route.name === 'Index'">
<div>Toolbar for Index Page </div>
</template>
<template v-else-if="$route.fullPath.startsWith('/nested')">
<div>Toolbar for Index2 Page </div>
</template>
<template v-else>
<div>Toolbar for Default Pages </div>
</template>
</q-toolbar>
</q-header>
<q-page-container>
<router-view></router-view>
</q-page-container>
</q-layout>
</template>
Of course, moving these v-if logics into a separate component is a better practice.
It's been a long time since the original post, but maybe it can help someone else. Use names in router-view.
<template>
<q-layout view="lHh Lpr lFf">
<q-header elevated class="bg-secondary text-white">
<q-toolbar >
<router-view name="toolbar"></router-view>
</q-toolbar>
</q-header>
<q-page-container >
<router-view></router-view>
</q-page-container>
</q-layout>
</template>
Then, the route setup can contains something like this
const routes = [
{
path: '/',
component: () => import('layouts/MyLayout.vue'),
children: [{
path: '',
components : {
default: () => import('pages/Index.vue'),
toolbar: () => import('some another page or component.vue')
}
}]
}
]