Auto-hide Floating Action Button in Ionic 4 - ionic-framework

I want to show scrollToTop button when user scrollToBottom and when user at top then only show scrollToBottom. How to manage the button please help me...this is my screenshot please help me how to manage these button.
tab1.page.ts
<ion-content cache-view="false" (ionScrollStart)="logScrollStart()" (ionScroll)="logScrolling($event)"
(ionScrollEnd)="logScrollEnd()" [scrollEvents]="true">
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button *ngIf="showToolbar" (click)="ScrollToTop($event)">
<ion-icon name="arrow-dropup"></ion-icon>
</ion-fab-button>
</ion-fab>
<ion-fab vertical="top" horizontal="end" slot="fixed">
<ion-fab-button (click)="ScrollToBottom($event)">
<ion-icon name="arrow-dropdown"></ion-icon>
</ion-fab-button>
</ion-fab>
</ion-content>
tab1.page.ts
ScrollToTop(event){
this.content.ionScrollEnd.subscribe((data)=>{
if(this.content){
//console.log(data);
this.showToolbar = true;
}
});
this.content.scrollToTop(1500);
}
ScrollToBottom(){
this.content.scrollToBottom(1500);
}
logScrollStart(){
// console.log("logScrollStart : When Scroll Starts");
}
logScrolling(){
}
logScrollEnd(){
this.content.ionScrollEnd.subscribe((data)=>{
if(this.content){
//console.log(data);
this.showToolbar = true;
}
});

Maybe this helps
<ion-content cache-view="false" (ionScrollStart)="logScrollStart()" (ionScroll)="logScrolling($event)"
(ionScrollEnd)="logScrollEnd()" [scrollEvents]="true">
<ion-fab *ngIf="showTop" vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button *ngIf="showToolbar" (click)="ScrollToTop($event)">
<ion-icon name="arrow-dropup"></ion-icon>
</ion-fab-button>
</ion-fab>
<ion-fab *ngIf="showBottom" vertical="top" horizontal="end" slot="fixed">
<ion-fab-button (click)="ScrollToBottom($event)">
<ion-icon name="arrow-dropdown"></ion-icon>
</ion-fab-button>
</ion-fab>
</ion-content>
and
ScrollToTop(event){
this.content.ionScrollEnd.subscribe((data)=>{
if(this.content){
//console.log(data);
this.showToolbar = true;
}
});
this.content.scrollToTop(1500);
this.showBottom=true;
this.showTop=false;
}
ScrollToBottom(){
this.content.scrollToBottom(1500);
this.showTop=true;
this.showBottom=false;
}

Related

Ionic 6 ionViewWillEnter cannot read property 0 of undefined

I am learning ionic. So i have added data to an array declared in a service. When i go back to the lists page, ngOnInit will not execute if i have been to that page before. To load the data i need to use ionViewWillEnter or ionViewDidEnter. However, when i move the function call to the ionViewWillEnteri get the following error:
ERROR TypeError: Cannot read property '0' of undefined
at DiscoverPage_Template (template.html:39)
at executeTemplate (core.js:9544)
at refreshView (core.js:9413)
at refreshComponent (core.js:10579)
at refreshChildComponents (core.js:9210)
at refreshView (core.js:9463)
at refreshEmbeddedViews (core.js:10533)
at refreshView (core.js:9437)
at refreshComponent (core.js:10579)
at refreshChildComponents (core.js:9210)
The only way i can get rid of the above error is by calling the function in both ngOnInIt and ionViewWillEnter.
loadedPlaces: Place[];
listedLoadedPlaces: Place[]; //for virtual scroll
constructor(private placesService: PlacesService, private menuCntrl: MenuController) { }
ngOnInit() {
console.log('ngOnInIt');
this.loadData();
}
ionViewWillEnter(){
console.log('ionViewWillEnter');
this.loadData();
}
loadData(){
this.loadedPlaces = this.placesService.places; //getter property
//for the virtual scroll
this.listedLoadedPlaces = this.loadedPlaces.slice(1);
}
I have also tried declaring an empty array and then removing the function call from ngOnInit. In this case i get following issue:
core.js:6157 ERROR TypeError: Cannot read property 'title' of undefined
at DiscoverPage_Template (template.html:39)
at executeTemplate (core.js:9544)
at refreshView (core.js:9413)
at refreshComponent (core.js:10579)
at refreshChildComponents (core.js:9210)
at refreshView (core.js:9463)
at refreshEmbeddedViews (core.js:10533)
at refreshView (core.js:9437)
at refreshComponent (core.js:10579)
at refreshChildComponents (core.js:9210)
My environment
node: v14.15.5
npm: 6.14.11
angular: 11.2.0
ionic: 6.13.1
How can i solve this issue?
Update:HTML
<ion-header>
<ion-toolbar>
<!--menu drawer-->
<ion-buttons slot="start">
<ion-menu-button menu="menu1"></ion-menu-button>
</ion-buttons>
<ion-title>Discover Places</ion-title>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<ion-segment value="all" (ionChange)="segmentChanged($event)">
<ion-segment-button value="all">All Places</ion-segment-button>
<ion-segment-button value="bookable">Bookable Places</ion-segment-button>
</ion-segment>
<ion-grid>
<!--open the side drawer manually -->
<ion-row>
<ion-button fill="clear" (click)="onMenuDrawerOpen()">
Menu
<ion-icon slot="start" name="apps-outline"></ion-icon>
<ion-icon slot="end" name="checkmark-done-outline" color="primary"></ion-icon>
</ion-button>
</ion-row>
<!--Featured place-->
<ion-row>
<ion-col size="12" size-sm="8" offset-sm="2" class="ion-text-center">
<ion-card>
<ion-card-header>
<ion-card-title>{{ loadedPlaces[0].title }}</ion-card-title>
<ion-card-subtitle>{{ loadedPlaces[0].price | currency }} / Night</ion-card-subtitle>
</ion-card-header>
<ion-img [src]="loadedPlaces[0].imageUrl"></ion-img>
<ion-card-content>
<div>{{ loadedPlaces[0].description}}</div>
</ion-card-content>
<div class="ion-text-right">
<!--fill Clear mean no background -->
<ion-button fill="clear" color="primary" routerDirection="forward" [routerLink]="['/', 'places', 'tabs', 'discover', 'place', loadedPlaces[0].id, 'detail']">
More
<ion-icon slot="start" name="star"></ion-icon>
<ion-icon slot="end" name="arrow-forward-circle-outline" color="primary"></ion-icon>
</ion-button>
</div>
</ion-card>
</ion-col>
</ion-row>
<!--Other places-->
<ion-row>
<ion-col size="12" size-sm="8" offset-sm="2" class="ion-text-center">
<!--since using scroll, removed the for loop from the ion-item moved it to the ion-virtual-scroll-->
<!--it takes the items property -->
<!--get the approxItemHeight using the dev tools and picking the height of an ion-item. The default is 40px-->
<ion-virtual-scroll [items]="listedLoadedPlaces" approxItemHeight="60px">
<!--<ion-list>-->
<!--Excluding the featured item above-->
<!--<ion-item *ngFor="let place of loadedPlaces.slice(1); let i = index" [routerLink]="['/', 'places', 'tabs', 'discover', 'place', place.id, 'detail']" detail>-->
<ion-item [routerLink]="['/', 'places', 'tabs', 'discover', 'place', place.id, 'detail']"
detail
*virtualItem="let place">
<ion-thumbnail slot="start">
<ion-img [src]="place.imageUrl"></ion-img>
</ion-thumbnail>
<ion-label>
<h2>{{ place.title}}</h2>
<p>{{ place.description }}</p>
</ion-label>
</ion-item>
<!--</ion-list>-->
</ion-virtual-scroll>
</ion-col>
</ion-row>
</ion-grid>
</ion-content>
I had to check for the null. Changing to following fixed the issue, did it for all the properties:
loadedPlaces[0]?.title

ionic push notification opens page but in page navigation is messed up

Folks:
I have an ionic app that has a page where user can post comments on a story. If a user posts new comment, the comment is displayed in the page. Comments are fetched from a REST API using the "let comment of postComments" fragment with *ngFor.
<ion-content *ngIf="!searchUser" #myContent padding [scrollEvents]="true">
<ion-refresher slot="fixed" (ionRefresh)="reloadPage($event)">
<ion-refresher-content pullingIcon="arrow-dropdown" refreshingSpinner="circles" refreshingText="Refreshing..." >
</ion-refresher-content>
</ion-refresher>
<div class="post-content">
<ion-item lines="none" no-padding>
<ion-avatar slot="start">
<img [src]=" (profileUrl == null) ? 'assets/img/emptyperson.jpg' : profileUrl">
</ion-avatar>
<p class="user-post-text">
<strong>{{
username
}}</strong><span>
{{ description }}
</span>
</p>
</ion-item>
<div class="border"></div>
<ion-item-sliding class="comments-container" *ngFor="let comment of postComments; let i = index; let c = count;" [id]="'slidingItem' + i">
<ion-item no-padding lines="none">
<ion-avatar slot="start">
<img [src]=" (comment.profileUrl == null) ? 'assets/img/emptyperson.jpg' : comment.profileUrl">
</ion-avatar>
<p>
<strong>{{
comment.username
}}</strong>
{{ comment.comment }}
</p>
<ion-button
slot="end"
fill="clear"
(click)="likeButton(comment.id, comment.currentUserLike)"
[disabled]="disableLikeButton"
>
<ion-icon
*ngIf="!comment.currentUserLike"
slot="icon-only"
name="heart-outline"
color="black"
></ion-icon>
<ion-icon
*ngIf="comment.currentUserLike"
slot="icon-only"
name="heart"
color="danger"
></ion-icon>
</ion-button>
<br/>
</ion-item>
<ion-item-options *ngIf="isPostMine && (comment.userId != userdetails.id)">
<ion-item-option color="danger" >
<button color="danger" [disabled]="disableDeleteButton" (click)="removeComment(comment.id,i)"><ion-icon class="inside-icon" name="trash"></ion-icon></button>
</ion-item-option>
<ion-item-option color="primary" >
<button color="primary" (click)="reportOffensive(comment.id,i)"><ion-icon class="inside-icon" name="remove-circle"></ion-icon> </button>
</ion-item-option>
</ion-item-options>
<ion-item-options *ngIf="isPostMine && (comment.userId == userdetails.id)">
<ion-item-option color="danger" >
<button color="danger" [disabled]="disableDeleteButton" (click)="removeComment(comment.id,i)"><ion-icon class="inside-icon" name="trash"></ion-icon></button>
</ion-item-option>
</ion-item-options>
<ion-item-options *ngIf="!isPostMine && (comment.userId != userdetails.id)">
<ion-item-option color="primary" >
<button color="primary" (click)="reportOffensive(comment.id,i)"><ion-icon class="inside-icon" name="remove-circle"></ion-icon> </button>
</ion-item-option>
</ion-item-options>
<ion-item-options *ngIf="!isPostMine && (comment.userId == userdetails.id)">
<ion-item-option color="danger" >
<button color="danger" [disabled]="disableDeleteButton" (click)="removeComment(comment.id,i)"><ion-icon class="inside-icon" name="trash"></ion-icon></button>
</ion-item-option>
</ion-item-options>
<p class="all-comments">
<span class="grey-text"> {{ comment.createdAt | timeAgo }}</span>
<span *ngIf="comment.numLikes > 0" class="grey-text tab" (click)="userList(comment.id,'CommentLikes')">{{comment.numLikes}} likes</span>
</p>
</ion-item-sliding>
</div>
</ion-content>
<ion-footer>
<ion-item lines="none">
<ion-avatar slot="start">
<img src="{{ userProfileUrl }}" />
</ion-avatar>
<ion-textarea autocapitalize="sentences" maxLength="255" type="text"
[(ngModel)]="commentContent" (ionChange)="getUsers($event)"
rows="1"
placeholder="Add a comment"
></ion-textarea>
<ion-icon class="blueicon" slot="end" name="send" (click)="addComment()"></ion-icon>
</ion-item>
</ion-footer>
In my typescript, I have a function addComment() which refreshes the value of postComments so that the page automatically reloads.
addComment() {
if (this.commentContent.trim()) {
this.showLoader("Posting Comment ...");
this.userPostComment.comment = this.commentContent;
this.userPostCommentJSON = JSON.stringify(this.userPostComment);
//console.log("Comment JSON is "+this.userPostCommentJSON);
this.https.post(this.serviceUrl+'comments',this.userPostCommentJSON,this.httpOptions)
.subscribe(
(res: any)=>{
this.commentContent = "";
this.refresh();
setTimeout(() => {
console.log("In Timeout");
this.refreshPostComments("");
this.events.publish('postComment:created', {
id: res.id,
time: new Date()
});
this.hideLoader();
this.commentContent = "";
},2000)
},
err => {
this.hideLoader();
this.showAlert(err);
}
);
}
}
This is working like charm, when I get to the Comments page from the story.
Here is my problem. If I navigate to the Comments page from a oneSignal push notification, everything works fine up to the point where I enter a new comment. When I enter a new Comment, the comment is created in the database using the RESTAPI (works fine), the refreshPostComments method is called in typescript (works fine), but my view does not get updated till I physically click the comment box again.
I don't know what I am doing wrong. But in my app.component.ts, I check for oneSignal notification clicked and if clicked, I do this
if (localStorage.getItem('auth-user') != undefined || null) {
if (additionalData.type == "comment") {
await console.log("Going to Comments");
this.router1.navigateForward(['/comments', {
userId: parseInt(additionalData.userId),
username: additionalData.username,
userPostId: parseInt(additionalData.userPostId),
description: additionalData.description,
profileNameKey: additionalData.profileNameKey
}]);
}
That works. Takes me to the Comments Page. But once I am in the page, I can do virtually everything else like peach, except if I add a comment, then it does not reflect in real time.
Any pointers on what I am missing?
JR
Found my solution. I think Angular’s Change detection is triggered by certain events, and when I was navigating to the page from OneSignal's notification - whatever OneSignal was using, it was not getting on Angular's radar. So I had to import ChangeDetectorRef and do a manual detectChanges() to make sure ngFor would detect the changes as soon as they occur.
Phew. Spent almost a half a day on this. So much for OneSignal and Angular's quirks with OneSignal. And Ionic had nothing to do with it.
Regards,
JR

Ionic router failed

I have used ion-tabs and side-menu for change route the app, both uses routerLink. And when i press Home tab in ion-tab to go back to home page inside other pages, route changed to home but home page constructor method and onInit methods does not invoke.
side-menu.component.ts
<ion-app>
<ion-split-pane contentId="main-content">
<ion-menu *ngIf="isAuthenticated$ | async" contentId="main-content" type="overlay">
<ion-content>
<ion-list id="inbox-list">
<div class="menu-header">
<ion-list-header>
<img src="assets/wa-logo.ico">
</ion-list-header>
<ion-note></ion-note>
</div>
<ion-menu-toggle auto-hide="false" *ngFor="let p of appPages; let i = index">
<ion-item (click)="selectedIndex = i" routerDirection="root" [routerLink]="[p.url]" lines="none" detail="false" [class.selected]="selectedIndex == i">
<ion-icon slot="start" [ios]="p.icon + '-outline'" [md]="p.icon + '-sharp'"></ion-icon>
<ion-label>{{ p.title }}</ion-label>
</ion-item>
</ion-menu-toggle>
</ion-list>
</ion-content>
<ion-footer>
<ion-menu-toggle auto-hide="false">
<ion-item
(click)="logout()"
routerDirection="root"
lines="none"
detail="true"
>
<ion-icon
slot="start"
ios='log-out-outline'
md="log-out-sharp"
></ion-icon>
<ion-label> Logout </ion-label>
</ion-item>
</ion-menu-toggle>
</ion-footer>
</ion-menu>
<ion-router-outlet id="main-content"></ion-router-outlet>
<ion-tabs *ngIf="isAuthenticated$ | async">
<ion-tab-bar [translucent]="true" slot="fixed">
<ion-tab-button (click)="onClick()">
<ion-icon name="home"></ion-icon>
<ion-label> Home </ion-label>
</ion-tab-button>
<ion-tab-button [routerLink]="['/features/new-request']">
<ion-icon name="document-text"></ion-icon>
<ion-label> New Request </ion-label>
</ion-tab-button>
<ion-tab-button [routerLink]="['/features/prev-requests']">
<ion-icon name="reader"></ion-icon>
<ion-label> All Requests </ion-label>
</ion-tab-button>
</ion-tab-bar>
</ion-tabs>
</ion-split-pane>
</ion-app>
So if i go inside prev-request page using ion-tab and once using hardware back button or ion-toolbar back button to changes route back to home page constructor and ngOnInit does not invoke, but if i route using side menu and go back with hardware back button or ion-toolbar back button constructor and ngOnInit methods are invoked.
prev-req.page
constructor(private _preReqService: PrevReqService,private platform: Platform,
private _modal: ModalService,
private _router: Router) {
this.platform.backButton.subscribeWithPriority(10, () => {
this._router.navigate(['/features/home'])
});
}
Still i don't have any clue why this happens.
Any idea why this happens ?
If you want to execute a function every time you go to a page Use Ionic Page Life Cycle.
ionViewWillEnter: Fired when the component routing to is about to animate into view.
ionViewDidEnter: Fired when the component routing to has finished animating.
ionViewWillLeave: Fired when the component routing from is about to animate.
ionViewDidLeave: Fired when the component routing to has finished animating.
export class ExamplePage implements OnInit {
constructor(){
}
ionViewWillEnter(){
console.log('Will Enter Fired') // will fire every time to go to a page.
}
}
Ionic Life Cycle Docs

Ionic-Vue Ionicons 5.x.x doesn't show icon

I used ionic-vue with ionicons 5.0.1 but after call
<ion-icon name="add"></ion-icon>
i was following https://dev.to/aaronksaunders/build-your-first-ionic-vue-app-18kj and https://github.com/ionic-team/ionic/issues/19078 tutorial, but stucked and icon in FAB cannot be show.
This is my syntax, thank you for helping.
<template>
<ion-page>
....
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button #click="$router.push({ name: 'new-item' })">
<ion-icon name="add"></ion-icon>
</ion-fab-button>
</ion-fab>
</ion-content>
</ion-page>
</template>
<script>
...
import { addIcons } from 'ionicons';
import * as allIcons from 'ionicons/icons';
const currentIcons = Object.keys(allIcons).map(i => {
const key = i.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`)
if(typeof allIcons[i] === 'string') {
return {
[key]: allIcons[i],
}
}
return {
['ios-' + key]: allIcons[i].ios,
['md-' + key]: allIcons[i].md,
};
});
const iconsObject = Object.assign({}, ...currentIcons);
addIcons(iconsObject);
...
</script>
Result FAB does not show icon 'add':
For Ionic Vue 3 using Composition API:
<template>
<ion-icon :icon="rocket" />
</template>
<script>
import { IonIcon } from '#ionic/vue';
import { rocket } from 'ionicons/icons';
export default {
components: { IonIcon },
setup() {
return {
rocket
}
}
}
</script>
For <script setup>
<script setup>
import { IonIcon } from '#ionic/vue';
import { rocket } from 'ionicons/icons';
</script>
<template>
<ion-icon :icon="rocket" />
</template>
I was also following the same guide(https://dev.to/devmount/how-to-use-ionicons-v5-with-vue-js-53g2), and I encountered the same issue.
I decided to downgrade the ionicons version to version 4:
npm i ionicons#4
This solved my issue.
The code that I used from the guide:
<template>
<ion-page>
<ion-header>
<ion-toolbar color="primary">
<ion-title>Home</ion-title>
</ion-toolbar>
</ion-header>
<ion-content padding>
<ion-list>
<ion-item>
<ion-checkbox slot="start"></ion-checkbox>
<ion-label>
<h1>Create Ideaa</h1>
<ion-note>Run Idea by Brandy</ion-note>
</ion-label>
<ion-badge color="success" slot="end">5 Days</ion-badge>
</ion-item>
</ion-list>
<ion-fab vertical="bottom" horizontal="end" slot="fixed">
<ion-fab-button >
<ion-icon name="add" ></ion-icon>
</ion-fab-button>
</ion-fab>
</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: "HomePage",
props: {
msg: String
}
};
</script>
In your main.js file
import * as allIcons from "ionicons/icons";
Vue.mixin({
data() {
return {
i: allIcons,
};
},
methods: {
icon(name) {
return this.i[name];
}
}
});
now you can use <ion-icon :src="icon('search')"></ion-icon> anywhere in the vue application, it is going to work
hey thanks for checking out the blogs and videos...
you can also get the icons this way...
<template>
<ion-button #click="handleAddItemClicked" >
<ion-icon slot="icon-only" :src="i.saveOutline" ></ion-icon>
</ion-button>
<ion-button #click="handleAddItemClicked" >
<ion-icon slot="icon-only" :src="i.save" ></ion-icon>
</ion-button>
<ion-button #click="handleAddItemClicked" >
<ion-icon slot="icon-only" :src="i.saveSharp" ></ion-icon>
</ion-button>
</template>
<script>
import * as allIcons from "ionicons/icons";
...
data() {
return {
i : allIcons,
};
},
</script>
I followed this comment in a closed issue on #modus/ionic-vue repo: https://github.com/ModusCreateOrg/ionic-vue/issues/120#issuecomment-633666592
import { addIcons } from 'ionicons'
import { add, cartOutline } from 'ionicons/icons'
addIcons({ add, "cart-outline": cartOutline })
This worked with ionicons#5.1.2 installed. Note how the multi word icon imports are camel case instead of kebab case. If you want to use the kebab case variant of the name for an ion-icon you have to do the assignment to the kebab case name yourself like in the case of cart-outline above.
Though if you wanted to add all of them at once and support kebab case, you could map a new object like this:
import { addIcons } from 'ionicons'
import * as allIcons from 'ionicons/icons'
import _ from 'lodash'
addIcons(_.mapKeys(allIcons, (value, key) => _.kebabCase(key))
After i tried everything, there weren't any errors reported anywehere, just icon wasn't there.
Please check twice, that you have set the color of the icon, because in default icon style color is inhereit.
So, if you have for example, a green button, the color of the icon will be also green, if you do not specify.

Ionic 4: popover does not work with right floating

I am trying to show popover when the user click on a button. However, when I position the button to right of the screen, the popover does not show up.
This works, file: basket.page.html
<ion-button slot="icon-only" shape="round" fill="outline" (click)="presentPopover($event)" >
<ion-icon button size="large" name="more"></ion-icon>
</ion-button>
<ion-card-title>
{{ basket.name }}
</ion-card-title>
but this does not work, when I click on the button, the popover does not show up. I just added class="float-right" to ion-button tag.
<ion-button class="float-right" slot="icon-only" shape="round" fill="outline" (click)="presentPopover($event)" >
<ion-icon button size="large" name="more"></ion-icon>
</ion-button>
<ion-card-title>
{{ basket.name }}
</ion-card-title>
I have this in basket.page.ts
async presentPopover(ev: any) {
this.popover = await this.popoverController.create({
component: PopoverComponent,
event: ev,
translucent: true,
componentProps: { basketId: this.basketId }
});
return await this.popover.present();
}