#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-компонента.
Редактировать:
- В исходном коде 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. теги селектора не являются причиной вашей проблемы, и их удаление не решит ее. проблема в том, что вы визуализируете то, что вам не нужно, до того, как вам придется это сделать. вам нужно исправить свое приложение, чтобы вы выполняли рендеринг только тогда, когда это необходимо.