Удалить селектор динамических компонентов из DOM

#angular #angular-directive #angular-components

Вопрос:

Привет, Стэковер, Цветы,

Я пытаюсь создавать динамические компоненты, используя containerRef.createComponent(this.cfr.resolveComponentFactory(ChildComponent),index)

Это создаст ненужный html-тег селектора дочерних компонентов в DOM. Я создал директиву для перемещения элементов (включая комментарии для ngContainer или других ссылок на местоположение) внутри дочернего компонента за пределами селектора, а затем удалить тег селектора со следующей логикой.

         const parentElement = this.elementRef.nativeElement;
        while (parentElement.lastChild) {
            const element = parentElement.lastChild;
            parentElement.removeChild(element);
            parentElement.parentNode.insertBefore(element, parentElement.nextSibling);
        }
        parentElement.parentNode.removeChild(parentElement);
 

Приведенная выше логика работает нормально, однако проблема в том, что эта логика занимает более 5 секунд при загрузке страницы, так как я создаю сотни дочерних компонентов (причина удаления селектора в первую очередь). Существует ли более быстрый способ создания динамических компонентов без их селектора в DOM?

P. S: Я попытался удалить селектор из декоратора дочернего компонента @Component, который вместо этого создает тег ng-компонента.

Редактировать:

  1. В исходном коде angular для метода create в ComponentFactory я вижу const hostRNode , что это новый тег для селектора компонентов, я пытаюсь исправить ComponentFactory.create во время выполнения, чтобы удалить логику, связанную с hostRNode . Если я добьюсь успеха, я добавлю решение здесь.
   create(
      injector: Injector, projectableNodes?: any[][]|undefined, rootSelectorOrNode?: any,
      ngModule?: viewEngine_NgModuleRef<any>|undefined): viewEngine_ComponentRef<T> {
    ngModule = ngModule || this.ngModule;

    const rootViewInjector =
        ngModule ? createChainedInjector(injector, ngModule.injector) : injector;

    const rendererFactory =
        rootViewInjector.get(RendererFactory2, domRendererFactory3) as RendererFactory3;
    const sanitizer = rootViewInjector.get(Sanitizer, null);

    const hostRenderer = rendererFactory.createRenderer(null, this.componentDef);
    // Determine a tag name used for creating host elements when this component is created
    // dynamically. Default to 'div' if this component did not specify any tag name in its selector.
    const elementName = this.componentDef.selectors[0][0] as string || 'div';
    const hostRNode = rootSelectorOrNode ?
        locateHostElement(hostRenderer, rootSelectorOrNode, this.componentDef.encapsulation) :
        createElementNode(
            rendererFactory.createRenderer(null, this.componentDef), elementName,
            getNamespace(elementName));

    const rootFlags = this.componentDef.onPush ? LViewFlags.Dirty | LViewFlags.IsRoot :
                                                 LViewFlags.CheckAlways | LViewFlags.IsRoot;
    const rootContext = createRootContext();

    // Create the root view. Uses empty TView and ContentTemplate.
    const rootTView = createTView(TViewType.Root, null, null, 1, 0, null, null, null, null, null);
    const rootLView = createLView(
        null, rootTView, rootContext, rootFlags, null, null, rendererFactory, hostRenderer,
        sanitizer, rootViewInjector);

    // rootView is the parent when bootstrapping
    // TODO(misko): it looks like we are entering view here but we don't really need to as
    // `renderView` does that. However as the code is written it is needed because
    // `createRootComponentView` and `createRootComponent` both read global state. Fixing those
    // issues would allow us to drop this.
    enterView(rootLView);

    let component: T;
    let tElementNode: TElementNode;

    try {
      const componentView = createRootComponentView(
          hostRNode, this.componentDef, rootLView, rendererFactory, hostRenderer);
      if (hostRNode) {
        if (rootSelectorOrNode) {
          setUpAttributes(hostRenderer, hostRNode, ['ng-version', VERSION.full]);
        } else {
          // If host element is created as a part of this function call (i.e. `rootSelectorOrNode`
          // is not defined), also apply attributes and classes extracted from component selector.
          // Extract attributes and classes from the first selector only to match VE behavior.
          const {attrs, classes} =
              extractAttrsAndClassesFromSelector(this.componentDef.selectors[0]);
          if (attrs) {
            setUpAttributes(hostRenderer, hostRNode, attrs);
          }
          if (classes amp;amp; classes.length > 0) {
            writeDirectClass(hostRenderer, hostRNode, classes.join(' '));
          }
        }
      }

      tElementNode = getTNode(rootTView, HEADER_OFFSET) as TElementNode;

      if (projectableNodes !== undefined) {
        const projection: (TNode|RNode[]|null)[] = tElementNode.projection = [];
        for (let i = 0; i < this.ngContentSelectors.length; i  ) {
          const nodesforSlot = projectableNodes[i];
          // Projectable nodes can be passed as array of arrays or an array of iterables (ngUpgrade
          // case). Here we do normalize passed data structure to be an array of arrays to avoid
          // complex checks down the line.
          // We also normalize the length of the passed in projectable nodes (to match the number of
          // <ng-container> slots defined by a component).
          projection.push(nodesforSlot != null ? Array.from(nodesforSlot) : null);
        }
      }

      // TODO: should LifecycleHooksFeature and other host features be generated by the compiler and
      // executed here?
      // Angular 5 reference: https://stackblitz.com/edit/lifecycle-hooks-vcref
      component = createRootComponent(
          componentView, this.componentDef, rootLView, rootContext, [LifecycleHooksFeature]);

      renderView(rootTView, rootLView, null);
    } finally {
      leaveView();
    }

    return new ComponentRef(
        this.componentType, component, createElementRef(tElementNode, rootLView), rootLView,
        tElementNode);
  }
 

Заранее спасибо.

Комментарии:

1. я собираюсь посоветовать не делать этого. вы не можете создавать компоненты без их тега селектора, так работает angular. удаление тегов бессмысленно, как только они уже прикреплены к DOM, так как это просто дополнительная работа, перетасовывающая теги без причины. если у вас проблемы с производительностью, вам нужно отрисовывать меньше компонентов. IE реализует своего рода виртуальную прокрутку, которая отображает компоненты только тогда, когда их необходимо отобразить

2. @bryan60 спасибо за ваш быстрый ответ. Я понимаю, почему вы не советуете этого делать, однако я создаю пользовательские шаблоны компонентов на основе метаданных, и количество элементов DOM, созданных из-за возникшей у меня проблемы, препятствует производительности страницы. Итак, это проблема, которую я должен решить. Спасибо.

3. теги селектора не являются причиной вашей проблемы, и их удаление не решит ее. проблема в том, что вы визуализируете то, что вам не нужно, до того, как вам придется это сделать. вам нужно исправить свое приложение, чтобы вы выполняли рендеринг только тогда, когда это необходимо.