【问题标题】:Can't render routerLink in innerHTML in Angular无法在Angular的innerHTML中呈现routerLink
【发布时间】:2020-04-29 14:02:00
【问题描述】:

在组件代码中,我有一个名为 taggy 的字段,其中包含一个完整的锚标记。起初,在我的 HTML 中,我将它渲染到屏幕上,这当然没有成功,因为我只在逐字版本中看到了 taggy 的内容。文字字面意思是hazaa

谷歌搜索一下,我发现了我的错误。我应该使用innerHTML。它可以工作,但会导致浏览器重新加载,因为我们使用的是 href 而不是 routerLink。当然,我更改了 taggy 的值,所以它显示的是 routerLink 而不是 href。但是,当我这样做时,它会起作用但并不完全。在我的 HTML 中,我有以下内容。

{{taggy}}
<div [innerHTML]=taggy></div>
<div innerHTML=taggy></div>

这些都不会产生有效的链接。最接近的是中间的,因为它创建了一个 div 并在其中创建了一个锚点。但是,锚标记上没有属性,它只包含用户应该阅读的文本。路由信息没了。

怎么办?

我找到了a suggestion,但感觉不是一个好方法(即使作者表示这不是最好的方法)。还有this,但与我的情况无关,因为我将字符串传递给另一个组件。

【问题讨论】:

  • 这似乎违背了 Angular 的做法。您不应该以这种方式直接操作 DOM。您是否有理由不只是在模板中添加标签并绑定到其href
  • @WillAlexander 是的,有。我有一个带有 @Input() 页脚:字符串 的组件。在那个页脚中,我希望能够显示一些信息。有时,该信息将包含一个链接。由于传递的数据是一个字符串,我无法再指定任何内容,因此我需要将内容发送到 innerHTML,正如我在问题中提到的那样,它确实有效。问题是当我使用 href 时,页面将在单击页脚时重新加载,而我想成为路由器以避免重新加载。因此,我改为通过 routerLink 。这并没有被渲染。对替代方法有建议吗?
  • @WillAlexander 顺便说一句,我完全同意你的说法。我很高兴接受有关如何以 Angular 方式处理我的事情的教育。
  • 您需要从作为@Input 传递的字符串中提取所需的数据。怎么做取决于你自己,但你绝对不能 100% 传递纯 HTML,尤其是如果它来自用户。
  • @WillAlexander 我不确定您的评论是否有用。我的问题的重点是如何实现你所说的。如果您不知道如何在 Angular 中做到这一点,我完全理解。让我们看看其他人是否提供了一些有用的东西并从中学习。编码愉快。

标签: html angular routing anchor innerhtml


【解决方案1】:

如果您不介意为此安装库,请查看ngx-dynamic-hooks。它可以用实际的 Angular 组件(包括链接元素)替换动态内容字符串中的任何文本。

有趣的是,我已经根据使用[RouterLinks] 自动替换内部链接的确切想法编写了一个示例。看看here(或in this Stackblitz)。

【讨论】:

    【解决方案2】:

    根据问题,#Dean 解决方案工作正常,但不适用于外部链接。也就是说,它只能用于角度路径路径。以下是我解决内部和外部 url/链接的解决方案。

      import { Component, Input, HostListener } from '@angular/core';
      import { Router } from '@angular/router';
    
      import { Component, Input, HostListener } from '@angular/core';
      import { Router } from '@angular/router';
    
      @Component({
        selector: 'app-footer',
        template: '<div [innerHTML]="footerHtml"></div>'
      })
      export class FooterComponent {
        footerHtml = '<p>This is the innerHTML content with angular valid path  <a href="/admin">internal link!</a> and external link <a target="_blank" href="https://google.com">external link</a></p>';
    
        // Watch for clicks in our component
        @HostListener('click', ['$event'])
        onClick(event: MouseEvent) {
        this.routingService.urlLocator(event);
        // Do nothing if no anchor tag.
        if (event.target instanceof HTMLAnchorElement === false) {
          return;
        }
    
        // Prevent normal html anchor behaviour
        event.preventDefault();
    
        // Checks if the anchor has a full url name or if target is "_blank"
        // That is, target external url
        if (event.target.target === '_blank') {
          // open it in new window (You can decide to use window.location.href)
          window.open(event.target.href); 
        } else {
          // handle valid angular route path
          const target = <HTMLAnchorElement>event.target;
          this.router.navigate([target.pathname]);
        }
      }
    }
    

    【讨论】:

      【解决方案3】:

      正如您所发现的,您不能在 Angular 中使用动态模板,因为模板与组件作为 javascript 捆绑在一起。如果您想了解更多信息,请查看this issue on github。其结果是:动态模板会破坏 AOT 编译。

      您还发现可以将对象的innerHTML 设置为任意HTML,但routerLink 在该上下文中不起作用。 为什么? 因为routerlink 是 Angular 指令,而不是 HTML 属性。因为我们只是设置innerHTML,所以它不是编译模板。

      那么,解决方案是什么?让我们回过头来想想routerLink 首先在做什么。事实证明,并不多。看看the source。具体来说,当单击带有routerLink 指令的元素时,它会执行以下操作:

      @HostListener('click')
      onClick(): boolean {
        const extras = {
          skipLocationChange: attrBoolValue(this.skipLocationChange),
          replaceUrl: attrBoolValue(this.replaceUrl),
        };
        this.router.navigateByUrl(this.urlTree, extras);
        return true;
      }
      

      它只是使用HostListener 来监控点击,并相应地进行路由。我们可以在我们的组件中做类似的事情。我称它为FooterComponent,因为你提到这是一个带有动态 HTML 的页脚:

      import { Component, Input, HostListener } from '@angular/core';
      import { Router } from '@angular/router';
      
      @Component({
        selector: 'app-footer',
        template: '<div [innerHTML]="footerHtml"></div>'
      })
      export class FooterComponent {
        @Input() footerHtml: string = "";
      
        constructor(private router: Router) { }
        // Watch for clicks in our component
        @HostListener("click", ['$event'])
        onClick(event: MouseEvent) {
          // If we don't have an anchor tag, we don't need to do anything.
          if (event.target instanceof HTMLAnchorElement === false) { 
            return;
          }
          // Prevent page from reloading
          event.preventDefault();
          let target = <HTMLAnchorElement>event.target;
          // Navigate to the path in the link
          this.router.navigate([target.pathname]);
        }
      }
      

      如您所见,它接收名为footerHtml 的输入,它是字符串类型。要使用它,我们将在某处添加它:

      <app-footer [footerHtml]="footerHtml"></app-footer>
      

      我们组件中的footerHtml属性定义如下:

      footerHtml = '<p>This is the footer with a <a href="/admin">link!</a></p>';
      

      当点击元素时,我们会检查以确保用户点击了超链接。如果他们这样做了,我们将使用路由器进行导航并阻止默认行为。这是working StackBlitz example。希望这会有所帮助!

      【讨论】:

        猜你喜欢
        • 2018-01-26
        • 2020-10-20
        • 2019-01-24
        • 2022-12-10
        • 2017-05-13
        • 1970-01-01
        • 2020-11-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多