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
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
Le problème est que contrairement à d’autres frameworks, Angular ne semble pas forcer Service
s à ê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]