В основном вы пытаетесь динамически загрузить компонент внутри динамически загружаемой строки. Любой шаблон, предоставленный как встроенный или отдельно в файле HTML, обрабатывается механизмом визуализации Angular. ( Ivy - это новый и оптимизированный механизм рендеринга, начиная с Angular 9, с Angular 4 до 8, механизм рендеринга по умолчанию называется Renderer2
, который является преемником Renderer
, который был установлен по умолчанию из Angular 2 до 4).
Это в основном то, что вы пытаетесь сделать в прикрепленном stackBlitz,
@ViewChild("dynamicComponent", { static: false, read: ViewContainerRef }) myRef: ViewContainerRef;
constructor(private componentFactoryResolver: ComponentFactoryResolver) {
//Simulate the network call
setTimeout(() => {
this.htmlChildString = `<ng-template #dynamicComponent></ng-template>`;
}, 2000);
}
ngAfterViewInit(): void {
const factory = this.componentFactoryResolver.resolveComponentFactory(
ChildViewComponent
);
const ref = this.myRef.createComponent(factory);
ref.changeDetectorRef.detectChanges();
}
Первое, что здесь не так, очевидно, что ngAfterViewInit
будет Вызван перед вашим setTimeout
методом, записанным в constructor
из-за установленного вами тайм-аута 2000ms
, поэтому myRef
всегда будет неопределенным, потому что он еще не создан, следующая проблема, касающаяся вашего шаблона,
<div [innerHTML]="htmlChildString"></div>
innerHTML
не должен содержать символы angular, он может содержать только простые HTML, которые могут быть непосредственно встроены в DOM, поскольку они не будут обрабатываться механизмом рендеринга Angular Это именно то, на что @Andriy уже указал.
Теперь перейдем к причине, по которой вы пытаетесь сделать невозможным. Предположим, вы определили компонент с именем AComponent
, имеющий t emplate:
<span>I am {{name}}</span>
angular компилятор, при прохождении каждого из (статически определенных) компонентов и шаблонов, прикрепленных к этому компоненту, создает фабрику для каждого компонента. Когда angular компилятор проходит по вышеуказанному шаблону, он генерирует фабрику, подобную следующей,
function View_AComponent_0(l) {
return jit_viewDef1(0,
[
jit_elementDef2(0,null,null,1,'span',...),
jit_textDef3(null,['I am ',...])
],
null,
function(_ck,_v) {
var _co = _v.component;
var currVal_0 = _co.name;
_ck(_v,1,0,currVal_0);
, замечая следующую строку?
jit_elementDef2(0,null,null,1,'span',...),
Что Angular делает, использует JS для create elements, Angular использует эту фабрику для создания экземпляра View Definition, который, в свою очередь, используется для создания компонента View. Под капотом Angular представляет приложение в виде дерева взглядов. Для каждого типа компонента существует только один экземпляр определения представления, который действует как шаблон для всех представлений. Но для каждого экземпляра компонента Angular создает отдельное представление.
Выше фабрики описывается структура представления компонента и используется при создании экземпляра компонента. jit_viewDef1
является ссылкой на функцию viewDef
, которая создает определение представления. Определение представления получает узлы определения представления в качестве параметров, которые напоминают структуру html, но также содержат много angular специфицируемых c деталей.
эта фабрика - то, что возвращается из метода resolveComponentFactory
, так как вы статически объявил ChildViewComponent
в вашем модуле, а также пометил его как entryComponent
, поэтому angular решил предварительно сгенерировать свою фабрику, даже если он знает, что она в настоящее время не используется маршрутизатором или в шаблоне другого компонента.
Точно так же ViewContainerRef
- это ссылка на контейнер представления (ng-template
или ng-container
), который должен быть статически определен непосредственно в шаблоне или который может быть обнаружен с помощью комбинации ngSwitch
или Директивы ngIf
, но в любом случае движок компилятора / рендеринга должен знать о наличии этого контейнера перед началом работы при компиляции.
В вашем случае, когда вы динамически загружаете эти angular символы через API, angular знает только, что это простая строка, и не знает заранее, что это так мне вещь, которая может содержать представление во время выполнения. Если вы когда-либо просматривали сборку, сгенерированную Angular, она содержит только файлы JS и не содержит шаблонов HTML или CSS, соответствующих вашим компонентам. Каждый шаблон, содержащийся в этих HTML файлах, преобразуется в фабричные методы, которые в конечном итоге используются во время выполнения.
Я бы порекомендовал вам:
- Либо загружать только чистый HTML содержимого поверх остальных API и извлеките все символы angular Speci c в ваше приложение angular и загрузите их условно, например: взгляните на этот stackBlitz , в нем ничего особенного не происходит, он просто использует CDK Portal, который по сути является просто абстракцией над методами
resolveComponentFactory
и createComponent
и статически определенным templateRef
который динамически загружается в viewContainerRef
по нажатию кнопки.
Или создайте другое приложение JS или Angular и вставьте туда
ChildViewComponent
, и при этом Iframe загрузит содержимое на основе любого условия, если потребуется, Загрузите скомпилированную версию вашего шаблона аналогично тому, как angular lazy загружает свои модули, а некоторым - программно. Я не знаю, возможно ли это вообще или нет, это просто мысль в моей голове, но я не ожидаю, что это будет прямо вперед.