【问题标题】:Angular/Typescript Text with routerLink带有 routerLink 的 Angular/Typescript 文本
【发布时间】:2023-04-06 08:20:01
【问题描述】:

更新的问题更清晰

需要在 Angular HTML 中将一些文本和链接显示为 innerHTML(来自服务/DB 的数据),当用户点击时,它应该转到 Typescript 并以编程方式导航router.navigate

另外,如何从 @ViewChild/ElementRef 添加 DomSanitizer

在下面的代码中添加了所有示例

Here is the updated stackblitz code

如 angular.io 的截图所示,一些文本和一些链接

【问题讨论】:

  • 没有定义任何路由器模块/路由的任何特殊原因?
  • 你也不能做这样的 html 内容,你必须对其进行消毒或标记为安全。 medium.com/@swarnakishore/…
  • @alejendro Camba :模块/路由器定义是次要的。如果我们可以导航到 goto(),我可以实现目标。
  • @E.麦格尼。请查看问题中的屏幕截图。我想实现一些文本和一些链接。

标签: html angular typescript innerhtml angular-router


【解决方案1】:

对不起,我没有意识到你回答了我的评论。 Angular 路由不是次要的,如果你不使用 Angular 模块,你最终只会得到一个 HTML/CSS/Typescript 应用程序。你至少需要 Angular 的 RouterModule 才能使用路由,因此,对 DOM 做它应该做的事情。

第一:

  • 你没有导入 RouterModule

    解决方案:

    imports: [ 
      BrowserModule, 
      FormsModule, 
      RouterModule.forRoot([])  // this one
    ]
    

第二:

  • 你不能通过 innerHTML 属性绑定 Angular 事件

    修复:

利用@ViewChild 指令更改您的innerHTML 属性并手动绑定到click 事件,因此将您的app.component.html 更改为

<div id="box" [innerHTML]="shouldbedivcontent" ></div>

<div #box id="box"></div>

现在,在您的app.component.ts 中,添加一个属性来保存对该“box”元素的引用,以便您以后可以使用它对 dom 进行一些更改:

  @ViewChild('box')  container: ElementRef;

实现 AfterViewInit,该钩子是您能够实际处理容器的地方,如果您尝试在 OnInit 中使用它,您将得到未定义,因为该组件的 html 尚未在 dom 中。

export class AppComponent  implements AfterViewInit {

ngAfterViewInit() {
    this.container.nativeElement.innerHTML = this.shouldbedivcontent;
    this.container.nativeElement.addEventListener('click', 
      () => this.goto('bar')
    );
}

shouldbedivcontent 属性更改为:

'1) this is a click 
 <a (click)="goto("bar")">Click</a><br> 
 2)this is with routerlink 
 <a routerLink="" (click)="goto("bar")">Click</a><br> 
 3)This only works with href 
 <a href="goto/bar" title="bar">bar</a>&nbsp; and test'

'1) this is a click 
 <a id="link_1">Click</a><br> 
 2)this is with routerlink 
 <a [routerLink]="" (click)="goto(\'bar\')">Click</a><br> 
 3)This only works with href 
 <a href="goto/bar" title="bar">bar</a>&nbsp; and test'

即便如此,除非您自己应用一些样式,否则您仍然无法获得默认锚样式。

第三

您没有对HTML 进行清理,这可能很危险。 read more here

我的建议:

对于与您一起工作的其他人来说,您似乎有很多工作要做,而您可以像下面的示例中那样轻松做到这一点!

将您的 html 移动到您的 app.component.html:

<div id="box">
  1) this is a click 
  <a (click)="goto('bar')">Click</a><br> 
  2)this is with routerlink 
  <a routerLink="" (click)="goto('bar')">Click</a><br> 
  3)This only works with href 
  <a href="goto/bar" title="bar">bar</a>&nbsp; and test
</div>

<p>Below is actual content</p>

您会注意到现在一切正常,除了没有 routerLink 或 href 的锚点,因为那不是链接。

编辑:

看看新的 stackblitz,我建议改变方法,在处理纯文本甚至一些简单的 html 时绑定到 innerHTML 是可以的,但不是绑定事件或路由逻辑的好选择。

Angular 的 Renderer2 提供了一系列方法来动态地将元素添加到 DOM。有了这个,你只需要一点点努力就可以把你从后端得到的那个简单的 html 转换成类似的东西(将此属性粘贴到你的代码中,以便在下面提供的其余代码中对其进行测试):

public jsonHTML = [
  {
    tagName: '',
    text: 'some text with click ',
    attributes: {
    }
  },
  {
    tagName: 'a',
    text: 'bar',
    attributes: {
      value: 'bar' // goto parameter
    }
  },
  {
    tagName: '',
    text: ' some more text with click ',
    attributes: {
    }
  },
  {
    tagName: 'a',
    text: 'foo',
    attributes: {
      value: 'foo' // goto parameter
    }
  }
]

一旦你有了它,动态创建所有这些元素就更容易了:

这是针对您 Q1 中的代码的:

使用private r2: Renderer2 注入 Renderer2

并将AfterViewInit钩子中Q1相关代码替换为:

const parent = this.r2.createElement('div');  // container div to our stuff

