Unlock the power of Angular ViewChildren to access and manipulate SVG elements efficiently

I have an SVG file loaded as an object:

<object data="assets/img/states.svg" type="image/svg+xml" id="map"></object>

This SVG includes a large PNG map along with several rect and text elements.

<rect
     y="224.72084"
     x="644.87109"
     height="231.68137"
     width="101.08391"
     id="RECT_TO_GET" />

I want to store all the rect elements in an array, similar to how I do it with regular HTML elements using Angular's @ViewChildren:

@ViewChildren('rect') rects !: QueryList<any>;
this.rects.forEach(r=> {
            console.log(r);
        });

The issue is that the rects array always remains empty. I've attempted to access them in both ngViewInit() and ngAfterViewChecked() without any luck. How can I retrieve elements from an SVG?

I haven't tried using getElementsById() yet, as I prefer to handle it the Angular way if possible.

The main goal here is to assign specific colors to each rect element based on their context once retrieved.

Answer №1

ViewChild and ViewChildren do not have the capability to support CSS selectors.

Check out this link for more information on ViewChild

The supported selectors include:

  • any class with the @Component or @Directive decorator
  • a template reference variable as a string (e.g. query with @ViewChild('cmp'))
  • any provider defined in the child component tree of the current component (e.g. @ViewChild(SomeService) someService: SomeService)
  • any provider defined through a string token (e.g. @ViewChild('someToken') someTokenVal: any)
  • a TemplateRef (e.g. query with @ViewChild(TemplateRef) template;)

You are able to retrieve the HtmlElement from a template reference variable and then proceed to manipulate it after the Object Element Resource has Loaded.

Here is an online example for reference

HTML

<object #mySvg data="./assets/new.svg" type="image/svg+xml" id="map"></object>

Component

@ViewChild('mySvg') mySvg;

  ngOnInit() {
      const objElm = (this.mySvg.nativeElement as HTMLObjectElement);
    objElm.onload = () => {
        const paths = objElm.contentDocument.querySelectorAll('path');

        paths.forEach((path,index) => {
          console.log(`path:${index} , d=${path.getAttribute("d")}`);
        })

    }    
  }

Answer №2

I wanted to contribute a small addition to the response provided by @Chunbin Li (I currently lack the reputation to comment, unfortunately). In the discussion, @Perrier noted that this.rects remains an empty nodes list. This issue can be resolved by employing a different lifecycle method.

When utilizing ViewChild, it is recommended to make use of the lifecycle method ngAfterViewInit(). As per @Chunbin Li's explanation, the function is being called in ngOnInit(), which likely means that the SVG has not been completely rendered yet. By moving the function call to ngAfterViewInit(), sufficient time will be given for the DOM to render the SVG entirely, thus making the SVG properties accessible.

For more information, refer to the angular documentation on ViewChild.

Answer №3

After conducting further investigation, I was able to uncover the solution provided below. What's interesting is that this code resides within the ngAfterViewInit method of my Angular component and still requires the window.onload event listener to function properly. Why is that necessary?

window.addEventListener("load", function() {
    var svgObject = document.getElementById('map').contentDocument;
    var svg = svgObject.getElementById('svg48152');
    console.log(svg);
    const rects = svg.querySelectorAll('rect');
    console.log(rects);
    rects.forEach(r => {
        console.log(r);
    });
});

The crucial elements here are the contentDocument property and the onload event listener. For future reference, some helpful information can be found at: Using JavaScript with SVG

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

The router's handler function sends back a collection of objects, but for some reason, the client is not receiving them in JSON format even though the response

