I want alerts to appear within/above my static bootstrap v4 navbar.
So I've got this simple service:
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable()
export class AppService {
private _navbarPadding: number = 50;
navbarPaddingChange: Subject<number> = new Subject();
constructor() {
this.navbarPadding = this._navbarPadding;
}
get navbarPadding(): number {
return this._navbarPadding;
}
set navbarPadding(val: number) {
this._navbarPadding = val;
this.navbarPaddingChange.next(this._navbarPadding);
}
}
Which I inject everywhere, including sidebar (below) and 'main body':
import { Component, OnInit, OnDestroy } from '@angular/core';
import { AppService } from '../app.service';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-sidebar',
templateUrl: './sidebar.component.html',
styleUrls: ['./sidebar.component.css']
})
export class SidebarComponent implements OnInit, OnDestroy {
navbarPadding: number;
subNavbarPadding: Subscription;
constructor(public appService: AppService) {}
ngOnInit() {
this.navbarPadding = this.appService.navbarPadding;
this.subNavbarPadding = this.appService.navbarPaddingChange.subscribe(val =>
this.navbarPadding = val
);
}
ngOnDestroy() {
this.subNavbarPadding.unsubscribe();
}
}
Then I've got this function:
addAlert() {
this.appService.navbarPadding += 81;
this.alertsService.alerts.push({
type: 'info',
msg: 'INFO'
})
}
Sidebar html (first line):
<div class="col-sm-3 col-md-2 sidebar" [style.margin-top.px]=navbarPadding>
…and it works just fine. But… this must be a terrible idea. It has heavy coupling everywhere. What's the correct Angular2 approach?
In general, this is a valid approach, however, I would recommend keeping the controller as simple as possible and making use of the async
-pipe when working with Observables
:
Your Service (uses BehaviorSubject
instead a a Subject
, though imho the getter and setter could probably go as well, and in the addAlert
you then could just use this.appService.navbarPadding$.next(131)
):
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable()
export class AppService {
private _navbarPadding: number = 50;
navbarPadding$: BehaviorSubject<number> = new BehaviorSubject(this._navbarPadding);
constructor() {
}
get navbarPadding(): number {
return this._navbarPadding;
}
set navbarPadding(val: number) {
this._navbarPadding = val;
this.navbarPadding$.next(this._navbarPadding);
}
}
The component is kept to a bare minimum (no manual subscriptions):
import { Component, OnInit, OnDestroy } from '@angular/core';
import { AppService } from '../app.service';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-sidebar',
templateUrl: './sidebar.component.html',
styleUrls: ['./sidebar.component.css']
})
export class SidebarComponent implements OnInit, OnDestroy {
constructor(public appService: AppService) {}
}
Your template (the async-pipe automatically handles the subscription):
<div class="col-sm-3 col-md-2 sidebar" [style.margin-top.px]="appService.navbarPadding$ | async">
Since your approach is similar to ngrx you might want to check out the ngrx-store, which provides you with a very nice way of handling exactly those kind of application-states, like your padding.