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

时间:2017-08-04 02:54:00

标签: angular angular-template

我正在尝试创建一个共享Angular组件库,以便在许多不同的Web项目中使用。除了共享组件库之外,我还尝试创建一个包含并显示所有这些组件的Web项目以及如何使用它们的代码示例。

从与类似问题相关的其他查询中可以看出,为了在<pre><code>标记的网页中显示HTML代码,这些HTML代码段的某些方面需要包含HTML编码字符(即>必须为&gt;<必须为&lt;)。需要从源代码手动进行这些更改可能非常繁琐且乏味。

我想找到一种方法能够读取给定组件的HTML模板,进行少量文本替换,然后将该值设置为要在视图中显示的属性。我已经看到了其他类似的SO问题,这些问题的答案不同且不够,例如this

如果我有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属性与元素相关联?

2 个答案:

答案 0 :(得分:3)

您可以尝试使用特殊功能来获取注释:

<强> 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

另见

答案 1 :(得分:0)

yurzui's answer适用于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);
  });
}