A simple message service should show messages which are created by differnet classes. When the router executes a routing, the the currently shown messages should be cleared.
A message has a text and a type. Both are readonly fields of the Message class:
export class Message {
constructor(readonly type: MessageType, readonly text: String) {
}
}
export enum MessageType {
INFO,
ERROR,
CLEAR
}
Note the “special” type CLEAR which is used to indicate that the currently shown messages should be removed
(the message queue should be cleared).
The template for the MessagePanelComponent is quite simple:
<div *ngFor="let message of messages">
<!-- the default is a bit lengthy do formulate in the case if there are more message types -->
<!-- use a class binding for the different message types to show -->
<div class="alert" [class.alert-danger]="message.type == messageType.ERROR"
[class.alert-info]="message.type != messageType.ERROR">
</div>
</div>
Not the usage of the messageType property of MessagePanelComponent in order to access an element from the global namespace.
(Accessing it in a “static” manner like MessageType.ERROR does not work
because the MessagePanelComponent has no property MessageType. The context against which the expression is resolved is the MessagePanelComponent)
The MessagePanelComponent manages the list of messages to show:
export class MessagePanelComponent implements OnDestroy {
// Template can only access elements from the component. Access to other elements is not possible.
// Therefore we have to add a property in order to access the enum:
public readonly messageType = MessageType;
public messages: Message[] = [];
private subscription: Subscription;
constructor(messageService: MessageService) {
this.subscription = messageService.subscribe(m => this.addMessage(m));
}
private addMessage(message: Message): void {
// The "CLEAR" type removes the currently shown messages:
if (message.type === MessageType.CLEAR) {
this.messages = [];
} else {
this.messages.push(message);
}
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
}
}
The MessageService simply publishes the messages by using a Subject:
@Injectable()
export class MessageService implements OnDestroy {
private readonly messageSubject: Subject<Message> = new Subject<Message>();
constructor() {
}
public publish(message: Message): void {
this.messageSubject.next(message);
}
public subscribe(next?: (value: Message) => void, error?: (error: any) => void, complete?: () => void): Subscription {
// Currently just delegates to the subject.
return this.messageSubject.subscribe(next, error, complete);
}
ngOnDestroy(): void {
this.messageSubject.complete();
}
}
The MessageService has to be regestered a provider.
In order to remove the messages when a routing is initiated, we register a listener to the Router.events in the AppRoutingModule:
const routes: Routes = [...];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
...
})
export class AppRoutingModule {
constructor(messageService: MessageService, router: Router) {
router.events.subscribe(event => {
// clear the message panel on "page change"
if (event instanceof NavigationStart) {
messageService.publish(Message.clear());
}
});
}
}
The MessageService can be injected by Angular in a constuctor and then used in the class
constructor(private messageService: MessageService) {
}
doSomething(...): ... {
...
this.messageService.publish(new Message(MessageType.ERROR, "My error text");
...
}