简体   繁体   中英

How to reach SVG elements by Angular ViewChildren

I have an svg loaded as an object:

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

This svg contains a big png map and several rect and text elements.

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

I'd like to get all the rect elements in an array just like I do with normal html elements like this:

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

The problem is that the rects array is always empty. I have tried to get them in ngViewAfterInit() and in ngAfterViewChecked() without success. How can I reach the elements of an svg?

I haven't tried to get them by getElementsById() yet, I'd like to do it the angular way if it's possible.

The whole thing is just about giving contextual colors of each rect elements once I get them.

ViewChild or ViewChildren cannot support CSS selectors

https://angular.io/api/core/ViewChild

Supported selectors include:

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

you can get the HtmlElement from a template reference variable and start trying to manipulate it after Object Element Resource Loaded

online example

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")}`);
        })

    }    
  }

Did some more research and found the solution below. The fun part is that this code is in the ngAfterViewInit method of my angular component and it still needs the window.onload eventlistener to work. Why?

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 key components are the contentDocument property and the onload eventlistener. Some useful info for future reference: Using Javascript with SVG

I just wanted to add one small thing to the answer that @Chunbin Li gave (I don't have enough reputation to comment at the moment sadly). Anyways, in the comments section, @Perrier said that this.rects is still an empty nodes list. This can be fixed by using a different lifecycle method.

When using ViewChild , it's common to utilize the lifecycle method ngAfterViewInit() . In @Chunbin Li's answer, the function is being called in ngOnInit() , so most likely the SVG hasn't had enough time to render. If you call the function inside ngAfterViewInit() instead, it would give the DOM enough time to fully render the SVG, and the SVG's properties will be accessible.

For further reading, check out the angular docs on ViewChild .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM