Есть ли способ получить HTML-шаблон для компонента Angular*?

Я пытаюсь создать библиотеку общих компонентов Angular для использования в различных веб-проектах. Наряду с общей библиотекой компонентов я пытаюсь создать веб-проект, который содержит и отображает все эти компоненты и примеры кода, как их использовать.

Как видно из других запросов, связанных с аналогичным вопросом, для отображения HTML-кода на веб-странице в тегах <pre> или <code> некоторые аспекты этих фрагментов HTML должны иметь закодированные символы HTML (т. Е. > должен быть &gt;, < должен быть быть &lt;). Необходимость вносить эти изменения вручную из исходного кода может быть весьма обременительной и утомительной.

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

Если у меня есть foo.component.ts следующим образом:

import { Component } from '@angular/core';

@Component({
    selector: 'foo',
    template: `
        <div class="foo">
            <div class="content">
                <ng-content select="[top-content]"></ng-content>
            </div>
            <div class="extra content">
                <ng-content select="[bottom-content]"></ng-content>
            </div>
        </div>
    `
})
export class FooComponent {}

Я хочу создать display-foo.component.ts с чем-то следующим:

import { Component } from '@angular/core';
import { FooComponent } from './foo.component';

@Component({
    selector: 'display-foo',
    template: `
        <pre class="example-code">
          <code>
            {{encodedExampleHtml}}
          </code>
        </pre>
        <foo>
          <div top-content>TOP</div>
          <div bottom-content>BOTTOM</div>
        </foo>
    `
})
export class DisplayFooComponent {
  encodedExampleHtml: string;

  constructor() {
    this.encodedExampleHtml = new FooComponent().template.replace('<', '&lt;').replace('>', '&gt;');
  }
}

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

Часть DisplayFooComponent, которую я не могу понять, как работать, - это эта строка this.encodedExampleHtml = new FooComponent().template.replace('<', '&lt;').replace('>', '&gt;'); Возьмите это как псевдокод для того, что я хотел бы сделать, потому что объект компонента Angular не имеет свойства шаблона и, насколько я могу видеть, нет свойства, которое выставляет шаблон для этого компонента.

Есть ли способ использовать фреймворк Angular для получения шаблона HTML для компонента? Опять же, я хочу заменить не интерполированное содержимое с выражениями, а реальный необработанный HTML-шаблон, связанный с элементом. либо по свойству декоратора template, либо по его свойству templateUrl?


person peinearydevelopment    schedule 04.08.2017    source источник
comment
Собираетесь ли вы использовать АОТ?   -  person yurzui    schedule 04.08.2017
comment
@yurzui Да, я планировал это   -  person peinearydevelopment    schedule 04.08.2017
comment
Вы с этим чего-нибудь добились?   -  person Chris    schedule 18.07.2019
comment
@Крис, к сожалению, нет   -  person peinearydevelopment    schedule 18.07.2019


Ответы (2)


Вы можете попробовать использовать специальную функцию для получения аннотаций:

annotation.ts

declare let Reflect: any;
export function getAnnotation(typeOrFunc: Type<any>): any[]|null {
  // Prefer the direct API.
  if ((<any>typeOrFunc).annotations) {
    let annotations = (<any>typeOrFunc).annotations;
    if (typeof annotations === 'function' && annotations.annotations) {
      annotations = annotations.annotations;
    }
    return annotations[0];
  }

  // API of tsickle for lowering decorators to properties on the class.
  if ((<any>typeOrFunc).decorators) {
    return convertTsickleDecoratorIntoMetadata((<any>typeOrFunc).decorators)[0];
  }

  // API for metadata created by invoking the decorators.
  if (Reflect && Reflect.getOwnMetadata) {
    return Reflect.getOwnMetadata('annotations', typeOrFunc)[0];
  }
  return null;
}

function convertTsickleDecoratorIntoMetadata(decoratorInvocations: any[]): any[] {
  if (!decoratorInvocations) {
    return [];
  }
  return decoratorInvocations.map(decoratorInvocation => {
    const decoratorType = decoratorInvocation.type;
    const annotationCls = decoratorType.annotationCls;
    const annotationArgs = decoratorInvocation.args ? decoratorInvocation.args : [];
    return new annotationCls(...annotationArgs);
  });
}

а потом

this.encodedExampleHtml = getAnnotation(FooComponent).template;

Пример планкера

Смотрите также

person yurzui    schedule 04.08.2017
comment
о, знакомая вещь) - person Max Koretskyi; 04.08.2017
comment
@ Максимус, да, ты. Здесь нет ничего нового. это базовый уровень - person yurzui; 04.08.2017
comment
Это не похоже на специфичность Angular, а скорее способ получить любую информацию от любого декоратора. Есть ли какой-то конкретный механизм Angular? - person peinearydevelopment; 07.08.2017

ответ yurzui отлично работает для Angular 4.4.2, но не для 5.0.2. Изменился механизм аннотации: вместо Reflect.defineMetadata() используется свойство для хранения метаданных. Поскольку это не задокументировано, его можно найти только в коде:

const /** @type {?} */ TypeDecorator = (function TypeDecorator(cls) {
    const /** @type {?} */ annotations = Reflect.getOwnMetadata('annotations', cls) || [];
    annotations.push(annotationInstance);
    Reflect.defineMetadata('annotations', annotations, cls);
    return cls;
});

в Angular 4.4.2 (@angular/core/@angular/core.js, l.356) до

const ANNOTATIONS = '__annotations__';
const /** @type {?} */ TypeDecorator = /** @type {?} */ (function TypeDecorator(cls) {
    // Use of Object.defineProperty is important since it creates non-enumerable property which
    // prevents the property is copied during subclassing.
    const /** @type {?} */ annotations = cls.hasOwnProperty(ANNOTATIONS) ?
        (/** @type {?} */ (cls))[ANNOTATIONS] :
        Object.defineProperty(cls, ANNOTATIONS, { value: [] })[ANNOTATIONS];
    annotations.push(annotationInstance);
    return cls;
});

в Angular 5.0.2 (@angular/core/esm2015/core.js, л.96).

Обновлен код для доступа к метаданным, например. шаблон:

import 'reflect-metadata';

export function getAnnotation(typeOrFunc: Type<any>): any[]|null {
  // Prefer the direct API.
  if ((<any>typeOrFunc)['__annotations__']) {
      return (<any>typeOrFunc)['__annotations__'][0];
  }

  // API of tsickle for lowering decorators to properties on the class.
  if ((<any>typeOrFunc).decorators) {
    return convertTsickleDecoratorIntoMetadata((<any>typeOrFunc).decorators)[0];
  }

  // API for metadata created by invoking the decorators.
  if (Reflect && Reflect.getOwnMetadata) {
    return Reflect.getOwnMetadata('annotations', typeOrFunc)[0];
  }
  return null;
}

function convertTsickleDecoratorIntoMetadata(decoratorInvocations: any[]): any[] {
  if (!decoratorInvocations) {
    return [];
  }
  return decoratorInvocations.map(decoratorInvocation => {
    const decoratorType = decoratorInvocation.type;
    const annotationCls = decoratorType.annotationCls;
    const annotationArgs = decoratorInvocation.args ? decoratorInvocation.args : [];
    return new annotationCls(...annotationArgs);
  });
}
person Florian Gössele    schedule 23.11.2017
comment
Любые предложения для версии 5.2.9, так как она больше не работает - person tic; 11.04.2018
comment
Я хотел бы получить ответ на это! Наверняка это не так сложно сделать? - person Chris; 17.07.2019