-->

Best way to pass angular routerLink URL's in a

2019-08-20 08:52发布

问题:

I have a notification service in my angular app, typically you call it like

this.notificationsService.showError('My Title', 'My Message...');

I'd like to have the ability to to pass app links in the messages as well, and I know I need to allow SafeHtml input, like this:

this.notificationsService.showError(
  'Error!',
  this.domSanitizer.bypassSecurityTrustHtml(
    `Something has gone wrong, but you can go to <a routerLink="'/home/accounts-list'">home page</a> to start over.`
  )
);

Over in my service, this is what I do with it:

showError(title: string, message: string | SafeHtml): void {
    const newNotification: INotification = {
        title,
        message,
        htmlMessage: typeof message !== 'string'
    };

    this.notifications.push(newNotification);
}

And then I show it like this:

<div class="toast" *ngFor="let n of notificationsService.notificationsList">
    <div class="toast-header">
        <strong>{{n.title}}</strong>
    </div>
    <div class="toast-body" *ngIf="!n.htmlMessage">{{n.message}}</div>
    <div class="toast-body" *ngIf="n.htmlMessage" [innerHTML]="n.message"></div>
</div>

So... to get to the point of this question! This only somewhat works in that the HTML gets though, but apparently it's not being parsed by angular to make the routerLink functional. The actual HTML output to the browser is:

<a routerlink="'/home/accounts-list'">home page</a>

however, it's not clickable since a link that's actually parsed by angular would output this HTML:

<a routerlink="'/home/accounts-list'" ng-reflect-router-link="/home/accounts-list" href="/home/accounts-list">home page</a>

How can I get these links to work?

Am I even going about this in the correct way?

回答1:

This is what I ended up doing based on @cgTag's comment & suggestion

on my page that shows the error I have this:

<ng-template #errorMessageTemplate>
    Something has gone wrong, but you can go to the <a [routerLink]="['/home/accounts-list']">home page</a>
</ng-template>
@ViewChild('errorMessageTemplate') errorMessageTemplate!: TemplateRef<NgTemplateOutlet>;


someMethod(): void {
  this.notificationsService.showError('Error!', this.errorMessageTemplate);
}

And in my service I have this:

showError(title: string, message: string | TemplateRef<NgTemplateOutlet>): void {
    const newNotification: INotification = {
        title,
        message,
        messageAsTemplate: typeof message !== 'string'
    };

    this.notifications.push(newNotification);
}

And then I show it like this:

<div class="toast" *ngFor="let n of notificationsService.notificationsList">
    <div class="toast-header">
        <strong>{{n.title}}</strong>
    </div>
    <div class="toast-body" *ngIf="!n.messageAsTemplate">{{n.message}}</div>
    <div class="toast-body" *ngIf="n.messageAsTemplate">
        <ng-container [ngTemplateOutlet]="n.message"></ng-container>
    </div>
</div>


回答2:

Since the HTML is not compiled by the angular compiler, there is no good solution. :)

There are some hacks to achieve what you want. But they are dirty and possibly insecure.

One solution is to create a directive which sits on the host element. It watches clicks on the host and prevents the default action if the clicked element has the routerlink attribute. it then just calls the router.navigate function with the value of routerlink attribute.

I prepared a quick example on stackblitz: https://stackblitz.com/edit/angular-ttfwwg