使用 RouterLink 动态添加元素

Add element with RouterLink dynamically

当我将锚元素放在 Angular 组件的某处时,如下所示:

<a [routerLink]="['/LoggedIn/Profile']">Static Link</a>

一切正常。单击 link 时,Angular 路由器导航到目标组件。

现在,我想动态添加相同的 link。在我的应用程序的某个地方,我有一个“通知组件”,它的唯一职责是显示通知。

通知组件做这样的事情:

<div [innerHTML]="notification.content"></div>

其中 notification.contentNotificationComponent class 中的 public 字符串变量,其中包含要显示的 HTML。

notification.content 变量可以包含如下内容:

<div>Click on this <a [routerLink]="['/LoggedIn/Profile']">Dynamic Link</a> please</div>

一切正常并显示在我的屏幕上,但是当我单击动态 link 时没有任何反应 link。

有没有办法让 Angular 路由器与这个动态添加的 link 一起工作?

PS:我知道 DynamicComponentLoader,但我真的需要一个更不受限制的解决方案,我可以将各种 HTML 发送到我的通知组件,各种不同links.

routerLink 是一个指令。不会为使用 [innerHTML] 添加的 HTML 创建指令和组件。这个 HTML 没有被 Angular 以任何方式处理。

推荐的方法是不使用 [innerHTML],而是使用 DynamicComponentLoaderViewContainerRef.createComponent,将 HTML 包装在一个组件中并动态添加。

有关示例,请参阅

routerLink 无法在内容已经呈现后添加,但您仍然可以达到预期的效果:

  1. 用动态数据创建一个href并给它一个class:

    `<a class="routerlink" href="${someDynamicUrl}">${someDynamicValue}</a>`
    
  2. 将 HostListener 添加到 app.component 以侦听点击并使用路由器 导航

    @HostListener('document:click', ['$event'])
    public handleClick(event: Event): void {
     if (event.target instanceof HTMLAnchorElement) {
       const element = event.target as HTMLAnchorElement;
       if (element.className === 'routerlink') {
         event.preventDefault();
         const route = element?.getAttribute('href');
         if (route) {
           this.router.navigate([`/${route}`]);
         }
       }
     }
    

    }

自 angular9 起,AOT 是编译 angular 项目的默认推荐方式。 与 JIT 不同,AOT 在运行时不为编译器保留实例,这意味着您无法动态编译 angular 代码。 可以在 angular 9 中禁用 AOT,但不建议这样做,因为你的包大小会更大并且你的应用程序会更慢。

我解决这个问题的方法是在运行时使用渲染器 api 添加点击侦听器,防止 url 的默认行为并调用 angular 路由器

import { Directive, ElementRef, OnDestroy, OnInit, Renderer2 } from '@angular/core';
import { Router } from '@angular/router';

@Directive({
  selector: '[hrefToRouterLink]'
})
export class HrefToRouterLinkDirective implements OnInit, OnDestroy {
  private _listeners: { destroy: () => void }[] = [];

  constructor(private _router: Router, 
  private _el: ElementRef, 
  private _renderer: Renderer2) {
  }

  ngOnInit() {
    // TODO how to guarantee this directive running after all other directives without setTimeout?
    setTimeout(() => {
      const links = this._el.nativeElement.querySelectorAll('a');
      links.forEach(link => {
        this._renderer.setAttribute(link, 'routerLink', link?.getAttribute('href'));
        const destroyListener = this._renderer.listen(link, 'click',
          (_event) => {
            _event.preventDefault();
            _event.stopPropagation();
            this._router.navigateByUrl(link?.getAttribute('href'));
          });
        this._listeners.push({ destroy: destroyListener });
      });
    }, 0);
  }

  ngOnDestroy(): void {
    this._listeners?.forEach(listener => listener.destroy());
    this._listeners = null;
  }

}

你可以在这里找到一个例子:https://stackblitz.com/edit/angular-dynamic-routerlink-2

显然上面解释的方法对 JIT 和 AOT 都有效,但是如果你还在使用 JIT 并且想动态编译组件(这可能有助于解决其他问题)。你可以在这里找到一个例子: https://stackblitz.com/edit/angular-dynamic-routerlink-1

已用资源:

https://indepth.dev/here-is-what-you-need-to-know-about-dynamic-components-in-angular