Visualizing hierarchical data in Angular 12 using D3 tree with clickable navigation links

I'm facing a challenge in displaying a routerLink within my d3.tree(). I've attempted to do so like this:

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

However, the following code works:

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

The issue is that the code with routerLink only displays the text and is not clickable, unlike when directly written into the component where everything works fine.

Answer №1

create a custom link directive called custom-link.directive.ts

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

// Enhance Angular Routing functionality and prevent default 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]);
  }

  // Apply styling to links
  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");
      });
    }
  }
}

implement the directive in your code

const apiSectionText= "Access our <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> 

View live demo on StackBlitz

Answer №2

Update: Kindly refer to the response provided by @K-coding, as it offers a more efficient approach than mine!

Here is another less effective approach:

Angular does not compile, so if you add an .html file, you cannot use "Angular" to control it. You need to handle it in JavaScript.

The issue with mixing JavaScript and Angular is that we cannot rely on the function names. Hence, we must dispatch a CustomEvent and subscribe to it.

Let's create a function that retrieves all the "link" elements inside a div called wrapper using 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');
      
      //for each link
      for (var i = 0; i < links.length; i++) {
          //we change the color only to check if it works
          links[i].style.color = 'red';

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

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

            //dispatch a custom event from "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]);
      });
}
}

Here is a simple stackblitz example

NOTE: In the stackblitz, notice that I call the function "createLink" after everything is painted. I use a setTimeout to allow Angular time to render - alternatively, you could use ngAfterViewInit, but when creating the D3 tree, you need to call it after the rendering process.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Iterating over a JSON array using *ngFor

Here is the JSON structure that I have: { "first_name": "Peter", "surname": "Parker", "adresses": { "adress": [{ "info1": "intern", "info2": "bla1" }, { "info1": "extern", "info2": "bla2" }, { "info1": " ...

Angular 2 - Ensuring mandatory fields are completed when checkbox is checked

Hey everyone, I'm a newcomer to Angular2 and JS frameworks in general. I've been following tutorials on the official site but I can't seem to find a solution to my problem. So, I have a checkbox that is optional, but if it is checked, a new ...

TS2688 Error: Type definition file for 'tooltip.js' not found

Why am I getting an 'undefined' error when trying to import the Tooltip class from the npm tooltip.js package in my TypeScript file? ...

Unable to locate dependencies while testing the react package locally

Recently, I came across this npm package designed for React using Typescript. To debug it locally, I initiated npm link in a new React project but encountered an error: https://i.sstatic.net/nObH6.png I suspect it may not be reading the packages correct ...

Passing dynamic values to nested components within an ngFor loop in Angular

I'm currently facing an issue with a child component inside a ngFor loop where I need to pass dynamic values. Here is what I have attempted so far, but it doesn't seem to be working as expected <div *ngFor="let item of clientOtherDetails& ...

Getting the date from an XHR header in Angular2: A step-by-step guide

Is it possible to retrieve a date from XHR Header Response? https://i.sstatic.net/ErMMh.jpg I attempted to include '{observe: 'response'}' as options constructor(private http: HttpClient) { } getAllTemps() { return this. ...

What is the best way to specify a type for an object without altering its underlying implicit type?

Suppose we have a scenario where an interface/type is defined as follows: interface ITest { abc: string[] } and then it is assigned to an object like this: const obj: ITest = { abc: ["x", "y", "z"] } We then attempt to create a type based on the valu ...

Angular: A guide to exporting a service from an npm module

I have a useful service within my Angular 2 package that I am looking to publish on NPM. Here are the key lines of code for this service: public showModal: Subject<any> = new Subject<any>(); public $showModal = this.showModal.asObservable(); ...

RXJS: Introducing a functionality in Observable for deferred execution of a function upon subscription

Implementing a Custom Function in Observable for Subscribers (defer) I have created an Observable using event streams, specifically Bluetooth notifications. My goal is to execute a function (startNotifictions) only when the Observable has a subscriber. ...

Encountering an issue post installation of phonegap-plugin-push on an Ionic 2 project

I'm working on an exciting project with Ionic 2/Angular 2 that involves using Amazon SNS/GCM. My main goal is to efficiently send and receive push messages through GCM. To set up the push plugin, I followed these steps:   ionic plugin add phonega ...

Setting up CI/CD for a project involving an API, Angular application, and database on Azure App Services

In my VSTS local GIT REPO, I have a solution file with three main projects: an API, an Angular App, and a SQL Server DB Project. There are also some test projects included in the solution. I am currently facing challenges in setting up CI/CD for this setu ...

Implement CSS to globally style material.angular's mat-card by customizing the background color

Looking for a way to globally change the background of mat-card on material.angular.io. I attempted adding the following code snippet to styles.css with no success. mat-card { background-color: purple; } ...

Is it possible to integrate the extension library of three.js (including OBJLoader, SceneUtils, etc.) with Angular 6?

Attempting to implement the below code, however, it is not functioning as expected. npm install --save three-obj-loader import * as ThreeObjLoader from 'three-obj-loader'; const OBJLoader = ThreeObjLoader(THREE); let loader = new OBJLoader(): ...

The information is being properly displayed in the console, but when attempting to show it on the webpage, an ERROR occurs with the message: "Error trying to differentiate '[object Object]'"

The data is successfully displayed in the console. However, when trying to display it on the page, an error occurs: ERROR Error: Error trying to diff '[object Object]'. Only arrays and iterables are allowed services getdetails(id:number) : ...

Using ServiceWorker with React and Typescript

If you are working on a javascript-based React project, adding a Service Worker is simply a matter of updating serviceWorker.unregister() to serviceWorker.register() in index.jsx. Here is an example project structure: - src |- index.jsx |- serviceWo ...

Display Module within Component using Angular 5

In the application I'm working on, I want to incorporate a variety of progress-loader-animations such as spinners or bars. To achieve this, I've developed a module with a component. Now, I'm trying to figure out how to display the module&ap ...

Properly specifying the data type for a generic type variable within a function in TypeScript

As I work on my express project, I am currently coding a function called route. const morph = (params: Function[]) => (req: Request) => params.map(f => f(req)) const applyTransformers = (transformers: Function[]) => (response: any) => { ...

Expanding a Typescript class with a new method through its prototype

https://i.sstatic.net/3hIOo.png I'm encountering an issue while attempting to add a method to my Typescript class using prototype. Visual Studio is giving me a warning that the function does not exist in the target type. I came across some informati ...

What is the essential setup needed for a bootstrap dropdown list?

I'm currently working on an Angular project and I've been trying to implement the dropdown navbar from Bootstrap. I simply copied the code from here, but unfortunately, the dropdown feature isn't functioning properly for me: // I've att ...

There was a parsing error due to encountering an unexpected reserved word 'interface' in the code, as flagged

I'm encountering an issue with my code when trying to utilize Props. The error message I'm receiving is "Parsing error: Unexpected reserved word 'interface'. (3:0)eslint". This project is being developed using next with TypeScript. Er ...