title: Notification service - Angular 2
body: <p>I am strictly following a tutorial on how to implement a push and toasts notifcation service. i have run into this error and i seem not to why the error keeps coming up. Below is the error and the code for the simple notification stack in angular 2</p>
  
  <p><strong>error</strong></p>
  
  <pre><code>` Uncaught (in promise): Error: Error in :0:0 caused by: this.item is undefined
  BaseError@http://localhost:4200/vendor.bundle.js:31569:9 [angular]
  WrappedError@http://localhost:4200/vendor.bundle.js:31634:9 [angular]
  ViewWrappedError@http://localhost:4200/vendor.bundle.js:62968:9 [angular]
  DebugAppView.prototype._rethrowWithContext@http://localhost:4200/vendor.bundle.js:87491:23 [angular]
  DebugAppView.prototype.detectChanges@http://localhost:4200/vendor.bundle.js:87464:13 [angular]
  ViewRef_.prototype.detectChanges@http://localhost:4200/vendor.bundle.js:63895:9 [angular]
  RouterOutlet.prototype.activate@http://localhost:4200/vendor.bundle.js:69403:9 [angular]
  ActivateRoutes.prototype.placeComponentIntoOutlet@http://localhost:4200/vendor.bundle.js:26433:9 [angular]
  ActivateRoutes.prototype.activateRoutes@http://localhost:4200/vendor.bundle.js:26400:21 [angular]
  ActivateRoutes.prototype.activateChildRoutes/&lt;@http://localhost:4200/vendor.bundle.js:26336:52 [angular]
  ActivateRoutes.[…]`
  </code></pre>
  
  <p><strong>Notification.Component.ts</strong></p>
  
  <pre class="lang-js prettyprint-override"><code>import {Component, OnInit, OnDestroy, Input, ViewEncapsulation, NgZone,
  trigger, state, style, transition, animate
  } from '@angular/core';
  import {DomSanitizer, SafeHtml} from '@angular/platform-browser';
  import {Notification} from '../interfaces/notification.type';
  import {NotificationsService} from '../services/notifications.service';
  
  @Component({
      selector: 'simple-notification',
      encapsulation: ViewEncapsulation.None,
      animations: [
          trigger('enterLeave', [
  
              // Enter from right
              state('fromRight', style({opacity: 1, transform: 'translateX(0)'})),
              transition('* =&gt; fromRight', [
                  style({opacity: 0, transform: 'translateX(5%)'}),
                  animate('400ms ease-in-out')
              ]),
              state('fromRightOut', style({opacity: 0, transform: 'translateX(-5%)'})),
              transition('fromRight =&gt; fromRightOut', [
                  style({opacity: 1, transform: 'translateX(0)'}),
                  animate('300ms ease-in-out')
              ]),
  
              // Enter from left
              state('fromLeft', style({opacity: 1, transform: 'translateX(0)'})),
              transition('* =&gt; fromLeft', [
                  style({opacity: 0, transform: 'translateX(-5%)'}),
                  animate('400ms ease-in-out')
              ]),
              state('fromLeftOut', style({opacity: 0, transform: 'translateX(5%)'})),
              transition('fromLeft =&gt; fromLeftOut', [
                  style({opacity: 1, transform: 'translateX(0)'}),
                  animate('300ms ease-in-out')
              ]),
  
              // Rotate
              state('scale', style({opacity: 1, transform: 'scale(1)'})),
              transition('* =&gt; scale', [
                  style({opacity: 0, transform: 'scale(0)'}),
                  animate('400ms ease-in-out')
              ]),
              state('scaleOut', style({opacity: 0, transform: 'scale(0)'})),
              transition('scale =&gt; scaleOut', [
                  style({opacity: 1, transform: 'scale(1)'}),
                  animate('400ms ease-in-out')
              ]),
  
              // Scale
              state('rotate', style({opacity: 1, transform: 'rotate(0deg)'})),
              transition('* =&gt; rotate', [
                  style({opacity: 0, transform: 'rotate(5deg)'}),
                  animate('400ms ease-in-out')
              ]),
              state('rotateOut', style({opacity: 0, transform: 'rotate(-5deg)'})),
              transition('rotate =&gt; rotateOut', [
                  style({opacity: 1, transform: 'rotate(0deg)'}),
                  animate('400ms ease-in-out')
              ])
          ])
      ],
      template: `
          &lt;div class="simple-notification"
              [@enterLeave]="item.state"
              (click)="onClick($event)"
              [class]="theClass"
              [ngClass]="{
                  'alert': item.type === 'alert',
                  'error': item.type === 'error',
                  'success': item.type === 'success',
                  'info': item.type === 'info',
                  'bare': item.type === 'bare',
                  'rtl-mode': rtl
              }"
              (mouseenter)="onEnter()"
              (mouseleave)="onLeave()"&gt;
              &lt;div *ngIf="!item.html"&gt;
                  &lt;div class="sn-title"&gt;{{item.title}}&lt;/div&gt;
                  &lt;div class="sn-content"&gt;{{item.content | max:maxLength}}&lt;/div&gt;
                  &lt;div class="icon" *ngIf="item.icon !== 'bare'" [innerHTML]="safeSvg"&gt;&lt;/div&gt;
              &lt;/div&gt;
              &lt;div *ngIf="item.html" [innerHTML]="item.html"&gt;&lt;/div&gt;
              &lt;div class="sn-progress-loader" *ngIf="showProgressBar"&gt;
                  &lt;span [ngStyle]="{'width': progressWidth + '%'}"&gt;&lt;/span&gt;
              &lt;/div&gt;
          &lt;/div&gt;
      `,
  
  
      providers: [NotificationsService]
  })
  
  export class NotificationComponent implements OnInit, OnDestroy {
  
      @Input() public timeOut: number;
      @Input() public showProgressBar: boolean;
      @Input() public pauseOnHover: boolean;
      @Input() public clickToClose: boolean;
      @Input() public maxLength: number;
      @Input() public theClass: string;
      @Input() public rtl: boolean;
      @Input() public animate: string;
      @Input() public position: number;
      @Input() public item: Notification;
  
  
      // Progress bar variables
      public progressWidth = 0;
      private stopTime = false;
      private timer: any;
      private steps: number;
      private speed: number;
      private count = 0;
      private start: any;
      private diff: any;
  
      private icon: string;
      private safeSvg: SafeHtml;
  
      constructor(
          private notificationService: NotificationsService,
          private domSanitizer: DomSanitizer,
          private zone: NgZone
      ) {}
  
      ngOnInit(): void {
          if (this.animate) {
              this.item.state = this.animate;
          }
          if (this.item.override) {
              this.attachOverrides();
          }
          if (this.timeOut !== 0) {
              this.startTimeOut();
          }
  
          this.safeSvg = this.domSanitizer.bypassSecurityTrustHtml(this.icon || this.item.icon);
      }
  
      startTimeOut(): void {
          this.steps = this.timeOut / 10;
          this.speed = this.timeOut / this.steps;
          this.start = new Date().getTime();
          this.zone.runOutsideAngular(() =&gt; this.timer = setTimeout(this.instance, this.speed));
      }
  
      onEnter(): void {
          if (this.pauseOnHover) {
              this.stopTime = true;
          }
      }
  
      onLeave(): void {
          if (this.pauseOnHover) {
              this.stopTime = false;
              setTimeout(this.instance, (this.speed - this.diff));
          }
      }
  
      setPosition(): number {
          return this.position !== 0 ? this.position * 90 : 0;
      }
  
      onClick($e: MouseEvent): void {
          this.item.click!.emit($e);
  
          if (this.clickToClose) {
              this.remove();
          }
      }
  
      // Attach all the overrides
      attachOverrides(): void {
          Object.keys(this.item.override).forEach(a =&gt; {
              if (this.hasOwnProperty(a)) {
                  (&lt;any&gt;this)[a] = this.item.override[a];
              }
          });
      }
  
      ngOnDestroy(): void {
          clearTimeout(this.timer);
      }
  
      private instance = () =&gt; {
          this.zone.runOutsideAngular(() =&gt; {
              this.zone.run(() =&gt; this.diff = (new Date().getTime() - this.start) - (this.count * this.speed));
  
              if (this.count++ === this.steps) this.zone.run(() =&gt; this.remove());
              else if (!this.stopTime) {
                  if (this.showProgressBar) this.zone.run(() =&gt; this.progressWidth += 100 / this.steps);
  
                  this.timer = setTimeout(this.instance, (this.speed - this.diff));
              }
          })
      };
  
      private remove() {
          if (this.animate) {
              this.item.state = this.animate + 'Out';
              this.zone.runOutsideAngular(() =&gt; {
                  setTimeout(() =&gt; {
                      this.zone.run(() =&gt; this.notificationService.set(this.item, false))
                  }, 310);
              })
          } else {
              this.notificationService.set(this.item, false);
          }
      }
  }
  </code></pre>
  
  <p><strong>simple-nofitication</strong></p>
  
  <pre class="lang-js prettyprint-override"><code>@Component({
      selector: 'simple-notifications',
      encapsulation: ViewEncapsulation.None,
      template: `
          &lt;div class="simple-notification-wrapper" [ngClass]="position"&gt;
              &lt;simple-notification
                  *ngFor="let a of notifications; let i = index"
                  [item]="a"
                  [timeOut]="timeOut"
                  [clickToClose]="clickToClose"
                  [maxLength]="maxLength"
                  [showProgressBar]="showProgressBar"
                  [pauseOnHover]="pauseOnHover"
                  [theClass]="theClass"
                  [rtl]="rtl"
                  [animate]="animate"
                  [position]="i"
                  &gt;
              &lt;/simple-notification&gt;
          &lt;/div&gt;
      `,
  
      `],
      providers: [NotificationsService]
  })
  
  export class SimpleNotificationsComponent implements OnInit, OnDestroy {
  
      @Input() set options(opt: Options) {
          this.attachChanges(opt);
      }
  
      @Output() onCreate = new EventEmitter();
      @Output() onDestroy = new EventEmitter();
  
      public notifications: Notification[] = [];
      public position: ['top' | 'bottom', 'right' | 'left'] = ['bottom', 'right'];
  
      private lastNotificationCreated: Notification;
      private listener: Subscription;
  
      // Received values
      private lastOnBottom = true;
      private maxStack = 8;
      private preventLastDuplicates: any = false;
      private preventDuplicates = false;
  
      // Sent values
      public timeOut = 0;
      public maxLength = 0;
      public clickToClose = true;
      public showProgressBar = true;
      public pauseOnHover = true;
      public theClass = '';
      public rtl = false;
      public animate: 'fromRight' | 'fromLeft' | 'rotate' | 'scale' = 'fromRight';
  
      constructor(private _service: NotificationsService) {}
  
      ngOnInit(): void {
          // Listen for changes in the service
          this.listener = this._service.getChangeEmitter()
              .subscribe(item =&gt; {
                  switch (item.command) {
                      case 'cleanAll':
                          this.notifications = [];
                          break;
  
                      case 'clean':
                          this.cleanSingle(item.id!);
                          break;
  
                      case 'set':
                          if (item.add) this.add(item.notification!);
                          else this.defaultBehavior(item);
                          break;
  
                      default:
                          this.defaultBehavior(item);
                          break;
                  }
              });
      }
  
      // Default behavior on event
      defaultBehavior(value: any): void {
          this.notifications.splice(this.notifications.indexOf(value.notification), 1);
          this.onDestroy.emit(this.buildEmit(value.notification, false));
      }
  
  
      // Add the new notification to the notification array
      add(item: Notification): void {
          item.createdOn = new Date();
  
          let toBlock: boolean = this.preventLastDuplicates || this.preventDuplicates ? this.block(item) : false;
  
          // Save this as the last created notification
          this.lastNotificationCreated = item;
  
          if (!toBlock) {
              // Check if the notification should be added at the start or the end of the array
              if (this.lastOnBottom) {
                  if (this.notifications.length &gt;= this.maxStack) this.notifications.splice(0, 1);
                  this.notifications.push(item);
              } else {
                  if (this.notifications.length &gt;= this.maxStack) this.notifications.splice(this.notifications.length - 1, 1);
                  this.notifications.splice(0, 0, item);
              }
  
              this.onCreate.emit(this.buildEmit(item, true));
          }
      }
  
      // Check if notifications should be prevented
      block(item: Notification): boolean {
  
          let toCheck = item.html ? this.checkHtml : this.checkStandard;
  
          if (this.preventDuplicates &amp;&amp; this.notifications.length &gt; 0) {
              for (let i = 0; i &lt; this.notifications.length; i++) {
                  if (toCheck(this.notifications[i], item)) {
                      return true;
                  }
              }
          }
  
          if (this.preventLastDuplicates) {
  
              let comp: Notification;
  
              if (this.preventLastDuplicates === 'visible' &amp;&amp; this.notifications.length &gt; 0) {
                  if (this.lastOnBottom) {
                      comp = this.notifications[this.notifications.length - 1];
                  } else {
                      comp = this.notifications[0];
                  }
              } else if (this.preventLastDuplicates === 'all' &amp;&amp; this.lastNotificationCreated) {
                  comp = this.lastNotificationCreated;
              } else {
                  return false;
              }
              return toCheck(comp, item);
          }
  
          return false;
      }
  
      checkStandard(checker: Notification, item: Notification): boolean {
          return checker.type === item.type &amp;&amp; checker.title === item.title &amp;&amp; checker.content === item.content;
      }
  
      checkHtml(checker: Notification, item: Notification): boolean {
          return checker.html ? checker.type === item.type &amp;&amp; checker.title === item.title &amp;&amp; checker.content === item.content &amp;&amp; checker.html === item.html : false;
      }
  
      // Attach all the changes received in the options object
      attachChanges(options: any): void {
          Object.keys(options).forEach(a =&gt; {
              if (this.hasOwnProperty(a)) {
                  (&lt;any&gt;this)[a] = options[a];
              }
          });
      }
  
      buildEmit(notification: Notification, to: boolean) {
          let toEmit: Notification = {
              createdOn: notification.createdOn,
              type: notification.type,
              icon: notification.icon,
              id: notification.id
          };
  
          if (notification.html) {
              toEmit.html = notification.html;
          } else {
              toEmit.title = notification.title;
              toEmit.content = notification.content;
          }
  
          if (!to) {
              toEmit.destroyedOn = new Date();
          }
  
          return toEmit;
      }
  
      cleanSingle(id: string): void {
          let indexOfDelete = 0;
          let doDelete = false;
  
          this.notifications.forEach((notification, idx) =&gt; {
              if (notification.id === id) {
                  indexOfDelete = idx;
                  doDelete = true;
              }
          });
  
          if (doDelete) {
              this.notifications.splice(indexOfDelete, 1);
          }
      }
  
      ngOnDestroy(): void {
          if (this.listener) {
              this.listener.unsubscribe();
          }
      }
  </code></pre>