this.jsonHTML.forEach((element) => {
  const attributes = Object.keys(element.attributes);
  const el = element.tagName && this.r2.createElement(element.tagName);
  const text = this.r2.createText(element.text);

  if (!el) {  // when there's no tag to create we just create text directly into the div.
    this.r2.appendChild(
      parent,
      text
    );
  } else { // otherwise we create it inside <a></a>
    this.r2.appendChild(
      el,
      text
    );

    this.r2.appendChild(
      parent,
      el
    );
  }
  
  if (attributes.length > 0) {
    attributes.forEach((name) => {
      if (el) {
        this.r2.setAttribute(el, name, element.attributes[name]); // just the value attribute for now

       if (name === 'value') { 
          this.r2.listen(el, 'click', () => {
            this.goto(element.attributes[name]); // event binding with property "value" as parameter to navigate to
          })
        }
      } else {
        throw new Error('no html tag specified as element...');
      }
    })
  }
})

this.r2.appendChild(this.container.nativeElement, parent); // div added to the DOM

不需要 html sanitizer 也不需要使用 routerLink 或者只需注入 Router 并导航到您想要的路由!对代码进行改进以使其符合您的需求,这至少应该是一个很好的起点

祝你好运!

【讨论】:

  • 感谢@Alejandro Camba。我在我的工作副本中使用了 ViewChild 和路由,但为了简单起见,stackbliz 没有添加。如果我们使用 innerHTML,您的解决方案将不起作用。我的内容将来自服务/数据库。我已经尝试过 HTML Sanatizer,但在使用 Sanatizer 时我遗漏了一些东西。如果你能在那里帮助我,那就太好了。
  • 究竟是什么不起作用?请记住,事件绑定不适用于 innerHTML,您必须在获取 html 内容并将其设置为您的 innerHTML 后手动绑定事件。查看 ngAfterViewInit 的代码
  • 嗨@Alejandro Camba 我创建了另一个stackbliz 为了更清楚请看一下stackblitz.com/edit/…
【解决方案2】:

你有一个 CSS 问题。

  1. &lt;a href="something"&gt;&lt;/a&gt; 看起来像一个链接
  2. &lt;a [routerLink]="something"&gt;&lt;/a&gt; 看起来像一个链接,因为如果您检查 HTML,它实际上会添加一个 href 属性,因为 routerLink
  3. &lt;a (click)="goTo()"&gt;&lt;/a&gt; 看起来不像是链接,因为没有 href

Chrome 和 Safari 的默认用户代理 css 不会在没有 href 的情况下设置 &lt;a&gt; 的样式(尚未确认 Firefox,但我确信它很有可能)。像 bootstrap 这样的框架也是如此。

更新 stackblitz,将 CSS 移至全局,而不是 app.css https://stackblitz.com/edit/angular-ivy-kkgmkc?embed=1&file=src/styles.css

这会将所有链接的样式设置为默认蓝色,如果该浏览器支持,则为 -webkit-link。如果您希望它在整个应用程序中运行,它应该在您的 global.css 文件中。

a {
  color: rgb(0, 0, 238);
  color: -webkit-link;
  cursor: pointer;
  text-decoration: underline;
}

【讨论】:

  • 谢谢@cjd82187。但是当我们使用innerHTML时这不起作用。这是链接stackblitz.com/edit/angular-ivy-gynf1a
  • 那是因为 CSS 位于 app.css 中,将其范围限定为特定组件。内部 HTML 并没有意识到这一点。我在新的 stackblitz 中将其移至全局样式 CSS。同样,所有这些都归结为 CSS 问题。如果您检查元素,您可以看到正在应用什么以及为什么/为什么不为其他人应用。
  • 哦,我看到您更新了您原来的问题,即您也需要在 innerHTML 中发生点击事件,而不仅仅是样式。今晚晚些时候我可以试试看,现在没有时间。
【解决方案3】:

这对我来说非常适合:D

 @Directive({
  selector: "[linkify]",
})

// * Apply Angular Routing behavior, PreventDefault behavior
export class CustomLinkDirective {
  @Input()
  appStyle: boolean = true;
  constructor(
    private router: Router,
    private ref: ElementRef,
    @Inject(PLATFORM_ID) private platformId: Object
  ) {}

  @HostListener("click", ["$event"])
  onClick(e: any) {
    e.preventDefault();
    const href = e.target.getAttribute("href");
    href && this.router.navigate([href]);
  }

  ngAfterViewInit() {
    if (isPlatformBrowser(this.platformId)) {
      this.ref.nativeElement.querySelectorAll("a").forEach((a: HTMLElement) => {
        const href = a.getAttribute("href");
        href &&
          this.appStyle &&
          a.classList.add("text-indigo-600", "hover:text-indigo-500");
      });
    }
  }
}

我如何使用它

 <p linkify
     class="mt-3 text-lg text-gray-500 include-link"
     [innerHtml]="apiSectionText"
   ></p> 

result

【讨论】:

  • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
猜你喜欢
  • 2022-10-20
  • 2018-12-16
  • 2021-03-30
  • 1970-01-01
  • 2017-04-20
  • 1970-01-01
  • 2021-01-24
  • 2018-01-26
  • 1970-01-01
相关资源
最近更新 更多