То, что вы в основном пытаетесь сделать, - это динамически загрузить компонент внутри динамически загружаемой строки. Любой шаблон, предоставленный встроенным или отдельно в файле 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
не должен содержать никаких угловых символов, он может содержать только простой HTML, который может быть непосредственно встроен в DOM, поскольку они не будут обрабатываться механизмом рендеринга Angular, на что @Andriy уже указал.
Теперь переходим к причине, по которой то, что вы пытаетесь сделать, невозможно. Предположим, вы определяете компонент с именем AComponent
, имеющий шаблон:
<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 для создания элементов, Angular использует эту фабрику для создания экземпляра определения представления, которое, в свою очередь, используется для создания представления компонента. Под капотом Angular представляет приложение в виде дерева представлений. Для каждого типа компонента существует только один экземпляр определения представления, который действует как шаблон для всех представлений. Но для каждого экземпляра компонента Angular создает отдельное представление.
Вышеуказанная фабрика описывает структуру представления компонента и используется при создании экземпляра компонента. jit_viewDef1
- это ссылка на функцию viewDef
, которая создает определение представления. Определение представления получает узлы определения представления в качестве параметров, которые напоминают структуру html, но также содержат много специфических деталей angular.
эта фабрика - это то, что возвращается из метода resolveComponentFactory
, поскольку вы статически объявили ChildViewComponent
в своем модуле, а также пометили его как entryComponent
, поэтому angular решил предварительно сгенерировать свою фабрику, даже если он знает, что в настоящее время он не используется маршрутизатором или в шаблон другого компонента.
Аналогичным образом ViewContainerRef
- это ссылка на контейнер представления (ng-template
или ng-container
), который должен быть либо статически определен непосредственно в шаблоне, либо который должен быть обнаружен с помощью комбинации директив ngSwitch
или ngIf
, но в любом случае механизм компилятора / рендеринга имеет заранее знать о наличии этого контейнера во время компиляции.
В вашем случае, когда вы загружаете эти угловые символы динамически через API, angular знает только, что это простая строка, и не знает заранее, что это что-то, что может содержать представление во время выполнения. Если вы когда-нибудь смотрели на сборку, сгенерированную Angular, она содержит только файлы JS и не содержит шаблонов HTML или файлов стилей CSS, соответствующих вашим компонентам. Каждый шаблон, содержащийся в этих HTML-файлах, преобразуется в фабричные методы, которые в конечном итоге используются во время выполнения.
Я бы порекомендовал вам,
- Либо загрузите только чистый HTML-контент поверх остального API, либо вытащите все символы angular в свое приложение angular и загрузите их условно, например взгляните на этот stackBlitz В нем ничего нестандартного не происходит, он просто использует портал CDK, который в основном представляет собой просто абстракцию над методами
resolveComponentFactory
и createComponent
и статически определенный templateRef
, который динамически загружается в viewContainerRef
одним нажатием кнопки.
![введите описание изображения здесь](https://i.stack.imgur.com/dCCCs.gif)
- Или создайте другое приложение JS или Angular и вставьте туда
ChildViewComponent
, и iframe загрузит контент на основе любого условия, если это необходимо,
- Загрузите скомпилированную версию вашего шаблона аналогично тому, как angular lazy загружает свои модули, и каким-то образом загружает его в розетку маршрутизатора программно. Я не знаю, возможно ли это вообще или нет, это просто идея в моей голове, но я не ожидаю, что это будет прямолинейно.
person
Saif
schedule
24.01.2020