I am very new to ionic, currently working/learning with Ionic 2. I would like to show a toast when a user goes offline. I am currently able to do that as shown in my code below (toast shows whenever user goes offline). However what i would like to do is show the toast on http request (pull to refresh and infinite scroll). So that even when data is already loaded, the toast gets displayed when the user tries to pull to refresh on load more data through infinite scroll then they get notified that they are offline.
export class HomePage {
datas:any = [];
page:number =1;
connected: Subscription;
disconnected: Subscription;
constructor(private toast: ToastController, private network: Network, public navCtrl: NavController, private wpapi: Wpapi) {
this.getPosts();
}
displayNetworkUpdate(connectionState: string){
//let networkType = this.network.type
this.toast.create({
message: `You are currently ${connectionState}, please re connect your data`,
duration: 3000
}).present();
}
ionViewDidEnter() {
this.disconnected = this.network.onDisconnect().subscribe(data => {
console.log(data)
this.displayNetworkUpdate(data.type);
}, error => console.error(error));
}
getPosts() {
//this.page = '1';
//this.wpapi.index(this.page)
this.wpapi.index(1)
.subscribe(data => {
this.datas = data;
console.log(this.datas);
});
}
loadMore(infiniteScroll) {
this.page++;
this.wpapi.index( this.page ).subscribe( datas => {
// Loads posts from WordPress API
let length = datas["length"];
if( length === 0 ) {
infiniteScroll.complete();
return;
}
for (var i = length - 1; i >= 0; i--) {
this.datas.push( datas[i] );
}
infiniteScroll.complete();
});
}
doRefresh(refresher) {
this.wpapi.index(1)
.subscribe(data => {
this.datas = data;
refresher.complete();
});
}
ionViewWillLeave(){
this.connected.unsubscribe();
this.disconnected.unsubscribe();
}
}
This is what i'm doing. In my app.components i have the connection subscriber, beeing it offline or online, so if a user goes offline i save a conn boolean variable with false, if online i save true in my localStorage and present a toast saying it has gone offline (if gone online there's no need to present a toast).
network.onDisconnect().subscribe(() => {
storage.set('conn', false);
let conoff = ToastCtrl.create({
closeButtonText: 'Ok',
showCloseButton: true,
message: 'You're not connected to internet.',
position: 'top'
});
conoff.present();
});
You can create a service to do this, something like
import { Injectable } from '#angular/core';
import { Storage } from '#ionic/storage';
import { ToastController, Platform } from 'ionic-angular';
#Injectable()
export class Verificador {
constructor(public toast: ToastController, public storage: Storage, public platform: Platform) {
}
verifyConnection = (): Promise<boolean> => {
return new Promise<boolean>((res, rej) => {
this.storage.get('conn').then(conn => {
if (conn) {
res(true);
} else {
let t = this.toast.create({
closeButtonText: 'Ok',
showCloseButton: true,
message: 'You can't do this without internet.',
position: 'top'
});
t.present();
res(false);
}
})
})
}
}
So in every component, page entering, http call, you import that service/provider and call the verifyConnection function, if it returns true you just let the user do what it needs to do, if it's false the provider will show the toast.
import { ConnVerification} from '../../../providers/ConnVerification';
#IonicPage()
#Component({
selector: 'your-page',
templateUrl: 'your-page',
providers: [ConnVerification]
})
export class YourPage {
constructor(public verif: ConnVerification){}
myFunctionForSomething(){
this.verif.verifyConnection().then(conn =>{
if(conn){
// DO YOUR CODE
}
// NO NEED FOR ELSE SINCE IT'S HANDLED ON PROVIDER
})
}
}
Hope it helps :)
Related
I have an angular app with the Youtube IFrame API. When the website/pwa is used on an android device, and a Youtube video is playing, the app automatically creates a notification in the android notification tray, allowing you to see what video is currently playing.
I created my own playlist-controller, so I'm not using queueVideoById since I don't like the way the default youtube-iframe playlist-controller works (you cannot remove queued videos afterwards).
From my AppComponent I'm loading the YouTube IFrame API
app.component.ts
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent implements OnInit, OnDestroy {
constructor(private youtubeHelper: YoutubeHelper) {
this.youtubeHelper.loadApi().then((isLoaded) => {
this.player.playSong(song.playerInfo.id);
});
}
#ViewChild('player') player: YoutubePlayerComponent;
}
app.component.html
<body>
<youtube-player #player [domId]="'player'" ... (previousPressed)="onPreviousPressed()" (nextPressed)="onNextPressed()"></youtube-player>
</body>
This helper is used to load the IFrame API script. LoadApi inserts a script-tag with the src set to the youtube-iframe-url:
import { Injectable } from '#angular/core';
import { Promise } from 'q';
import { BehaviorSubject } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class YoutubeHelper {
static scriptTag: HTMLScriptElement = null;
public loadApi() {
return Promise<boolean>((resolve, reject) => {
if (typeof window !== 'undefined') {
if (YoutubeHelper.scriptTag === null) {
window['onYouTubeIframeAPIReady'] = () => {
this.apiReady.next(true);
resolve(true);
};
YoutubeHelper.scriptTag = window.document.createElement('script');
YoutubeHelper.scriptTag.src = 'https://www.youtube.com/iframe_api';
const firstScriptTag = window.document.getElementsByTagName('script')[0];
firstScriptTag.parentNode.insertBefore(YoutubeHelper.scriptTag, firstScriptTag);
} else {
resolve(true);
}
} else {
resolve(false);
}
});
}
public unloadApi() {
return Promise((resolve) => {
if (typeof window !== 'undefined') {
if (YoutubeHelper.scriptTag !== null) {
YoutubeHelper.scriptTag.parentNode.removeChild(YoutubeHelper.scriptTag);
YoutubeHelper.scriptTag = null;
//console.log('Removed YouTube iframe api');
this.apiReady.next(false);
}
}
});
}
public apiReady = new BehaviorSubject<boolean>(
(typeof window === 'undefined')
? false
: window['YT'] !== undefined
);
}
When the iframe api script is inserted in the dom, the YoutubePlayerComponent deals with this by creating an instance of YT.Player (this happens at the this.youtubeHelper.apiReady subscription):
/// <reference path="../../../../node_modules/#types/youtube/index.d.ts" />
import { Component, OnInit, Input, Output, EventEmitter } from '#angular/core';
import { YoutubeHelper } from '../../helpers/youtube-api.helper';
import { SongProgress } from '../../entities/song-progress';
import { Song } from '../../entities/song';
import { BehaviorSubject } from 'rxjs';
#Component({
selector: 'youtube-player',
templateUrl: './youtube-player.component.html',
styleUrls: ['./youtube-player.component.scss']
})
export class YoutubePlayerComponent implements OnInit {
constructor(private youtubeHelper: YoutubeHelper) {
}
#Input() domId: string;
// Properties omitted for breverity
#Output() previousPressed: EventEmitter<any> = new EventEmitter();
#Output() nextPressed: EventEmitter<any> = new EventEmitter();
private player: YT.Player;
private isPlayerReady = new BehaviorSubject<boolean>(false);
ngOnInit() {
}
ngAfterViewInit() {
this.youtubeHelper.apiReady.subscribe((ready) => {
if (ready && !this.player) {
this.player = new YT.Player(this.domId, {
width: this.width,
height: this.height,
playerVars: {
autoplay: this._autoplay
},
events: {
onReady: (event) => {
this.isPlayerReady.next(true);
},
onStateChange: (state: { data: YT.PlayerState }) => {
}
}
});
// Attempt to give the player methods my very own implementation
this.player.previousVideo = () => {
this.previousPressed.emit();
};
this.player.nextVideo = () => {
this.nextPressed.emit();
};
console.log('player', this.player);
}
});
}
static mediaInit = false;
public playSong(youtubeId: string) {
if (this.isPlayerReady.value) {
this.player.loadVideoById(youtubeId);
} else {
console.log('Player not yet ready');
this.isPlayerReady.subscribe((value) => {
if (value === true) {
this.player.loadVideoById(youtubeId);
}
});
}
// Here is the code that's supposed to interact with the media session in the iframe.
if (!YoutubePlayerComponent.mediaInit) {
YoutubePlayerComponent.mediaInit = true;
let frame = <HTMLIFrameElement>document.getElementById(this.domId);
frame.contentWindow.navigator.mediaSession.setActionHandler('previoustrack', () => {
this.previousPressed.emit();
});
frame.contentWindow.navigator.mediaSession.setActionHandler('nexttrack', () => {
this.nextPressed.emit();
});
}
}
public play() { this.player.playVideo(); }
// Pause, stop methods omitted for breverity
}
After that the isPlayerReady is triggered, allowing a video to be played:
When calling YoutubePlayerComponent.playSong, it either waits for the YoutubePlayer to be ready (isPlayerReady) or immediately calls this.player.loadVideoById, which plays the requested video.
Having said this, the question is:
By default when a video is playing inside my website/pwa it displays a media notification as shown in the first image. I want to be able to navigate to the previous/next track in my own playlist. So I want to intercept these events:
According to this link it's possible to modify the mediaSession of the Window after playing a video, attaching custom handlers to the previous/next buttons in the android tray. But since the video (and off course the mediaSession) is being played from the iframe, it's not possible to modify the metadata:
navigator.mediaSession.metadata = new MediaMetadata({
title: 'Never Gonna Give You Up',
artist: 'Rick Astley',
album: 'Whenever You Need Somebody'
});
would become:
document.getElementById('player').contentWindow.navigator.mediaSession.metadata = new MediaMetadata({
title: 'Never Gonna Give You Up',
artist: 'Rick Astley',
album: 'Whenever You Need Somebody'
});
And just to check, while a video is playing the mediaSession of the mainWindow is empty:
navigator.mediaSession.metadata
> null
And the actual mediaSession should be available in:
document.getElementById("player").contentWindow.navigator.mediaSession
> Blocked a frame with origin "https://example.com" from accessing a cross-origin frame
So is there any way I can add previous/next buttons to the notification while using the Youtube iframe API, and handle these events?
If necessary, the entire project is available on GitHub
So, I'm quite a noob when it comes to ionic 4 firebase integration. Basically I've managed to get the firebase authentication to work, but the problem is I can't seem to save the user's input in the sign up page to firebase database. I need it to work as I would like to link the user to his/her checklist.
This is my sign-up page.ts
import { Component, OnInit } from '#angular/core';
import { Router } from "#angular/router";
import { Platform, AlertController } from '#ionic/angular';
import { LoadingController, ToastController } from '#ionic/angular';
import { AngularFireAuth } from '#angular/fire/auth';
//disable side menu
import { MenuController } from '#ionic/angular';
#Component({
selector: 'app-signup',
templateUrl: './signup.page.html',
styleUrls: ['./signup.page.scss'],
})
export class SignupPage implements OnInit {
email: string = '';
password: string = '';
error: string = '';
username: string = '';
constructor(
private fireauth: AngularFireAuth,
public router: Router,
public menuCtrl: MenuController,
private toastController: ToastController,
private platform: Platform,
public loadingController: LoadingController,
public alertController: AlertController
) { }
async openLoader() {
const loading = await this.loadingController.create({
message: 'Please Wait ...',
duration: 2000
});
await loading.present();
}
async closeLoading() {
return await this.loadingController.dismiss();
}
signup() {
this.fireauth.auth.createUserWithEmailAndPassword(this.email, this.password)
.then(res => {
if (res.user) {
console.log(res.user);
this.updateProfile();
}
})
.catch(err => {
console.log(`login failed ${err}`);
this.error = err.message;
});
}
updateProfile() {
this.fireauth.auth.onAuthStateChanged((user) => {
if (user) {
console.log(user);
user.updateProfile({
displayName: this.username
})
.then(() => {
this.router.navigateByUrl('/login');
})
}
})
}
async presentToast(message, show_button, position, duration) {
const toast = await this.toastController.create({
message: message,
showCloseButton: show_button,
position: position,
duration: duration
});
toast.present();
}
ionViewWillEnter (){
this.menuCtrl.enable(false);
}
ngOnInit(){}
}
Actually, you aren't using the firebase database. The user.updateProfile method stores the displayName property in the Firebase Auth system. That information is saved in the Firebase Auth system.
I have a ionic + firebase chat app and I'd like to send a notification to a user when a new message is received.
In the Ionic oficial documentation, the recommended plugin for this is:
https://github.com/katzer/cordova-plugin-local-notifications
But as you can see inside the repository, it hasnt been updated since last february and based in comments inside it, it seems like it doesnt work with last Android OS versions.
Does anyone know an alternative?
Thanks!
I had the same problem to implement push notifications in my app last month. This is the best tutorial to do that: https://medium.com/#senning/push-notifications-with-ionic-and-cordova-plugin-firebase-ab0c0cad3cc0
I customized this tutorial to a file: messaging.service.ts
import {Injectable} from '#angular/core';
import {ApiService} from './api.service';
import {AppApiResponse} from '../interfaces/interfaces'
import {Firebase} from "#ionic-native/firebase";
import {Platform} from "ionic-angular";
import {AngularFirestore} from "#angular/fire/firestore";
#Injectable()
export class MessagingService {
private userUid: string;
constructor(private firebase: Firebase,
public afs: AngularFirestore,
public platform: Platform) {
}
initializeFirebase(userUid) {
this.userUid = userUid;
if (!this.platform.is("core")) {
this.firebase.subscribe("all");
this.platform.is('android') ? this.initializeFirebaseAndroid() : this.initializeFirebaseIOS();
}
}
initializeFirebaseAndroid() {
this.firebase.getToken().then(token => {
this.saveTokenToFirestore(token);
console.log('token android= ' + JSON.stringify(token));
});
this.firebase.onTokenRefresh().subscribe(token => {
this.saveTokenToFirestore(token);
console.log('token refresh android= ' + JSON.stringify(token));
});
this.subscribeToPushNotifications();
}
initializeFirebaseIOS() {
this.firebase.grantPermission()
.then(() => {
this.firebase.getToken().then(token => {
this.saveTokenToFirestore(token);
console.log('token ios= ' + JSON.stringify(token));
});
this.firebase.onTokenRefresh().subscribe(token => {
this.saveTokenToFirestore(token);
console.log('token refresh ios= ' + JSON.stringify(token));
});
this.subscribeToPushNotifications();
})
.catch((error) => {
this.firebase.logError('push erro ios= ' + error);
});
}
subscribeToPushNotifications() {
this.firebase.onNotificationOpen().subscribe((response) => {
console.log('response push= ' + JSON.stringify(response));
if (response.tap) {
//Received while app in background (this should be the callback when a system notification is tapped)
//This is empty for our app since we just needed the notification to open the app
} else {
//received while app in foreground (show a toast)
}
});
}
private saveTokenToFirestore(token) {
if (!token) return;
const devicesRef = this.afs.collection('devices');
const docData = {
token,
userId: this.userUid,
};
return devicesRef.doc(token).set(docData)
}
}
To use in your code is just include to page constructor:
public msgService: MessagingService
And use:
try {
this.msgService.initializeFirebase(user.uid);
} catch (error) {
console.log('fire push erro= ' + error);
}
Been trying to fetch products from WooCommerce REST API now forever and my brain is bleeding :'( I followed all instructions on woocommerce and github/woocommerce and I can't for my life get anything in Postman with Basic Auth:
But when I select Auth 1.0 - I get all products:
But then if I take the Auth 1.0 generated URL and put it in my browser:
..Instructions under Authentication over HTTP (here) describes the parameters which are generated in URL automatically when i select Auth 1.0 in Postman - but how am I gonna generate those in my React component?
const APP_URL = 'http://0.0.0.0:80'
const CONSUMER_KEY = 'ck_xxxx'
const CONSUMER_SECRET = 'cs_xxxx'
const PRODUCT_URL = `${APP_URL}/wp-json/wc/v2/products?consumer_key=${CONSUMER_KEY}&consumer_secret=${CONSUMER_SECRET}`
fetch(PRODUCT_URL).then(res => {
if(res.status === 200){
return json
} else {
console.log("ERROR RESPONSE STATUS: " + status);
}
}).then( json => {
console.log(json)
})
})
So thankful for all input!
I think this problem may be solved by below code using "Interceptor" concept...
import {
Injectable,
// Injector
} from '#angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
HttpErrorResponse
} from '#angular/common/http';
// import { Router } from '#angular/router';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
// import { AuthService } from './auth.service';
import { config } from '../config/config';
#Injectable()
export class AppInterceptor implements HttpInterceptor {
constructor(
// private injector: Injector,
// private router: Router
) { }
private includeWooAuth(url) {
const wooAuth = `consumer_key=${config.consumerKey}&consumer_secret=${config.secretKey}`;
const hasQuery = url.includes('?');
let return_url = '';
if (hasQuery) {
return_url = wooAuth;
} else {
return_url = '?' + wooAuth;
}
return return_url;
}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
// const auth = this.injector.get(AuthService);
const authRequest = request.clone({
setHeaders: {
// Authorization: `Bearer ${auth.getToken()}`
},
url: `${config.basePath}/${request.url}${this.includeWooAuth(request.url)}`
});
return next.handle(authRequest)
.catch(err => {
if (err instanceof HttpErrorResponse && err.status === 0) {
console.log('Check Your Internet Connection And Try again Later');
} else if (err instanceof HttpErrorResponse && err.status === 401) {
// auth.setToken(null);
// this.router.navigate(['/', 'login']);
}
return Observable.throw(err);
});
}
}
This code will be kept into http.interceptor.ts. Obviously, you should initialize the consumer key and other details of woocommerce API into a constant variable. After that you create a service for show the list of the product like this:
retriveProducts(query: ProductQuery = {}): Observable<RetriveProductsResponse> {
return this.httpClient.get(`products`, {params: this.wooHelper.includeQuery(query), observe: 'response'})
.pipe(
map(value => this.wooHelper.includeResponseHeader(value)),
catchError(err => this.wooHelper.handleError(err)));
}
And call this service to the product.ts file like this:
getProducts() {
this.woocommerceProductsService.retriveProducts()
.subscribe(productResponse => {
this.products = productResponse.products;
}, error => this.errorMessage = <any>error);
}
I have used these above code into my project. I think it will help you.
Hi all I'M working on angularjs 2/ionic2 mobile app , i need to do pulling refresher in my app, we have tried this link:- https://ionicframework.com/docs/v2/api/components/refresher/Refresher/ process i got refreshing the page but it's not get dismissLoader, we have given the images of my app refreshing:-
we don't know where we did the mistake and where we need to add the correct functionality in my project...
while we pulling the page it's refreshing but it's not get dismiss, Refreshing text and icon showing it's not get dismissed...
what we expecting once we pulled the page it's need to refresh after that refreshing text and icon need to be dismiss...
**we added coding only in html:-****
<ion-refresher (ionRefresh)="setFilteredItems($event)">
<ion-refresher-content refreshingSpinner="circles" refreshingText="Refreshing...">
</ion-refresher-content>
</ion-refresher>
we have not added anything in type script part...so please check and update the solution please....
we have created example Plunker
please update the plunker as well to know the solution, thanks.....
My Type Script constructor code:-
import { Component } from '#angular/core';
import { NavController } from 'ionic-angular';
import { GlobalStateService } from '../../services/global-state.service';
import { AccountSigninPage } from '../account-signin/account-signin';
import { AccountSignupPage } from '../account-signup/account-signup';
import { ActivityAddPage } from '../activity-add/activity-add';
import { Activity } from "../../services/actopi-sdk/model/Activity";
import { UserLoginService } from "../../services/account-management.service";
import { ResourceListPage } from '../resource-list/resource-list';
import { IamAuthorizerClient } from "../../services/actopi-api.service";
import { CustomAuthorizerClient, NoAuthorizationClient, UserPoolsAuthorizerClient } from "../../services/actopi-api.service";
import { Config } from '../../config/config'
import { Logger } from '../../services/logger.service';
import 'rxjs/add/operator/debounceTime';
import { FormControl } from '#angular/forms';
declare const AWS: any;
#Component({
templateUrl: 'activity-list.html',
})
export class ActivityListPage {
initialized = false;
accountSigninPage = AccountSigninPage;
accountSignupPage = AccountSignupPage;
activityAddPage = ActivityAddPage;
activitys: Activity[] = [];
resourceListPage = ResourceListPage;
searchTerm: string = '';
searchControl: FormControl;
displayDeleteActivityConfirmation(activityId, activityName) {
console.log("Deleting activityID " + activityId);
let confirm = this.globals.getAlertController().create({
title: 'Delete activity?',
message: `Are you sure you want to delete [<b>${activityName}</b>]? All resources and bookings associated with [<b>${activityName}</b>] will also be deleted!`,
buttons: [
{
text: 'Cancel',
handler: () => { /* do nothing */ }
},
{
text: 'OK',
handler: () => {
this.deleteActivity(activityId)
.then(() => {
this.globals.dismissLoader();
this.globals.displayToast(`Activity [${activityName}] has been successfully deleted`);
})
.catch((err) => {
this.globals.dismissLoader();
this.globals.displayAlert('Error encountered',
'Delete failed. Please check the console logs for more information.');
console.log(err);
});
}
}
]
});
confirm.present();
}
deleteActivity(activityId): Promise<void> {
return new Promise<void>((resolve, reject) => {
// delete from the database
this.globals.displayLoader("Deleting...");
this.customAuthClient.getClient().activitysDelete(activityId).subscribe(
() => {
// remove the item from the activitys array
let index = this.activitys.findIndex(activity => { return activity.activityId == activityId });
if (index > -1) {
this.activitys.splice(index, 1);
}
resolve();
},
(err) => {
reject(err);
}
);
});
}
gotoResourceListPage(activity) {
this.navCtrl.push(ResourceListPage, activity);
}
filterItems(searchTerm): void {
this.activitys = [];
this.userPoolsAuthClient.getClient().activitysList().subscribe(
(data) => {
this.activitys = data.items.filter((activity) => {
return activity.name.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1;
});
this.globals.dismissLoader();
this.initialized = true;
},
(err) => {
this.globals.dismissLoader();
this.initialized = true;
console.error(err);
this.globals.displayAlert('Error encountered',
`An error occurred when trying to load the activitys. Please check the console logs for more information.`)
});
}
loadActivitysWithAuth(): void {
this.activitys = [];
this.userPoolsAuthClient.getClient().activitysList().subscribe(
(data) => {
// this.activitys = data.items
// sort by name
let searchTerm: string = '';
// this.activitys = data.items.sort((a, b) => {
// return a.name.localeCompare(b.name);
// });
this.globals.dismissLoader();
this.initialized = true;
},
(err) => {
this.globals.dismissLoader();
this.initialized = true;
console.error(err);
this.globals.displayAlert('Error encountered',
`An error occurred when trying to load the activitys. Please check the console logs for more information.`)
}
);
};
loadActivitysWithoutAuth(): void {
this.activitys = [];
this.noAuthClient.getClient().activitysList().subscribe(
(data) => {
// this.activitys = data.items
// sort by name
this.activitys = data.items.sort((a, b) => {
return a.name.localeCompare(b.name);
});
this.globals.dismissLoader();
this.initialized = true;
},
(err) => {
this.globals.dismissLoader();
this.initialized = true;
console.error(err);
this.globals.displayAlert('Error encountered',
`An error occurred when trying to load the activitys. Please check the console logs for more information.`)
}
);
};
constructor(private navCtrl: NavController, public globals: GlobalStateService, private noAuthClient: NoAuthorizationClient, private customAuthClient: CustomAuthorizerClient, private userPoolsAuthClient: UserPoolsAuthorizerClient, private authClient: IamAuthorizerClient) {
this.searchControl = new FormControl();
}
ionViewDidEnter() {
Logger.banner("Activitys");
this.activitys = [];
if (!this.initialized) {
this.initialized = false;
if (UserLoginService.getAwsAccessKey() != null) {
// if (CognitoUtil.getUserState() === UserState.SignedIn) {
// console.log(AWS.config.credentials);
UserLoginService.getAwsCredentials()
.then((data) => {
this.globals.displayLoader("Loading...");
this.setFilteredItems(refresher);
this.searchControl.valueChanges.debounceTime(700).subscribe(search => {
this.globals.displayLoader("Loading...");
this.setFilteredItems(refresher);
this.globals.dismissLoader();
});
})
.catch((err) => {
console.log("ERROR: Unable to load activitys!");
console.log(err)
})
}
}
}
setFilteredItems(refresher) {
return this.filterItems(this.searchTerm);
refresher.complete();
}
}
You need to call refresher.complete() to dismiss your refresher once loading of new data is done..
setFilteredItems(refresher?:any){
//async call to load.
// in the then function
if(refresher)
refresher.complete();
}
}
The refresher is sent from the html onRefresh. With ? you can call without passing object in your code.
this.setFilteredItems();
Also consider refactoring your code. You should ideally call complete after async task ia done and no point in returning another function to the html side and calling complete after return will just end up as dead code.