我有一个Angular Pipe:
import {Pipe, PipeTransform} from '@angular/core';
import { DomSanitizer } from '@angular/platform-browser';
import * as Remarkable from 'remarkable';
import * as toc from 'markdown-toc';
@Pipe({
name: 'MarkdownToc'
})
export class MarkdownTableOfContentsPipe implements PipeTransform {
constructor(private sanitized: DomSanitizer) {}
public transform(markdown: string) {
const toc_opts = {
linkify: function(tok, text, slug, options) {
const regex = /(.+\b)(.*)$/
slug = slug.replace(regex, function(str, g1) { return g1; });
tok.content = `[${text}](#${slug})`;
return tok;
}
}
const toc_md = new Remarkable('commonmark')
.use(toc.plugin(toc_opts))
const md = new Remarkable('commonmark')
md.renderer.rules.link_open = function(tokens, idx, options /* env */) {
var title = tokens[idx].title ? (' title="' + Remarkable.utils.escapeHtml(Remarkable.utils.replaceEntities(tokens[idx].title)) + '"') : '';
var target = options.linkTarget ? (' target="' + options.linkTarget + '"') : '';
return '<a href="/articles' + Remarkable.utils.escapeHtml(tokens[idx].href) + '"' + title + target + '>';
};
const toc_md_text = toc_md.render(markdown);
console.log(md.render(toc_md_text.content));
return this.sanitized.bypassSecurityTrustHtml(md.render(toc_md_text.content));
}
}
它会生成一个链接列表(这是一个缩短的列表):
<ul>
<li><a href="/articles#introduction">Introduction</a></li>
<li><a href="/articles#downloads">Downloads</a></li>
</uL>
但是,显示的每个链接都是“file:///”+ href,这当然不起作用。是否有某种方法来修复hrefs以使其工作或以其他方式。
在我的控制器中,我有这个功能:
private async _show() {
const db = await this.databaseService.get();
const id = this.route.snapshot.paramMap.get('id').split('-').join(' ');
const article$ = db.article
.findOne(toTitleCase(id))
.$;
this.sub = article$.subscribe(async article => {
this.article = article;
const attachment = await this.article.getAttachment('index.md');
this.text = this.markdownPipe.transform(await attachment.getStringData());
this.intermoduleservice.toc = this.markdownTocPipe.transform(await attachment.getStringData());
this.zone.run(() => {});
});
}
InterModuleService
是一个全局服务,用于将TOC推送到TOC所在的Side Nav菜单。当我通过此服务将TOC html推送到Side Nav时,似乎没有对HTML执行渲染更新。所以[routerLink]
绑定或Angular特定代码永远不会正确更新。
答案 0 :(得分:0)
所以这就是我要做的事情。 href
链接自己只是一种解决方法,因为你可能将html设置为普通字符串,这个字符串无法抓住,因此没有像路由这样的角度特定代码。这并不理想,因为它导致整页重新加载和另一个完整的角度初始化,最终需要花费很多时间。
让我们看看我们是否可以通过尝试实现原生角度路由来加快速度。为了清楚起见,我不知道这是否适用于Electron。你可以做的是参考你用@ViewChild()
注入降价的html标签。我在谈论通过角度角度组件创建的最后一级html。假设您使用管道的div
属性为innerHTML
。
<div [innerHTML]="originalMarkdown | MarkdownToc" #markdownDiv></div>
现在这种方式,内部HTML完全插入为一个字符串而没有角度知道里面是什么,所以没有角度的东西在这个div内部工作。你可以做的是使用该div上的引用来在创建之后沿着innerHTML 的HTML树走下去。
@ViewChild('markdownDiv') markdownDiv: ElementRef;
// this hook makes sure the html is already available
ngAfterViewInit() {
// find a link tag inside your innerHTML
// this can be any selector, like class, id or tag
var link = this.markdownDiv.nativeElement.querySelector("a");
// ... do something with the link
}
你说你有多个链接,也许你必须使用另一个选择器,甚至在html创建中给每个链接一个ID,并在这里引用。只要您有对该链接的引用,就可以使用它来创建一个onClick函数,例如可以使用路由。我们需要一些名为Renderer2
的东西来帮助我们。它作为正常服务注入。以下是您需要的大部分代码,可以放在component.ts
。
constructor(private renderer: Renderer2, private router: Router){}
@ViewChild('markdownDiv') markdownDiv: ElementRef;
// this hook makes sure the html is already available
ngAfterViewInit() {
// find a link tag inside your innerHTML
// this can be any selector, like class, id or tag
var link = this.markdownDiv.nativeElement.querySelector("a");
// ... on click
this.renderer.listen(link, 'click', (event) => {
this.router.navigateByUrl("/articles#introduction");
});
}
现在要做很多事情,但它可以通过比现在尝试更快的实现来解决您的问题。如果您有任何疑问,请随时提出。
我认为这里描述的生命周期钩子ngAfterViewInit()
可能为了查询你的元素还为时过早,因为在这个阶段DOM尚未更新。如果我看到它正确,您正在进行数据库调用以创建降价,这需要时间。 Angular通常只是在模板的视图中,并且之后不依赖于动态DOM操作。
我们要做的事情可能是进入后期阶段。这有点棘手,但应该仍然可以管理。我们要使用的是ngAfterViewChecked()
。
ngAfterViewChecked()
在Angular检查组件的视图和子视图后进行响应。 在ngAfterViewInit之后以及随后的每一次调用ngAfterContentChecked()
。
它与ngAfterViewInit()
非常相似,我们只需要确保我们不会在同一个<a>
标记上多次创建点击侦听器,因为可能会经常调用该函数。所以请记住,我们只想做一次。所以我们需要声明一个布尔值,表明我们是否已经添加了一个监听器。此外,我们应该检查DOM是否真的存在,因为在注入HTML之前,该函数也被调用了几次。这是代码。
constructor(private renderer: Renderer2, private router: Router){}
@ViewChild('markdownDiv') markdownDiv: ElementRef;
// this is our boolean that prevents us from multiple addings
listenerAdded: boolean = false;
// this hook makes sure the html is already available
ngAfterViewChecked() {
// check if the div we watch has some innerHTML and if we haven't set the link already
if(this.markdownDiv.nativeElement.childNodes.length>0 && !listenerAdded) {
// find a link tag inside your innerHTML
// this can be any selector, like class, id or tag
var link = this.markdownDiv.nativeElement.querySelector("a");
// ... on click
this.renderer.listen(link, 'click', (event) => {
this.router.navigateByUrl("/articles#introduction");
});
// remember not to do it again
this.listenerAdded=true;
}
}
答案 1 :(得分:0)
好的,所以我向持有TOC的click
添加了<div></div>
个事件:
<mat-sidenav mode="side" #sidenav id="sidenav" fixedInViewport="fixed" fixedTopGap="65">
<button mat-menu-item [routerLink]="['articles']">
<mat-icon svgIcon="arrow-left"></mat-icon>
Return to Article List
</button>
<div class="sidenav-toc" (click)="onTocClick($event)" [innerHtml]="article | MarkdownToc" id="toc" #tocDiv></div>
</mat-sidenav>
然后在我的sidenav组件上添加了两个函数:
public onTocClick(event: Event) {
const elem = event.target as Element;
if (elem.tagName.toLowerCase() == 'a') {
const frag = elem.getAttribute('data-link');
const id = this.interModuleService.currentArticle.split(' ').join('-');
this.goTo(frag);
}
}
goTo(anchor: string) {
// TODO - HACK: remove click once https://github.com/angular/angular/issues/6595 is fixed
(<HTMLScriptElement>document.querySelector('#'+ anchor)).scrollIntoView();
}
我曾想过某种动态组件。但是,由于Angular中的问题,由于上述问题,滚动到锚点并不容易。