【问题标题】:Is there a way to get the HTML template for an Angular* component?有没有办法获取 Angular* 组件的 HTML 模板?
【发布时间】:2018-01-11 20:19:29
【问题描述】:

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

从与类似问题相关的其他查询中可以看出,为了在<pre><code> 标记中的网页中显示 HTML 代码,这些 HTML sn-ps 的某些方面需要具有 HTML 编码字符(即> 需要是>< 需要是<)。从源代码手动进行这些更改的需要非常繁琐和乏味。

我想找到一种方法来读取给定组件的 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;', '&amp;lt;').replace('&gt;', '&amp;gt;'); 将其作为我想做的伪代码,因为 Angular 组件对象没有模板属性,并且尽可能我看不到任何公开该组件模板的属性。

有没有办法利用 Angular 框架来获取组件的 HTML 模板? 同样,我不想替换带有表达式的内插内容,而是要关联的实际原始 HTML 模板使用装饰器的 template 属性或其 templateUrl 属性的元素?

【问题讨论】:

  • 你会使用AOT吗?
  • @yurzui 是的,我正计划这样做
  • 你有什么收获吗?
  • @Chris 很遗憾没有

标签: angular angular-template


【解决方案1】:

你可以尝试使用特殊函数来获取注解:

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

另见

【讨论】:

  • 啊,熟悉的东西)
  • @Maximus 是的,你是。这里没有什么新鲜事。这是基础级别
  • 这似乎不是 Angular 特定的,而是一种从任何装饰器获取任何信息的方法。有任何 Angular 特定的机制吗?
【解决方案2】:

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);
  });
}

【讨论】:

  • 5.2.9 版本的任何建议,因为这不再有效
  • 我很想回答这个问题!做起来肯定不会那么难吧?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-08-16
  • 2016-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多