Как использовать ng-шаблон внутри HTML в Angular - PullRequest
0 голосов
/ 20 января 2020

Может кто-нибудь помочь мне, как использовать ng-шаблон внутри внутреннего тега HTML,

Демонстрация Stackblitz: https://stackblitz.com/edit/angular-xgnnj4

Что я хотел сделать есть,

В компоненте HTML

<div [innerHTML]="htmlString"><div>

В компоненте TS

@ViewChild('dynamicComponent', { static: false, read: ViewContainerRef }) myRef: ViewContainerRef;

htmlString = `<ng-template #dynamicComponent></ng-template>`;

  ngAfterViewInit(): void {
    const factory = this.componentFactoryResolver.resolveComponentFactory(CommentComponent);
    const ref = this.myRef.createComponent(factory);
    ref.changeDetectorRef.detectChanges();
  }

Мне действительно нужно добиться этого, потому что я хотел получить HTML содержимого из API и используйте это значение для динамического отображения HTML содержимого

Поскольку я получаю сообщение об ошибке как Невозможно прочитать свойство 'createComponent' из неопределенного , но если я вывожу за пределы внутренний HTML работает отлично

Я прошел https://angular.io/guide/dynamic-component-loader но это не помогает моему scneario

Ответы [ 2 ]

6 голосов
/ 24 января 2020

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

Я бы порекомендовал вам:

  1. Либо загружать только чистый HTML содержимого поверх остальных API и извлеките все символы angular Speci c в ваше приложение angular и загрузите их условно, например: взгляните на этот stackBlitz , в нем ничего особенного не происходит, он просто использует CDK Portal, который по сути является просто абстракцией над методами resolveComponentFactory и createComponent и статически определенным templateRef который динамически загружается в viewContainerRef по нажатию кнопки.

enter image description here

Или создайте другое приложение JS или Angular и вставьте туда ChildViewComponent, и при этом Iframe загрузит содержимое на основе любого условия, если потребуется, Загрузите скомпилированную версию вашего шаблона аналогично тому, как angular lazy загружает свои модули, а некоторым - программно. Я не знаю, возможно ли это вообще или нет, это просто мысль в моей голове, но я не ожидаю, что это будет прямо вперед.
2 голосов
/ 20 января 2020

Вы можете использовать DomSanitizer для передачи любого HTML в шаблон:

import { DomSanitizer } from '@angular/platform-browser';

...

export class HelloComponent  {
  htmlString = this.sanitizer.bypassSecurityTrustHtml(`
    <ng-template>
      <div style="width: 10px; height: 10px; background: red;"></div>
    </ng-template>
  `);

  constructor(private sanitizer: DomSanitizer) {}
}

STACKBLITZ: https://stackblitz.com/edit/angular-t9roxg?file=src%2Fapp%2Fhello.component.ts

сохранить в помните, что таким образом HTML передается как обычный HTML без какой-либо обработки Angular

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...