How to create an SVG component dynamically in Angu

2019-05-06 09:36发布

I am creating a web application which uses SVG. I have created components consist of SVG element, and they are put into a root svg element. They have attribute selector, because SVG/XML document tree is strict so I cannot use element selector. And they have a template starts with svg:g tag:

@Component({
  selector:'[foo]',
  template: '<svg:g>...</svg:g>',
})

In the application, I want to create a component when a user press a button, and simultaneously start dragging it. I thought it can be achieved by creating a component dynamically using ComponentResolver:

  @ViewChild('dynamicContentPlaceHolder', {read: ViewContainerRef})
  protected dynamicComponentTarget: ViewContainerRef

  private componentResolver: ComponentResolver

  onMouseDown() {
    this.componentResolver
      .resolveComponent(FooComponent)
      .then((factory) => {
        const dynamicComponent = this.dynamicComponentTarget.createComponent(factory, 0)
        const component: FooComponent = dynamicComponent.instance
        const element = dynamicComponent.location.nativeElement
        // add event listener to start dragging `element`.
      })
  }

Component is created when onMouseDown() called, but its DOM element is div, so it is illegal element in svg document and cannot be displayed.

I have tried with selector='svg:g[foo]', then g element is created, but its namespace is not for SVG (http://www.w3.org/2000/svg), but normal HTML namespace (http://www.w3.org/1999/xhtml) and its class is HTMLUnknownElement > g.

I also tried with selector='svg:svg[foo]', then svg:svg element is created and it is displayed. But svg:svg cannot move with transform attribute so this doesn't work well for my application.

How can I dynamically create svg:g element for attribute selector component?

I am using Angular2: 2.0.0-rc4.

标签: svg angular
2条回答
Viruses.
2楼-- · 2019-05-06 10:36

You're right about the namespacing issues keeping the g element from rendering as svg. Unfortunately, attaching the node as an svg element is the only way to feasibly get the component to namespace properly.

However, this doesn't mean this won't work. If you add the drag functionality as a directive on the g element in the template, it will be compiled with your component, and you can offset your logic into that directive. The top level svg will be namespaced correctly, and the template will inherit this accordingly.

import {Component, Input} from '@angular/core';

@Component({
  selector: 'svg:svg[customName]', // prevent this from hijacking other svg
  template: '<svg:g dragDirective>...</svg:g>', // note the directive added here
  style: []
})
export class GComponent {

  constructor() { }

}

This may not be ideal, but until https://github.com/angular/angular/issues/10404 is resolved, there's not much of an alternative.

查看更多
Anthone
3楼-- · 2019-05-06 10:37

Instead of trying to create your component to the view with the Component Resolver I will do this instead.

  • Create a object with properties which match the attributes you want to pass to your SVG Component.
  • Append this object to an array (ex.svgItems).
  • Add *ngFor="svgItem in svgItems" to the SVG component you want to create dynamically.

Hope it's clear and solve your problem.

查看更多
登录 后发表回答