Angular: использование ComponentFactoryResolver для динамического создания компонентов, рендеринга внутри SVG

У меня есть компонент, который отображает DOM, который должен находиться внутри тега svg:

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

@Component({
  selector: 'g[hello]',
  template: `<svg:text x = "50%" y = "50%" text-anchor = "middle">Hello, {{name}}</svg:text>`,
  styles: [`h1 { font-family: Lato; }`]
})
export class HelloComponent  {
  @Input() name: string;
}

Когда я создаю его статически, все работает нормально (текст виден на странице):

<svg>
  <svg:g hello name = "Static component"></svg:g>
</svg>

Генерируется следующий DOM:

<svg _ngcontent-iej-c129 = "">
  <g _ngcontent-iej-c129 = "" hello = "" name = "Static component" _nghost-iej-c130 = "" ng-reflect-name = "Static component">
    <text _ngcontent-iej-c130 = "" text-anchor = "middle" x = "50%" y = "50%">
      Hello, Static component
    </text>
  </g>
</svg>

Проблема начинается, когда я пытаюсь создать экземпляр компонента динамически, используя ComponentFactoryResolver:

<svg>
  <ng-container #container></ng-container>
</svg>
import { Component, ViewChild, ViewContainerRef, ComponentFactoryResolver, OnInit } from '@angular/core';
import { HelloComponent } from './hello.component'

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent implements OnInit {
  @ViewChild('container', {read: ViewContainerRef, static: true}) container: ViewContainerRef;

  constructor(private componentFactoryResolver: ComponentFactoryResolver) {

  }

  ngOnInit() {
    // Instantiating HelloComponent dynamically
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(HelloComponent)
    const componentRef = this.container.createComponent(componentFactory);
    componentRef.instance.name = 'Dynamic component'
  }
}

Созданный DOM выглядит нормально, но по какой-то причине текст не виден на странице:

<svg _ngcontent-iej-c129 = "">
  <!---->
  <g hello = "" _nghost-iej-c130 = "">
    <text _ngcontent-iej-c130 = "" text-anchor = "middle" x = "50%" y = "50%">
      Hello, Dynamic component
    </text>
  </g>
</svg>

См. Воспроизведение на stackblitz

Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Введение в CSS
Введение в CSS
CSS является неотъемлемой частью трех основных составляющих front-end веб-разработки.
Как выровнять Div по центру?
Как выровнять Div по центру?
Чтобы выровнять элемент <div>по горизонтали и вертикали с помощью CSS, можно использовать комбинацию свойств и значений CSS. Вот несколько методов,...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Ангулярный шаблон для бронирования путешествий
Toor - Travel Booking Angular Template один из лучших Travel & Tour booking template in the world. 30+ валидированных HTML5 страниц, которые помогут...
9
0
2 256
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Я предполагаю, что здесь есть два вопроса:

  1. Как заставить это работать?
  2. Почему это происходит?

Ответ на первый вопрос заключается в использовании svg вместо g для группировки элементов.
В вашем конкретном примере это будет означать изменение селектора:

@Component({
  selector: 'svg[hello]',
  template: `<svg:text x = "50%" y = "50%" text-anchor = "middle">Hello, {{name}}</svg:text>`,
  styles: [`h1 { font-family: Lato; }`]
})

И app.component.html:

<svg>
  <svg hello name = "Static component"></svg>
</svg>

Теперь давайте перейдем ко второму вопросу. Почему это происходит?
Ваш селектор не содержит пространства имен svg. Для правильного отображения селектор должен быть svg:g[hello].
Но это невозможно из-за старой проблемы, которая существует со времен Angular 5.
Подробнее здесь и здесь.

Как упоминалось в комментарии это, основная проблема здесь заключается в том, что селектор Angular не может содержать пространство имен для создания элемента. Селектор svg:g[hello] будет преобразован в g[hello], в результате чего Angular будет использовать document.createElement вместо document.createElementNS для создания нового элемента.

Почему использование svg[hello] работает?
Потому что если мы используем селектор svg[hello], то он анализируется на <svg child> и для этого тега Angular равен предоставление пространства имен неявно:

'svg': new HtmlTagDefinition({implicitNamespacePrefix: 'svg'}),

Похоже, что это связано с открытым вопросом, см.

https://github.com/angular/angular/issues/20337

Спасибо, что указали на эту проблему. Я также недавно нашел еще один, который также кажется связанным: github.com/angular/angular/issues/10404

Denis Itskovich 30.06.2019 20:45

Кажется, это связано со старой проблемой Angular: №10404, а также с проблемой, упомянутой Виталием: #20337

В №10404ДинВейчжэ предлагает следующий обходной путь:

Вместо этого кода:

    const componentRef = this.container.createComponent(componentFactory);

Чтобы использовать это:

    const groupElement = document.createElementNS("http://www.w3.org/2000/svg", "g");
    const componentRef = componentFactory.create(injector, [], groupElement);
    this.container.insert(componentRef.hostView)

Это изменение решает проблему без замены <g> на <svg>. Принятый ответ, конечно же, также решает проблему, но у меня есть некоторые опасения по поводу потери производительности, которая может привести к такой замене.

Рабочий стек-блиц: здесь

Я оставил заметку в выпуске github.

yurzui 01.07.2019 06:28

Другие вопросы по теме