I am currently developing an Express.js project using Typescript. In my project, I have defined an enum and an interface as follows: export enum ProductCategory { ELECTRONICS = 'electronics', CLOTHING = 'clothing', TOYS = & ...

The TS2339 error has occurred because the "email" property cannot be found on the specified "Object" type

I encountered an issue while using Angular 5, here is the code snippet : signIn(provider) { this._auth.login(provider).subscribe( (data) => { console.log(data); this.hideForm = false; this.emaill = data.email; ...

Comprehensive compilation of Typescript error codes along with solutions

Where can I find a comprehensive list of Typescript error codes and their corresponding fixes? I frequently encounter errors during compilation, such as: data_loader_service.ts(10,13): error TS1005: '=>' expected. data_loader_service.ts(10,24 ...

When a link containing an ID hash in the URL is opened in a new tab, the ID within the link

Is there a way to create a link that leads to http://localhost:4300/?id=RTnySsxr8T2lPIihu2LqMw==&lang=en-us Upon clicking the link, a new tab opens in the browser with that URL. The issue arises when the hashed ID in the address bar changes to http:/ ...

The variance in module types within the tsconfig.json file

When configuring your project in tsconfig.json, you may come across the following options: { "compilerOptions": { "target": "es5", "module": "commonjs", "moduleResolution": "node", "sourceMap": true, "emitDecoratorMetadata": t ...

The Glyphicon is failing to display on the template

I recently set up bootstrap in my VS Code environment and linked it to the styles.css file with this code snippet: @import '~bootstrap/dist/css/bootstrap.css'; Upon further inspection of the package.json, I confirmed that "bootstrap": "^4.1.1" ...

Creating Dynamic HTML/DOM in Angular 14: A Guide for Adding New Items to a List

I am currently iterating through a list of items and displaying them within a div element. These items are rendered when the page initially loads. <button (click)="addCut()" mat-raised-button color="primary">Add New Cut</button ...

Testing Angular Components Using HostListener

I am looking to write unit tests for @HostListener, but I'm unsure of how to go about it since it's located on top of the component. Here is an example of the code: @HostListener('document:click', ['$event']) onDocumentClick ...

How can Angular components that are not directly related subscribe to and receive changes using a declarative approach?

Here's the issue at hand: I'm facing a problem with my Dashboard component. The Dashboard consists of a Sidebar and Navbar as child components. On the Sidebar, I have applied a custom permission directive to the links to determine if the user ha ...

How to Share Angular Modules Between Two Projects with Ivy Compilation Necessity

Query: I am faced with the challenge of sharing common modules between two Angular projects, one of which requires full Ivy compilation to function properly. To manage these shared resources, we have set up a private GitHub NPM repository. However, becaus ...

The Angular HttpClient Service will exclusively provide responses that have a status code of 200

I'm facing an issue with my login component where it calls an http client service to send a request to the server for logging in. Everything works smoothly when I enter valid credentials, but if I input wrong credentials, the service doesn't seem ...

Is it possible to incorporate shadows on a pie chart using recharts?

Does anyone know how to enhance a pie chart with shadows using the recharts library? https://i.sstatic.net/JLGVF.png https://i.sstatic.net/f5qv4.png If you have any solutions, please share. Thank you! ...

Ways to determine the presence of a value in an array

Here is an example array: [ {practitioner: "place_1509136116761", H0709: false, H0911: false, H1113: false, H1315: false}, {practitioner: "place_1509136116772", H0709: true, H0911: false, H1113: true, H1315: false}, {practitioner: "place_15091361166 ...

What is the best way to generate a box when a button is pushed?

How can I make a button create a box with text inside it when clicked? <div class="trades"> </div> <button onclick="create()">add</button> Here is the JavaScript function to create the box: function create() { var box = docu ...

Sharing Information Across Angular Routes

I've been encountering a slight issue when working with routes in Angular 4. Whenever I attempt to pass data from one component to another using navigate('root', data), all I end up receiving is [object Object],[object Object],[object Object ...

Translate language in an Angular file using TypeScript

const typeArray= [ { id: 'PARENT', name: '{{ appConstants.type.PARENT | translate }}' }]; What is the best way to incorporate translations when declaring an array in a TypeScript file? ...

displaying a pair of inputs next to each other

Is it possible to display two input fields side by side? I am using Angular's matInput forms, but struggling to position the second input next to the first. What I would like to achieve is to have "input1 , input2" on the same line. Here is my code: ...

"Utilizing Primeng's P-Table Component for Enhanced Interactivity

Greetings to all! I am currently working with p-table, p-headercheckbox, and p-tableCheckbox along with additional filters for each column. However, I am encountering an issue where the filters are not aligning properly with the headers. ...

Concealing the sidebar in React with the help of Ant Design

I want to create a sidebar that can be hidden by clicking an icon in the navigation bar without using classes. Although I may be approaching this incorrectly, I prefer to keep it simple. The error message I encountered is: (property) collapsed: boolean ...

The error message 'ReferenceError: MouseEvent is not defined' indicates that

Recently, I attempted to incorporate ng2-select into a project that relies on angular/universal-starter (TypeScript 2.x) as its foundation. (Interestingly, ng2-select worked perfectly fine when added to an angular-cli generated project.) However, upon ad ...