Question: How can I create a swipable side menu for an ionic v4 vuejs app?
So I'm new to ionic and have been trying to migrate an existing web app to ionic. Following the steps on the limited articles up on ionic4 with vue. I installed #ionic/core, added the CDN to the index file, and the ignore flag for [/ion/] web compoenets right before mounting Vue.
I'm currently getting this error: Menu: must have a "content" element to listen for drag events on.
I'm a bit unsure of what to follow as many of the articles out there are on ion v2 and v3, I know there were a few changed made to how the ui-components and based on ionic4 beta docs what I have seems to be correct:'
<script lang="ts" src="./Layout.ts"></script>
<template>
<div class="layoutComponent">
<ion-page>
<ion-menu>
<ion-header>
<ion-toolbar color="primary">
<ion-title>Menu</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-list-header>
Navigate
</ion-list-header>
<ion-item>
<router-link :to="{name: 'link 1'}">
<ion-label full>
<eg-localizer token="1"></localizer>
</ion-label>
</router-link>
</ion-item>
<ion-item>
<router-link :to="{ name: 'link2'}">
<ion-label full>
<localizer token="2"></localizer>
</ion-label>
</router-link>
</ion-item>
<ion-item>
<a target="_blank" href="https://foo.com">
<ion-label full>
<localizer token="external link 3"></localizer>
</ion-label>
</a>
</ion-item>
</ion-list>
</ion-content>
</ion-menu>
<!-- main point of entry for app content -->
<slot></slot>
</ion-page>
</div>
</template>
The documentation on ionic framework it's a bit unclear or incomplete, but I was able to make the menu working:
<template>
<ion-menu side="start" content-id="menu-content">
<ion-header>
<ion-toolbar color="primary">
<ion-title>Start Menu</ion-title>
</ion-toolbar>
</ion-header>
<ion-content id="menu-content">
<ion-list lines="full">
<ion-item v-for="item in items" :key="item.name">
<ion-icon :name="item.icon" slot="start"></ion-icon>
<ion-label>{{ item.name }}</ion-label>
</ion-item>
</ion-list>
</ion-content>
</ion-menu>
</template>
<script>
export default {
data () {
return {
items: [
{ name: 'Home', icon: 'home', href: 'home' },
{ name: 'About', icon: 'information-circle', href: 'about' },
{ name: 'Articles', icon: 'list-box', href: 'articles' },
{ name: 'Log out', icon: 'logout', href: 'logout' }
]
}
}
}
</script>
The key is here:
<ion-menu side="start" content-id="menu-content">
and here
<ion-content id="menu-content">
Hope this helps anyone.
For Ionic 6 + Vue js 3.0 make sure you have your component <ion-router-outlet /> inside a <ion-content>, and that ion-content should have the id you have specified in content-id parameter for the menu.
<ion-content id="main">
<ion-router-outlet />
</ion-content>
Related
Background
I am making a exercise warm-up app. I've conceptually segmented it into a workout tab and a settings tab. In a linear fashion the workout tab lets one pick a workout plan ChooseWorkout.vue then an exercise ChooseLifts.vue then the weight ChooseWeight.vue and it will display an appropriate warmup.
The aforementioned components are working correctly and link to each other in the correct linear fashion.
What I want to do
I want to add tabbed navigation so a user can switch between the settings and the workout portion components of the app. The tabs I am making have a settings button and a workout button.
Apps flow
The problem
When I integrate the ion-tabs the main tab buttons correctly display the desired 2 components (ChooseWorkout.vue and Settings.vue) when clicked. But my problem is the router no longer
works. So when I navigate from the ChooseWorkout.vue to the ChooseLifts.vue I get a blank screen, but the tabs at the bottom of the app are visible.
The url for this navigation is http://localhost:8080/#/lifts/StrongLifts%205x5 (it works prior to the ion-tabs integration were <ion-vue-router /> was solely used on main.js).
Code
App.vue
<template>
<div id="app">
<ion-app>
<ion-tabs>
<ion-tab tab="workout">
<ion-vue-router />
</ion-tab>
<ion-tab tab="settings">
<Settings />
</ion-tab>
<template slot="bottom">
<ion-tab-bar>
<ion-tab-button tab="workout">
<ion-icon name="body-outline" />
<ion-label>Workouts</ion-label>
</ion-tab-button>
<ion-tab-button tab="settings">
<ion-icon name="body-outline" />
<ion-label>Settings</ion-label>
</ion-tab-button>
</ion-tab-bar>
</template>
</ion-tabs>
</ion-app>
</div>
</template>
COPY TO CLIPBOARD SELECT ALL
ChooseWorkout.vue
<template>
<ion-page>
<ion-header>
<ion-toolbar color="primary">
<ion-title>Choose Workout</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<ion-list>
<div v-for="workout in workouts" v-bind:key="workout">
<ion-item>
<ion-label>
<h1>{{workout.name}}</h1>
<p>{{workout.by}}</p>
</ion-label>
<router-link :to="{path:`lifts/${workout.name} `}">
<ion-button>Select</ion-button>
</router-link>
</ion-item>
</div>
</ion-list>
</ion-content>
</ion-page>
</template>
<script>
import { add } from "ionicons/icons";
import { addIcons } from "ionicons";
addIcons({
"ios-add": add.ios,
"md-add": add.md
});
export default {
name: 'ChooseWorkout',
data() {
return {
workouts: [
{name: 'Starting Strength', by: 'Mark Rippetoe'},
{name: 'StrongLifts 5x5', by: 'Mehdi'},
{name: 'GreySkull LP', by: 'John Sheaffer'},
{name: 'Max Single', by: 'n/a'}
]
}
},
methods: {
}
};
</script>
<style scope></style>
main.js
const router = new IonicVueRouter({
routes: [
{
path: "/",
redirect: "/workout"
},
{
path: "/settings",
name: "settings",
component: Settings
},
{
path: "/workout",
component: ChooseWorkout
},
{
path: "/lifts/:workout",
component: ChooseLifts
},
{
path: "/lifts/:workout/:excercise",
component: ChooseWeight
},
]
});
Your can use nested route in vue to accomplish that
https://ionicframework.com/docs/vue/navigation#working-with-tabs
I am using in my package.json:
#ionic/core: "^4.0.3",
#ionic/vue: "0.0.4",
vue: "^2.6.10",
vue-router: "^3.0.6"
furthermore:
I'm using the IonVueRouter from #ionic/vue
I've wrote an IonVuePage component, which has two other components, the HeaderSection (<ion-toolbar>), and the TabBar (<ion-tabs>). Between those components I've added a Tag, to add other components inside of this default layout.
However, If I am using a simple ion-button inside a new component (e.g. Home component), which is inside the <slot/> Tag of the IonVuePage component it is not clickable until:
I Wrap it inside of a <ion-buttons> Tag, or
I remove the TabBar component of the IonVuePage component.
What could explain that behaviour?
Do I have my mistake in combining of <ion-tabs> and IonVueRouter? Can I use both?
I've noticed, that the where the TabBar is placed in is overlying:
If I set the height of the diff to 10%, then the button is clickable, but unfortunately the TabBar is not visible anymore.
Here's my code:
IonVuePage component:
<template>
<ion-page
class="ion-page"
main>
<header-section
:title="title"
:show-back-button="showBackButton"/>
<slot name="content"/>
<tab-bar/>
</ion-page>
</template>
<script>
import TabBar from '#/components/page/TabBar';
import HeaderSection from '#/components/page/HeaderSection';
export default {
name: 'IonVuePage',
components: {
TabBar,
HeaderSection,
},
...
TabBar component:
<template>
<!-- Listen to before and after tab change events -->
<ion-tabs
#IonTabsWillChange="beforeTabChange"
#IonTabsDidChange="afterTabChange">
<ion-tab
tab="home"/>
<ion-tab
tab="request"/>
<template slot="bottom">
<ion-tab-bar >
<ion-tab-button tab="home">
<ion-icon name="home"/>
<ion-label>Schedule</ion-label>
<ion-badge>6</ion-badge>
</ion-tab-button>
<!-- Provide a custom route to navigate to -->
<ion-tab-button tab="request">
<ion-icon name="contacts"/>
<ion-label>Request</ion-label>
</ion-tab-button>
</ion-tab-bar>
</template>
</ion-tabs>
</template>
Home component:
<template>
<ion-vue-page
:title="welcometext"
:show-back-button="true">
<ion-content slot="content">
<ion-button #click="changeToRequest">change to request page!</ion-button>
<ion-buttons>
<ion-button
fill="solid"
#click="showModal">show modal! </ion-button>
</ion-buttons>
</ion-content>
</ion-vue-page>
</template>
},
...
App.vue:
<template>
<div id="app">
<ion-app>
<ion-vue-router />
</ion-app>
</div>
</template>
<script>
export default {
name: 'App',
};
</script>
router.js:
import Vue from 'vue';
import { IonicVueRouter } from '#ionic/vue';
import Home from '#/views/Home';
Vue.use(IonicVueRouter);
export default new IonicVueRouter({
mode: 'history', // for not having the # in the URL
routes: [
{
path: '/',
redirect: '/home',
},
{
path: '/home',
name: 'Home',
component: Home,
},
{
path: '/request',
name: 'Requet',
component: () => import('#/views/TestRequest'),
},
],
});
I appreciate any kind of help or advise.
Kind regards
I want to create a simple shopping list app with the price.
This is my shopping.html
<ion-header>
<ion-navbar color="secondary">
<ion-title align="center">
My Shopping Tracker
</ion-title>
<ion-buttons end>
<button ion-button icon-only (click)="addItem()"><ion-icon name="cart"></ion-icon></button>
</ion-buttons>
</ion-navbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item *ngFor="let item of items" (click)="viewItem(item)">{{item.type}}</ion-item>
</ion-list>
</ion-content>
<ion-content>
<ion-list>
<ion-item-sliding *ngFor="let item of items">
<ion-item>
<h2>{{item.today}}</h2>
<p>{{item.type}}</p>
<p>{{item.total}}</p>
</ion-item>
<ion-item-options side="right">
<button ion-button (click)="delete(item)">
<ion-icon name="trash"></ion-icon>Delete
</button>
<button ion-button (click)="edit(item)">
<ion-icon name="redo"></ion-icon>Edit
</button>
</ion-item-options>
</ion-item-sliding>
</ion-list>
</ion-content>
<ion-footer>
<ion-toolbar >
<ion-title>{{total}}</ion-title>
</ion-toolbar>
</ion-footer>
This is the addshopping.html
<ion-header>
<ion-toolbar color="secondary">
<ion-title>
Add Shopping List
</ion-title>
<ion-buttons end>
<button ion-button icon-only (click)="close()"><ion-icon name="close"></ion-icon></button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item>
<ion-label>Date:</ion-label>
<ion-datetime displayFormat="DD-MM-YYYY HH:mm" [(ngModel)]="today"></ion-datetime>
</ion-item>
<ion-item>
<ion-label floating>Type:</ion-label>
<ion-input type="text" [(ngModel)]="type"></ion-input>
</ion-item>
<ion-item>
<ion-label floating>Description:</ion-label>
<ion-input type="text" [(ngModel)]="description"></ion-input>
</ion-item>
<ion-item>
<ion-label floating>Total Amount:</ion-label>
<ion-input type="text" [(ngModel)]="total"></ion-input>
</ion-item>
</ion-list>
<button full ion-button color="secondary" (click)="saveItem()">Save</button>
</ion-content>
This is my addlist.ts
import { Component } from '#angular/core';
import { NavController, ViewController } from 'ionic-angular';
#Component({
selector: 'page-addlist',
templateUrl: 'addlist.html'
})
export class AddListPage {
type: string;
description: string;
today: Date;
total: price;
constructor(public navCtrl: NavController, public view: ViewController) {
}
saveItem(){
let newItem = {
type: this.type,
today: this.today,
description: this.description,
total: this.total,
};
this.view.dismiss(newItem);
}
close(){
this.view.dismiss();
}
}
How do I calculate the total price for each time I add up new item? and display it on the footer? I uploaded the image.
Do I need to change from string to number values? Or stay it as string?
You can use a function that returns the total price, like this
private getTotalPrice() {
let totalPrice = 0;
for (let item of itens) {
totalPrice += Number.parseFloat(item.total);
}
return totalPrice;
}
and then u can call it in the footer
[(ngModel)]="getTotalPrice()"
I am new in Ionic2 and Angular2 trying to update my array at front side but its not updating.Moreover its updating perfectly at backend (ts),checked using console. need your help
My Component:
import { Component } from '#angular/core';
import { NavController, NavParams, ViewController } from 'ionic-angular';
#Component({
selector: 'page-modal-filter',
templateUrl: 'modal-filter.html'
})
export class ModalFilterPage {
public fil=[];
public BRANDNAME: any;
public srts:any;
constructor(public nav: NavController, public viewCtrl: ViewController, public navParams: NavParams) {
this.fil = [];
this.srts="ABCD";
if (navParams.get('tabName') == 'filter') {
let data = navParams.get('data');
data.map(d => {
for (let op in d.OPTIONGROUP) {
for (let x in d.OPTIONGROUP[op]) {
if (x != "UPC") {
if (!this.fil[x]) {
this.fil[x] = [];
}
if (this.fil[x].indexOf(d.OPTIONGROUP[op][x]) == -1) {
this.fil[x].push(d.OPTIONGROUP[op][x]);
}
}
}
}
})
console.log(this.fil);
}
}
closeModal() {
// this.nav.pop();
this.viewCtrl.dismiss(true);
}
}
"fil" array not showing on frontside of html but console show its perfectly.
My html code:
fil array not showing
<ion-header>
<ion-navbar color="primary">
<ion-buttons start>
<button ion-button (click)="closeModal()">
<ion-icon name="close"></ion-icon>
</button>
</ion-buttons>
<ion-title>Search Result(105)</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding class="tab-filter">
<!--filter list-->
<pre>{{fil}}</pre>
<pre>{{srts}}</pre>
<ion-list class="list-no-border">
<ion-item>
<ion-label> PLACE</ion-label>
<ion-select>
<ion-option value="">All Regions</ion-option>
<ion-option value="vn">Vietnam</ion-option>
</ion-select>
</ion-item>
<ion-item class="price-ranger">
<ion-label>Price</ion-label>
<ion-input type="text" placeholder="Min"></ion-input>
-
<ion-input type="text" placeholder="Max"></ion-input>
</ion-item>
<ion-item>
<ion-label>Free shipping</ion-label>
<ion-toggle checked="false"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Once pice only</ion-label>
<ion-toggle checked="false"></ion-toggle>
</ion-item>
<ion-item>
<ion-label>Sale items</ion-label>
<ion-toggle checked="false"></ion-toggle>
</ion-item>
</ion-list>
</ion-content>
<!--Footer buttons-->
<ion-footer class="category">
<ion-toolbar position="bottom">
<ion-buttons end>
<button ion-button (click)="closeModal()">
CANCEL
</button>
<button ion-button (click)="closeModal()">
<span ion-text color="gray">APPLY</span>
</button>
</ion-buttons>
</ion-toolbar>
</ion-footer>
Your {{fil}} is an array yet you are declaring it on the view as a simple property. Angular does not automatically interpret an array with string interpolation.
For arrays you need to use *ngFor
So if you want like you have stated is being displayed in your console.log you could write something like
<div *ngFor="let item of fil">
<ion-item *ngFor="let color of item.COLOR">
{{color.propertyName}}
<ion-item>
<ion-item *ngFor="let size of item.SIZE">
{{color.propertyName}}
<ion-item>
</div>
I resolved my issue by using
this.fil = [];
to
this.fil={}
I have an array of objects that I want to loop through in my template and output as cards. I have it working currently using *ngfor and now I want to change it to use collection repeat instead.
Here is my code:
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { TowersModel } from '../../app/models/towers-model';
#Component({
selector: 'page-towers',
templateUrl: 'towers.html'
})
export class TowersPage {
towers: any;
constructor(public navCtrl: NavController){
this.towers = [
{
"name" : "Tower 1",
"image" : "http://placehold.it/350x150"
},
{
"name" : "Tower 2",
"image" : "http://placehold.it/350x150"
},
{
"name" : "Tower 3",
"image" : "http://placehold.it/350x150"
}
];
}
}
Template:
<ion-header>
<ion-navbar>
<ion-title>
Towers
</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-content>
<ion-card *ngFor="let tower of towers">
<img src="{{ tower.image }}" alt="{{tower.name}}">
<ion-item>
<h2>{{tower.name}}</h2>
<p>11 N. Way St, Madison, WI 53703</p>
</ion-item>
<ion-item>
<span item-left>18 min</span>
<span item-left>(2.6 mi)</span>
<button ion-button icon-left clear item-right>
<ion-icon name="navigate"></ion-icon>
Start
</button>
</ion-item>
</ion-card>
</ion-content>
</ion-content>
So as mentioned this approach works fine. If I try and change it though to use a collection repeat instead like so:
<ion-header>
<ion-navbar>
<ion-title>
Towers
</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-content>
<ion-item collection-repeat="let tower of towers">
<h1>Tower {{tower.name}}</h1>
</ion-item>
</ion-content>
</ion-content>
Then I get the following error:
Runtime Error
Error in ./TowersPage class TowersPage - caused by: Cannot read property 'name' of undefined
I think you are using ionic 2 and you need to use vitualScroll instead of collection-repeat.
<ion-list [virtualScroll]="towers">
<ion-item *virtualItem="let tower">
{{ tower.name }}
</ion-item>
</ion-list>