web-dev-qa-db-fra.com

Abonnement angulaire 2 observable ne déclenchant pas

J'ai un problème avec un abonnement à un observable non déclencheur.

J'ai une mise en page utilisant la directive de mise en page de navigation latérale ressemblant à ceci:

<md-sidenav-layout class="nav-bar">


    <md-sidenav #start>
        <nav>
            <a [routerLink]="['/home']">Home</a>
        </nav> 
        <button md-button (click)="start.close()">Close</button>
    </md-sidenav>

    <router-outlet></router-outlet>

</md-sidenav-layout>

Ce que je dois faire, c'est ouvrir la liste de navigation latérale du composant affiché par le routeur-prise.

Un de ces composants pourrait ressembler à ceci:

@Component({

    providers: [SidenavService, MdIconRegistry],
    directives: [MdIcon],
    templateUrl: `<md-icon (click)="toggleSidenav()">menu</md-icon>`,
    styleUrls: ["./home.component.scss"]
})

export class Home {

    myColor: string;

    constructor(private sidenavService: SidenavService) {
        this.myColor = "primary";

        this.sidenavService.getSidenavObs().subscribe(state => {console.log("state change")});
    }


    toggleSidenav() {

        this.sidenavService.toggleSidenav("open");
    }

}

J'ai créé un service qui renvoie un observable comme ceci:

@Injectable()
export class SidenavService {

    private toggle = new Subject<string>();
    private toggleObs = this.toggle.asObservable();

    getSidenavObs() {
        return this.toggleObs;
    }

    toggleSidenav(state: string) {
        this.toggle.next(state);
    }

}

Enfin, le code de la classe parente (qui appartient à la mise en page de navigation latérale HTML):

@Component({
  providers: [MdSidenav, SidenavService],
  directives: [ROUTER_DIRECTIVES, MD_SIDENAV_DIRECTIVES, MD_BUTTON_DIRECTIVES],
  selector: "app",
  templateUrl: "app.html",
  styleUrls: ["./app.scss"],
})

export class App {

  subscription: Subscription;

  constructor(private sidenavService: SidenavService, private sidenav: MdSidenav) {
    this.subscription = sidenavService.getSidenavObs().subscribe( status => {
      console.log("state change");
      sidenav.open();
    }, error => {
      console.log(error);
    }, () => {
      console.log("completed");
    });
  }
}

Maintenant, mon problème est que l'abonnement dans le composant App ne se déclenche pas. Cependant, l'abonnement dans le composant Accueil (qui est ce que le routeur-sortie affiche) est déclenché comme prévu.

Est-ce que j'ai râté quelque chose?

J'ai suivi ce guide: https://angular.io/docs/ts/latest/cookbook/component-communication.html#!#bidirectional-service

19
mda144

La même instance de service n'est pas partagée entre vos composants App et Home, car vous avez répertorié SidenavService en tant que fournisseur pour les deux.

Lorsque vous définissez fournisseur de service dans l'entrée providers d'un décorateur de composants, ce service est ensuite disponible pour ce composant et tous ses enfants en tant que singleton, ce qui signifie que la même instance du service sera utilisée.

Si vous redéfinissez un fournisseur dans un composant enfant, il créera une instance new du service et ne partagera plus la même instance de service que son parent/ancêtre.

Si vous utilisez SidenavService dans les composants App et Home et que vous avez besoin de la même instance, vous ne devez le fournir que dans le composant App et le supprimer de l'entrée providers de Home.

Pour plus d'informations sur DI, consultez les liens suivants: https://angular.io/docs/ts/latest/guide/dependency-injection.htmlhttps://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html

68
Michael

Le problème est que contrairement à d’autres frameworks, Angular ne semble pas forcer Services à être singletons _ par défaut.

Ainsi, vous rencontrez une situation dans laquelle vous déclarez chaque Component de la vôtre comme ayant sa propre instance de la Service. Cela donne à chaque instance des attributs différents (toggle et toggleObs). Schématiquement:

App -> SidenavService [instance A]

Accueil -> SidenavService [instance B]

Vous devez modifier vos déclarations pour qu'elles correspondent à Singleton Services, comme décrit dans Angular Documentation . Cela se traduit par le partage d'une seule instance d'une Service parmi toutes les Components. Schématiquement:

App -> SidenavService [instance A]

Accueil -> SidenavService [instance A]

0
Yuri