typescript 使用RouterLink动态添加元素

9vw9lbht  于 2023-01-18  发布在  TypeScript
关注(0)|答案(4)|浏览(128)

当我把一个锚元素放在一个Angular 组件的某个地方时,如下所示:

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

一切正常。2当点击链接时,Angular 路由器导航到目标组件。
现在,我想动态添加相同的链接。在我的应用程序中的某个地方我有一个“通知组件”,它的单一职责是显示通知。
通知组件的作用类似于:

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

其中notification.contentNotificationComponent类中的公共字符串变量,包含要显示的HTML。
notification.content变量可以包含如下内容:

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

一切都运行正常,并显示在我的屏幕上,但当我单击动态链接时,什么也没有发生。
有没有办法让Angular路由器使用这个动态添加的链路?
PS:我知道DynamicComponentLoader,但我真的需要一个更不受限制的解决方案,在那里我可以向我的通知组件发送各种HTML,带有各种不同的链接。

slwdgvem

slwdgvem1#

无法在呈现内容后添加routerLink,但您仍然可以获得所需的结果:
1.创建一个包含动态数据的href并给予它一个类:

`<a class="routerlink" href="${someDynamicUrl}">${someDynamicValue}</a>`

1.向app.component添加一个HostListener,用于侦听点击并使用路由器进行导航

@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}`]);
     }
   }
 }

}

ig9co6j1

ig9co6j12#

routerLink是指令。不会为使用[innerHTML]添加的HTML创建指令和组件。Angular不会以任何方式处理此HTML。
推荐的方法是不使用[innerHTML],而使用DynamicComponentLoaderViewContainerRef.createComponent,在DynamicComponentLoaderViewContainerRef.createComponent中,您将HTML Package 在组件中并动态添加它。
有关示例,请参见Angular 2 dynamic tabs with user-click chosen components

eqoofvh9

eqoofvh93#

从angular 9开始,AOT是编译Angular 项目的默认推荐方式。与JIT不同,AOT在运行时不为编译器保存示例,这意味着您不能动态编译Angular 代码。在angular 9中可以禁用AOT,但不推荐,因为您的包大小会更大,应用程序速度会更慢。
我解决这个问题的方法是在运行时使用renderer API添加一个click侦听器,防止url的默认行为并调用angular router

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://stackoverflow.com/a/35082441/6209801
https://indepth.dev/here-is-what-you-need-to-know-about-dynamic-components-in-angular

c9qzyr3d

c9qzyr3d4#

结合一些其他的答案--我想把它作为一个指令,这样我就可以针对正在被innerHTML 'd的特定元素,但要避免使用querySelector(etc)来保持所有内容都是有Angular 的。
我还发现了上述方法的一个问题,如果href是一个完整的URL(即https://www.example.com/abc),那么将整个URL馈送到路由器将导致导航到/https。
我还需要检查,以确保我们只有路由器的href是在我们的域。

@Directive({
  selector: '[hrefToRouterLink]'
})
export class HrefToRouterLinkDirective {
  constructor(private _router: Router){}

  private _baseHref = quotemeta(environment.root_url.replace(`^https?://`, ''));
  private _hrefRe: RegExp = new RegExp(`^(https?:)?(\\/+)?(www\\.)?${this._baseHref}`, `i`);

  @HostListener('click', ['$event'])
  onClick(e) {
    // Is it a link?
    if (!(e.target instanceof HTMLAnchorElement)) 
      return;

    let href: string = e.target?.getAttribute('href')
      .replace(/(^\s+|\s+$)/gs, '');

    // Is this a URL in our site?
    if (!this._hrefRe.test(href))
      return;
      
    // If we're here, it's a link to our site, stop normal navigation
    e.preventDefault();
    e.stopPropagation();

    // Feed the router.
    this._router.navigateByUrl(
      href.replace(this._hrefRe, '')
    );
  }
}

上面的environment.root_url描述了我们的基域,quotemeta是一个Perl风格的quotemeta函数的粗略实现,只是为了转义特殊字符。
YMMV和我肯定错过了一些边缘情况,但这似乎工作得很好。

相关问题