akserg / ng2-toasty

Angular2 Toasty component shows growl-style alerts and messages for your app.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Two instances of ToastyService when lazy loading routes and shared module

tomasisby opened this issue · comments

  • I'm submitting a ...
    [x] bug report
    [ ] feature request
    [ ] question about the decisions made in the repository
  • Do you want to request a feature or report a bug?
    bug
  • What is the current behavior?
    When I create lazy loaded routes with a shared module, two instances of ToastyService is created and no toasts is shown. Only ToastService in lazy loaded module gets updated.
  • If the current behavior is a bug, please provide the steps to reproduce and if possible a minimal demo of the problem via https://plnkr.co or similar.
    Tried to do a Plunker but didn't find out how to include Toasty in this. Here is the code that differs from Angular Quickstart:

app.module

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { SharedModule } from './shared.module';
import { routing } from './app.routing';
@NgModule({
  imports: [
    BrowserModule,
    SharedModule,
    routing
  ],
  declarations: [
    AppComponent
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component

import { Component } from '@angular/core';
@Component({
  selector: 'my-app',
  template: `<h1>My First Angular App</h1>
  <router-outlet></router-outlet>
  <ng2-toasty position="top-right"></ng2-toasty>`
})
export class AppComponent { 
}

app.routing

import { ModuleWithProviders } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { StartComponent } from './start.component';
const appRoutes: Routes = [
    { path: '', redirectTo: 'start', pathMatch: 'full' },
    { path: 'start', loadChildren: 'app/start.module#StartModule' }
];
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);

shared.module

import { NgModule, ModuleWithProviders } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { ToastyModule, ToastyService } from 'ng2-toasty';
@NgModule({
    imports: [
        CommonModule,
        ToastyModule.forRoot()
    ],
    declarations: [],
    exports: [
        CommonModule,
        FormsModule,
        ToastyModule
    ]
})
export class SharedModule {
}

start.module

import { NgModule } from '@angular/core';
import { StartComponent } from './start.component';
import { SharedModule } from './shared.module';
import { routing } from './start.routing';
@NgModule({
  imports: [
    SharedModule,
    routing
  ],
  declarations: [
      StartComponent
  ],
  exports: []
})
export class StartModule { }

start.component

import { Component } from '@angular/core';
import {ToastyService, ToastyConfig, ToastOptions, ToastData} from 'ng2-toasty';

@Component({
  moduleId: module.id,
  selector: 'my-start',
  template: `<button (click)="addToast()">Add Toast</button>`
})
export class StartComponent {
      constructor(private toastyService:ToastyService, private toastyConfig: ToastyConfig) { 
        // Assign the selected theme name to the `theme` property of the instance of ToastyConfig. 
        // Possible values: default, bootstrap, material
        this.toastyConfig.theme = 'material';
    }

        addToast() {
        // Just add default Toast with title only
        this.toastyService.default('Hi there');
        // Or create the instance of ToastOptions
        var toastOptions:ToastOptions = {
            title: "My title",
            msg: "The message",
            showClose: true,
            timeout: 5000,
            theme: 'default',
            onAdd: (toast:ToastData) => {
                console.log('Toast ' + toast.id + ' has been added!');
            },
            onRemove: function(toast:ToastData) {
                console.log('Toast ' + toast.id + ' has been removed!');
            }
        };
        // Add see all possible types in one shot
        this.toastyService.info(toastOptions);
        this.toastyService.success(toastOptions);
        this.toastyService.wait(toastOptions);
        this.toastyService.error(toastOptions);
        this.toastyService.warning(toastOptions);
    }
}

start.routing

import { ModuleWithProviders } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { StartComponent } from './start.component';
const appRoutes: Routes = [
    { path: '', component: StartComponent }
];
export const routing: ModuleWithProviders = RouterModule.forChild(appRoutes);
  • What is the expected behavior?
    When using lazy loaded routes and shared module, ToasyService should still be a singleton.
  • Please tell us about your environment:
  • Angular version: 2.0.0
  • Browser: [all ]

Hi @tomasisby
I didn't test it in lazy loading environment. Thanks for prompt.

Hello!
I've meddled around with Angular core.module and found a way to make this work. You need to export the ToastyModule in shared.module and ToastyService in core.module, like this:

In shared.module:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms';
import { ToastyModule } from 'ng2-toasty';

@NgModule({
imports: [
CommonModule,
ToastyModule
],
declarations: [],
exports: [
CommonModule,
FormsModule,
ToastyModule
]
})
export class SharedModule {}

And in core.module you take care of the services:

import { NgModule, Optional, SkipSelf } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ToastyService, ToastyConfig } from 'ng2-toasty';

@NgModule({
imports: [
CommonModule
],
declarations: [],
exports: [],
providers: [
ToastyService,
ToastyConfig,
]
})
export class CoreModule {
constructor( @optional() @SkipSelf() parentModule: CoreModule) {
if (parentModule) {
throw new Error('CoreModule is already loaded. Import it in the AppModule only');
}
}
}

And then in app.module you import both shared.module and core.module so you can get a singleton service for the root application:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent } from './app.component';
import { SharedModule } from './shared.module';
import { CoreModule } from './core/core.module';
import { routing } from './app.routing';
@NgModule({
imports: [
BrowserModule,
SharedModule,
CoreModule,
routing
],
declarations: [
AppComponent
],
bootstrap: [AppComponent]
})
export class AppModule { }

And voila! It works as expected with only one instance of toasty service for lazy loaded routes as well.

Impressive.
Thanks for time and sharing code.

Sorry to write on a closed issue, but is there any way to make this work without the core module pattern? Right now it's spawning a new service for each module.