1

I would like to display a routerLink inside my d3.tree(), but I am struggling with getting it displayed as a hyperlink.

I have tried like this

.append("a")
.html(`<a [routerLink]="['/mycomponent']" fragment="1.1">link to user component</a>`);

given that this

.append("a")
.html(`<a href="mycomponent#1.1">link to user component</a>`);

works, but I don't want the whole page to reload.

The code with routerLink just writes the text and it is not clickeable, while everything is displayed correctly when just writing it into the component itself.

2 Answers2

1

create custom-link.directive.ts

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

  // * styling
  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");
      });
    }
  }
}

use it

const apiSectionText= "Take <a href='/tracking-api'>Tracking API</a> and <a href='/tracking-webhook'>Tracking Webhook</a> solutions,"


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

Result-Blitz

k-coding
  • 11
  • 2
  • I feel that your approach not work, the "linkfify" has no attribute href. If you put the "linkify" inside the html not work also because Angualr not compile "at time" – Eliseo Mar 08 '22 at 09:13
  • It works, it used this method on ship24.com. you can check this out. [innerHtml] is for angular to compute a link as HTML element. – k-coding Mar 09 '22 at 05:15
  • amazing!! it's true (and now I know the reason) it's a better solutions than mine, thanks – Eliseo Mar 09 '22 at 07:29
  • if you do not want to use @HostListener from angular, just use this a.addEventListener("click",(e)=>{ e.preventDefault(); const href = e.target.getAttribute("href"); href && this.router.navigate([href]); }); – k-coding Mar 09 '22 at 14:52
  • The good of your directive (and not add the event to the "a") is that, if you change the innerHTML the links go on working (I didn't know how resolve this problem). I prefer use `fromEvent`rxjs operator instead HostListener, but it's only a preference. A 3dtree are changing the nodes you see.This is the reason because your solution is amazing and resolve the problem (I not delete mine because "from the errors someone can learn too") – Eliseo Mar 09 '22 at 17:24
0

Update please, check the @K-coding's response, it's a better aproach than mine!

A very poor another aproach:

Angular not compile, so if you add an .html you can not use "Angular" to control. You need make in JavaScript.

The problem when mixing JavaScript and Angular is that we can not trust in the name of the functions. So we need dispatch a CustomEvent and subscribe to it

Well, we are going to create a function that gets all the "link" inside a div I call wrapper and use a template reference variable

<div #wrapper>
  <div [innerHTML]="link"></div>
</div>

  createLinks()
  {
    //get the "links" inside the "wrapper"
    const links = this.wrapper.nativeElement.getElementsByTagName('a');
      
      //with each link
      for (var i = 0; i < links.length; i++) {
          //we change the color only to check if work
          links[i].style.color = 'red';

          //we override the event click
          links[i].onclick = (event) => {
            //prevent default
            event.preventDefault();

            //get the "target" using getAttribute('href')
            const target = (event.target as HTMLElement).getAttribute('href');

            //dispatch a custom event in "wrapper"

            this.wrapper.nativeElement.dispatchEvent(
              new CustomEvent<string>('route', { detail: target })
            );
          };
      }
  }

//and in ngOnInit, we subscribe to the custom event

ngOnInit()
{
  fromEvent(this.wrapper.nativeElement, 'route').subscribe((res: any) => {
        this.router.navigate([res.detail]);
      });
  }
}

A simple stackblitz

NOTE: See in the stackblitz that I call to the function "createLink" "after" all is painted. In the stackblitz I use a setTimeout to give time to Angular to paint -in this case I coud use ngAfterViewInit, but in case you create the D3 tree you need call it after paint the 3D

Eliseo
  • 50,109
  • 4
  • 29
  • 67