1

I am very new to Angular 2 and want to pass a bool from one component to another with <router-outlet>

I understood for this I have to use a service.

Basically what I want is to set a bool in app.component.ts from another component front.component.ts, to expand and collapse a header in app.component.html.

This is what I have:

app.component.ts:

import { Component, OnInit } from "@angular/core";
import { HeaderService } from "./header.service";

@Component({
selector: "fandango-app",
templateUrl: "views/app.component.html",
providers: [HeaderService]
})

export class AppComponent implements OnInit {

headerCollapsed: Boolean = false;
headerService: HeaderService;

constructor(headerService: HeaderService) {
   this.headerService = headerService;
}

ngOnInit() {
    var self = this;
    this.headerService.headerToggle.subscribe((headerCollapsed: Boolean) => () => {
        self.headerCollapsed = headerCollapsed;
    });
  }
}

front.component.ts:

import { Component, AfterViewInit } from "@angular/core";
import { HeaderService } from "./header.service";

@Component({
templateUrl: "views/front.component.html",
styleUrls: ["content/front.component.css"]
})

export class FrontComponent implements AfterViewInit {

headerService: HeaderService;

constructor(headerService: HeaderService) {
    this.headerService = headerService;
}

ngAfterViewInit() {
    this.headerService.setHeader(false);
  }
}

header.service.ts:

import { Output, EventEmitter, Injectable } from "@angular/core";

@Injectable()
export class HeaderService {

@Output() headerToggle = new EventEmitter<Boolean>();

constructor() {

}

setHeader(headerCollapsed: Boolean) {
    this.headerToggle.emit(headerCollapsed);
}
}

app.routing.ts:

import { ModuleWithProviders } from "@angular/core";
import { Routes, RouterModule } from "@angular/router";

import { FrontComponent } from "./front.component";
import { PricingComponent } from "./pricing.component";

const appRoutes: Routes = [
{ path: "", redirectTo: "front", pathMatch: "full" },
{ path: "front", component: FrontComponent },
{ path: "pricing", component: PricingComponent }
];

export const appRoutingProviders: any[] = [];

export const routes: ModuleWithProviders = RouterModule.forRoot(appRoutes);

app.module.ts:

import { NgModule } from "@angular/core";
//import { NgbModule } from "@ng-bootstrap/ng-bootstrap";
import { BrowserModule } from "@angular/platform-browser";
import { routes, appRoutingProviders } from "./app.routing";

import { AppComponent } from "./app.component";
import { FrontComponent } from "./front.component";
import { PricingComponent } from "./pricing.component";

import { AffixDirective } from "./affix.directive";

@NgModule({
imports: [
    //NgbModule,
    BrowserModule,
    routes
],
declarations: [
    AppComponent,
    FrontComponent,
    PricingComponent,
    AffixDirective
],
providers: [
    appRoutingProviders
],
bootstrap: [AppComponent]
})

export class AppModule { }

app.component.html:

<header [class.expanded]="!headerCollapsed" [class.collapsed]="headerCollapsed">
<div class="container">
    <a href="/">
        my app
    </a>
</div>
</header>
<span>{{ headerCollapsed }}</span>
<router-outlet></router-outlet>

index.html:

<my-app id="app">        
    <div id="loader" class="container text-center">
        <p>loading</p>
        <div class="loader">
            <span></span>
            <span></span>
            <span></span>
        </div>
    </div>          
</my-app>

The problem is that it never steps into:

self.headerService.headerToggle.subscribe((headerCollapsed: Boolean) => () => {
    self.headerCollapsed = headerCollapsed;
});

What am I doing wrong?

Mind again, I am very new to Angular2 and I don't know if this code is the best way to do it.

  • Any errors in the console? – Maximilian Riegler Sep 14 '16 at 11:14
  • @rinukkusu No errors in the console –  Sep 14 '16 at 11:16
  • Have you tried to subscribe to your event in ngOnInit instead of subscribing in the constructor? It's a good practice to do work there and only initialize variables in the constructor. See http://stackoverflow.com/questions/35763730/what-is-the-difference-between-constructor-and-ngoninit – StephaneM Sep 14 '16 at 11:47
  • @StephaneM cool, I learned something again, and yes, you are right, there should not be any code in the constructor unless some dependency injection code, thanks for the tip. However, I tried it and it still doesn't work. I'm going to update my code –  Sep 14 '16 at 12:19
  • Do you have `providers: [HeaderService]` anywhere else than on `AppComponent`? – Günter Zöchbauer Sep 14 '16 at 12:21
  • @GünterZöchbauer, no I haven't... –  Sep 14 '16 at 12:33

1 Answers1

1

You shouldn't use @Output() or EventEmitter in a service. These are only for outputs on components and directives. @Output() in a service doesn't have any effect at all.

Use instead

headerToggle = new Subject<Boolean>();

In your case

headerToggle = new BehaviorSubject<Boolean>();

with

setHeader(headerCollapsed: Boolean) {
    this.headerToggle.next(headerCollapsed);
}

should fix your problem.

Also change

this.headerService.headerToggle.subscribe((headerCollapsed: Boolean) => () => {

to

this.headerService.headerToggle.subscribe((headerCollapsed: Boolean) => {

(remove ())

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Thanks for the tip, I changed the code but it doesn't work (I also changed headerToggle to onToggle just so it looks cleaner, yes I changed it in app.component.ts also). This is now my code in header.service.ts: import { Injectable } from "@angular/core"; import { BehaviorSubject } from "rxjs/Rx"; @Injectable() export class HeaderService { onToggle = new BehaviorSubject(false); constructor() { } setHeader(headerCollapsed: Boolean) { this.onToggle.next(headerCollapsed); } } –  Sep 14 '16 at 12:58
  • Code in comments is unreadable. Please edit your question and add the code there. – Günter Zöchbauer Sep 14 '16 at 12:59
  • I found another bug. I updated my answer. Remove `()`. This way what your code does when an event is emitted by the observable is returning a function that has `self.headerCollapsed = headerCollapsed;` as body but this function is never called (with your current syntax). – Günter Zöchbauer Sep 14 '16 at 13:02
  • 1
    YEESSS it works, the extra "()" was added by resharper, goddammit! :-) Thanks for the solution, I marked it as answered –  Sep 14 '16 at 13:04