4

I have an Ionic 2 app that has toast notifications in various places.

A good example of this is where the user updates their profile on the app and I run some validation checks. If the user fails some validation I might call the following:

      let toast = this.toastCtrl.create({
        message: 'Sorry, your password must be at least 6 characters long.  Your account was not updated.',
        duration: 3000,
        position: 'top'
      });
      toast.present();

No problems there. It just displays for 3 seconds then disappears.

The problem comes when multiple are shown at once. For example, the user may type a 6 character password, but it doesn't validate for another reason, so another toast notification is raised:

    let toast = this.toastCtrl.create({
      message: 'Sorry, your passwords do not match.  Your account was not updated.',
      duration: 3000,
      position: 'top'
    });
    toast.present();

This causes 2 toasts to overlap and one will permanently remain. The two overlapping isn't an issue, but the fact one remains indefinitely is a huge issue.

I imagine this is because I'm effectively overwriting the toast variable every time.

What is the best way to approach this? I don't want to have toast1, toast2, etc, as that won't fix the problem because the user might launch the same toast notification twice (<6 character password, submit twice).

Jakegarbo
  • 1,201
  • 10
  • 20
Mike
  • 8,767
  • 8
  • 49
  • 103
  • How do you create your toast? I created multiple toast at a same time and all work as expect. – Duannx Aug 09 '17 at 10:53
  • 1
    Just lots of `let toast = this.toastCtrl.create({ ... }); toast.present();`. I've only tested on the lab (`ionic serve --lab`), but assume it's the same on device. – Mike Aug 09 '17 at 11:02
  • Please check [this answer](https://stackoverflow.com/questions/45074161/prevent-duplicate-toast-messages-in-ionic2/45074283#45074283). By using the same property for all the toast, you could show only one toast each time (since it doesn't make sense to overlap toasts if they're validation messages). – sebaferreras Aug 09 '17 at 11:11
  • @sebaferreras: Do you know how to reproduce this problem? I tried many ways but can not face it. LOL – Duannx Aug 09 '17 at 11:15

2 Answers2

8

I would suggest handling all Toast interactions in a service. And inject it in whatever component/page/service you need it in. In the service you keep a reference to a single Toast and call dismiss() on it before presenting it. This solution will keep you from having more than one Toast presented at a time.

ToastService:

import { Injectable } from '@angular/core';
import { ToastController, Toast } from 'ionic-angular';

@Injectable()
export class ToastService{
    toast: Toast = null;

    constructor(private toastCtrl: ToastController){ }

    presentToast(text:string):void{
        let toastData = {
            message: text,
            duration: 3000,
            position: 'top'
        }

        this.showToast(toastData);
    }

    presentClosableToast(text:string):void{
        let toastData = {
            message: text,
            showCloseButton: true,
            closeButtonText: 'X',
            position: 'top' 
        };

        this.showToast(toastData);
    }

    private showToast(data:any):void{
        this.toast ? this.toast.dismiss() : false;
        this.toast = this.toastCtrl.create(data);
        this.toast.present();
    }
}
robbannn
  • 5,001
  • 1
  • 32
  • 47
  • 2
    I must say that I'm not sure if this is a good approach, since providers are not mean to be used to interact to the end user (or the UI) directly. A better way to do this would be to create a `BaseComponent` with that code, and extend your page with it. Or if you use toasts only in a single page, add this code to that page instead. – sebaferreras Aug 09 '17 at 15:03
  • 2
    I agree @sebaferreras. Is there a difference between extending a `class` and a `component` in this case? I made an edit yesterday (see edit history) where I extend a `class`. I used a `class` since only functionality and no template is needed. – robbannn Aug 11 '17 at 06:39
  • The main difference would be that _components_ also have metadata (decorators), and even tough metadata it's not important in this example, it may cause some errors when building for production (it only happens in some version of Ionic) , so you can add an _empty decorator_ to avoid that. Please take a look at [this SO answer](https://stackoverflow.com/questions/43029212/typescript-and-multiple-classes/43030491#43030491) where I create a `BaseComponent` to handle showing/creating toasts :) – sebaferreras Aug 11 '17 at 07:01
0

you can do it like this.

when you need to show a toast . call as a function . inside the function. you have a timer for 3 seconds. then if the toast function is recall again . you need to clear the timer then reset it again. like this code.

//delacare timer
_timer:any = null;

showToast(){
    let toast:any;
    //check if timer is running ,if its clear out and dismiss existing toast  
    if (this._timer) { 
         toast.dismiss()
         clearTimeout(this._timer)
    };

    this._timer = setTimeout(() => {
       toast = this.toastCtrl.create({
        message: 'Sorry, your passwords do not match.  Your account was not updated.', 
       position: 'top'
    });
    toast.present();
    },3000)

}

UPDATE

or you can also put a string argument like this . to avoid many toast code.

showToast(string_message){
        let toast:any;
        //check if timer is running it its . clear out  
        if (this._timer) { 
             toast.dismiss()
             clearTimeout(this._timer)
        };

        this._timer = setTimeout(() => {
           toast = this.toastCtrl.create({
            message: string_message, 
           position: 'top'
        });
        toast.present();
        },3000)

    }
Jakegarbo
  • 1,201
  • 10
  • 20