通知服务 - Angular 2

时间:2017-04-12 07:34:04

标签: angular push-notification toast

我严格遵循如何实施推送和敬酒通知服务的教程。我遇到了这个错误,我似乎没有为什么错误不断出现。下面是角度2

中的错误和简单通知堆栈的代码

错误

` 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/<@http://localhost:4200/vendor.bundle.js:26336:52 [angular]
ActivateRoutes.[…]`

Notification.Component.ts

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('* => fromRight', [
                style({opacity: 0, transform: 'translateX(5%)'}),
                animate('400ms ease-in-out')
            ]),
            state('fromRightOut', style({opacity: 0, transform: 'translateX(-5%)'})),
            transition('fromRight => fromRightOut', [
                style({opacity: 1, transform: 'translateX(0)'}),
                animate('300ms ease-in-out')
            ]),

            // Enter from left
            state('fromLeft', style({opacity: 1, transform: 'translateX(0)'})),
            transition('* => fromLeft', [
                style({opacity: 0, transform: 'translateX(-5%)'}),
                animate('400ms ease-in-out')
            ]),
            state('fromLeftOut', style({opacity: 0, transform: 'translateX(5%)'})),
            transition('fromLeft => fromLeftOut', [
                style({opacity: 1, transform: 'translateX(0)'}),
                animate('300ms ease-in-out')
            ]),

            // Rotate
            state('scale', style({opacity: 1, transform: 'scale(1)'})),
            transition('* => scale', [
                style({opacity: 0, transform: 'scale(0)'}),
                animate('400ms ease-in-out')
            ]),
            state('scaleOut', style({opacity: 0, transform: 'scale(0)'})),
            transition('scale => scaleOut', [
                style({opacity: 1, transform: 'scale(1)'}),
                animate('400ms ease-in-out')
            ]),

            // Scale
            state('rotate', style({opacity: 1, transform: 'rotate(0deg)'})),
            transition('* => rotate', [
                style({opacity: 0, transform: 'rotate(5deg)'}),
                animate('400ms ease-in-out')
            ]),
            state('rotateOut', style({opacity: 0, transform: 'rotate(-5deg)'})),
            transition('rotate => rotateOut', [
                style({opacity: 1, transform: 'rotate(0deg)'}),
                animate('400ms ease-in-out')
            ])
        ])
    ],
    template: `
        <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()">
            <div *ngIf="!item.html">
                <div class="sn-title">{{item.title}}</div>
                <div class="sn-content">{{item.content | max:maxLength}}</div>
                <div class="icon" *ngIf="item.icon !== 'bare'" [innerHTML]="safeSvg"></div>
            </div>
            <div *ngIf="item.html" [innerHTML]="item.html"></div>
            <div class="sn-progress-loader" *ngIf="showProgressBar">
                <span [ngStyle]="{'width': progressWidth + '%'}"></span>
            </div>
        </div>
    `,


    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(() => 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 => {
            if (this.hasOwnProperty(a)) {
                (<any>this)[a] = this.item.override[a];
            }
        });
    }

    ngOnDestroy(): void {
        clearTimeout(this.timer);
    }

    private instance = () => {
        this.zone.runOutsideAngular(() => {
            this.zone.run(() => this.diff = (new Date().getTime() - this.start) - (this.count * this.speed));

            if (this.count++ === this.steps) this.zone.run(() => this.remove());
            else if (!this.stopTime) {
                if (this.showProgressBar) this.zone.run(() => 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(() => {
                setTimeout(() => {
                    this.zone.run(() => this.notificationService.set(this.item, false))
                }, 310);
            })
        } else {
            this.notificationService.set(this.item, false);
        }
    }
}

简单nofitication

@Component({
    selector: 'simple-notifications',
    encapsulation: ViewEncapsulation.None,
    template: `
        <div class="simple-notification-wrapper" [ngClass]="position">
            <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"
                >
            </simple-notification>
        </div>
    `,

    `],
    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 => {
                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 >= this.maxStack) this.notifications.splice(0, 1);
                this.notifications.push(item);
            } else {
                if (this.notifications.length >= 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 && this.notifications.length > 0) {
            for (let i = 0; i < this.notifications.length; i++) {
                if (toCheck(this.notifications[i], item)) {
                    return true;
                }
            }
        }

        if (this.preventLastDuplicates) {

            let comp: Notification;

            if (this.preventLastDuplicates === 'visible' && this.notifications.length > 0) {
                if (this.lastOnBottom) {
                    comp = this.notifications[this.notifications.length - 1];
                } else {
                    comp = this.notifications[0];
                }
            } else if (this.preventLastDuplicates === 'all' && 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 && checker.title === item.title && checker.content === item.content;
    }

    checkHtml(checker: Notification, item: Notification): boolean {
        return checker.html ? checker.type === item.type && checker.title === item.title && checker.content === item.content && checker.html === item.html : false;
    }

    // Attach all the changes received in the options object
    attachChanges(options: any): void {
        Object.keys(options).forEach(a => {
            if (this.hasOwnProperty(a)) {
                (<any>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) => {
            if (notification.id === id) {
                indexOfDelete = idx;
                doDelete = true;
            }
        });

        if (doDelete) {
            this.notifications.splice(indexOfDelete, 1);
        }
    }

    ngOnDestroy(): void {
        if (this.listener) {
            this.listener.unsubscribe();
        }
    }

0 个答案:

没有答案