有没有办法为 Angular* 组件获取 HTML 模板?

Is there a way to get the HTML template for an Angular* component?

我正在尝试创建一个共享 Angular 组件库,以在多个不同的 Web 项目中使用。与共享组件库一起,我正在尝试创建一个 Web 项目,其中包含并显示所有这些组件以及关于如何使用它们的代码示例。

从与类似问题相关的其他查询可以看出,为了在 <pre><code> 标签中显示网页中的 HTML 代码,这些 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;');
  }
}

本质上,这会将模板放在 UI 中,供下一个开发人员查看和理解如何使用以及该类型组件的实际渲染元素以及显示组件的外观就像在实际应用中使用时一样。

DisplayFooComponent 中我不知道如何工作的部分是这一行 this.encodedExampleHtml = new FooComponent().template.replace('<', '&lt;').replace('>', '&gt;'); 将其作为我想做的事情的伪代码,因为 Angular组件对象没有模板 属性,据我所知,没有 属性 公开该组件的模板。

有没有办法利用 Angular 框架来获取组件的 HTML 模板? 同样,我不想要插值内容替换了表达式,但实际原始 HTML 模板与装饰器的 template 属性 或其 templateUrl 属性?[=27 相关联的元素=]

您可以尝试使用特殊函数获取注释:

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;

Plunker Example

另见

适用于 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, l.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);
  });
}