简体   繁体   English

如何在 Angular2 中动态创建 SVG 组件?

[英]How to create an SVG component dynamically in Angular2?

I am creating a web application which uses SVG.我正在创建一个使用 SVG 的 Web 应用程序。 I have created components consist of SVG element, and they are put into a root svg element.我创建了由 SVG 元素组成的组件,并将它们放入根svg元素中。 They have attribute selector, because SVG/XML document tree is strict so I cannot use element selector.他们有属性选择器,因为 SVG/XML 文档树很严格,所以我不能使用元素选择器。 And they have a template starts with svg:g tag:他们有一个以svg:g标签开头的模板:

@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 :我认为可以通过使用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.组件是在onMouseDown()调用时创建的,但是它的 DOM 元素是div ,所以它是 svg 文档中的非法元素,无法显示。

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 .我尝试过使用selector='svg:g[foo]' ,然后创建了g元素,但它的命名空间不是用于 SVG ( http://www.w3.org/2000/svg ),而是普通的 HTML 命名空间 ( http://www.w3.org/1999/xhtml ),它的类是HTMLUnknownElement > g

I also tried with selector='svg:svg[foo]' , then svg:svg element is created and it is displayed.我也尝试过使用selector='svg:svg[foo]' ,然后创建svg:svg元素并显示它。 But svg:svg cannot move with transform attribute so this doesn't work well for my application.但是svg:svg不能使用transform属性移动,所以这对我的应用程序来说效果不佳。

How can I dynamically create svg:g element for attribute selector component?如何为属性选择器组件动态创建svg:g元素?

I am using Angular2: 2.0.0-rc4.我正在使用 Angular2:2.0.0-rc4。

You're right about the namespacing issues keeping the g element from rendering as svg .您是正确的关于使g元素无法呈现为svg的命名空间问题。 Unfortunately, attaching the node as an svg element is the only way to feasibly get the component to namespace properly.不幸的是,将节点附加为一个 svg 元素是使组件正确地进入命名空间的唯一方法。

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.如果您在模板中的 g 元素上添加拖动功能作为指令,它将与您的组件一起编译,您可以将您的逻辑偏移到该指令中。 The top level svg will be namespaced correctly, and the template will inherit this accordingly.顶级 svg 将被正确命名,模板将相应地继承它。

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.这可能并不理想,但在解决https://github.com/angular/angular/issues/10404之前,没有太多替代方案。

I am not sure that my solution is right but it works for me (even with ivy ):我不确定我的解决方案是否正确,但它对我有用(即使使用ivy ):

@Component({
...
})
class ParentComponent {
    constructor(
        private injector: Injector,
        private appRef: ApplicationRef,
        private componentFactoryResolver: ComponentFactoryResolver,
    ) {}

    createDynamicComponent() {
        let node = document.createElementNS("http://www.w3.org/2000/svg", "svg");
        let factory = this.componentFactoryResolver.resolveComponentFactory(DynamicComponent);
        let componentRef = factory.create(this.injector, [], node);

        this.appRef.attachView(componentRef.hostView);
    }
}

After that you must manually append node into DOM.之后,您必须手动将node附加到 DOM 中。

Instead of trying to create your component to the view with the Component Resolver I will do this instead.我不会尝试使用 Component Resolver 为视图创建组件,而是这样做。

  • Create a object with properties which match the attributes you want to pass to your SVG Component.创建一个对象,其属性与要传递给 SVG 组件的属性相匹配。
  • Append this object to an array (ex.svgItems).将此对象附加到数组(例如.svgItems)。
  • Add *ngFor="svgItem in svgItems" to the SVG component you want to create dynamically.将 *ngFor="svgItem in svgItems" 添加到要动态创建的 SVG 组件。

Hope it's clear and solve your problem.希望它清楚并解决您的问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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