如何使用动态模板创建组件? (使用内联模板转换组件)

时间:2016-07-03 22:22:11

标签: angular angular2-components

我正在尝试创建一个组件,其中包含一个动态模板字符串,可以访问模板上的局部变量。我尝试过的每种方法最终都是“动态模板字符串”,而不是$compile'd(角度1术语,请原谅)。

以下是该组件的代码。在您看到评论的位置我想插入一个可以在item中引用ngFor的模板字符串。

@Component({
  selector: 'ion-alpha-scroll',
  template: `
    <ion-scroll [ngStyle]="calculateScrollHeight()" scrollX="false" scrollY="true">
      <ion-list class="ion-alpha-list-outer">
        <div *ngFor="let items of sortedItems | mapToIterable;">
          <ion-item-divider id="scroll-letter-{{items.key}}">{{items.key}}</ion-item-divider>
          <ion-item *ngFor="let item of items.value">
            <!-- how can I pass a dynamic template here that can reference item ? -->
          </ion-item>
        </div>
      </ion-list>
    </ion-scroll>
    <ul class="ion-alpha-sidebar" [ngStyle]="calculateDimensionsForSidebar()">
      <li (click)="alphaScrollGoToList(letter)" *ngFor="let letter of alphabet">
        <div class="letter">{{letter}}</div>
      </li>
    </ul>
  `,
  pipes: [MapToIterable]
})
export class IonAlphaScroll {
  @Input() listData: any;
  @Input() key: string;
  @Input() template: string;
  ....
}

理想情况下,我希望在ion-alpha-scroll中将item引用的ngFor引用为ng-content。我尝试在组件的必要ngFor中使用<ion-alpha-scroll *ngIf="breeds" [listData]="breeds" key="$t"> {{item.$t}} </ion-alpha-scroll> 并且没有运气 -

<ion-alpha-scroll *ngIf="breeds" [listData]="breeds" key="$t" [template]="alphaScrollTemplate">
</ion-alpha-scroll>

我试过的一件事就是这样 -

alphaScrollTemplate

{{item.$t}}只是一个包含... <ion-item *ngFor="let item of items.value"> {{template}} <!-- this just outputs {{item.$t}} literally --> </ion-item> ... 的字符串。然后我尝试在评论提出问题的组件中引用它,但它不起作用 -

rootRef.addListenerForSingleValueEvent()

我真的很好奇,如果角度2还可以。我刚刚找到this question which is very similar to mine。任何帮助或建议将不胜感激,谢谢。

1 个答案:

答案 0 :(得分:0)

这是我用于角度2.0.0-rc.3的解决方案

此解决方案创建了一个动态组件,并使用ViewContainerRefComponentFactory加载它。 Here is the ionic 2 component on GitHub

export function createComponentFactory(resolver: ComponentResolver, metadata: ComponentMetadata): Promise<ComponentFactory<any>> {
  const cmpClass = class DynamicComponent {};
  const decoratedCmp = Component(metadata)(cmpClass);
  return resolver.resolveComponent(decoratedCmp);
}

@Directive({
  selector: 'dynamic-html-outlet',
})
export class DynamicHTMLOutlet {
  @Input() src: string;
  @Input() ionAlphaScrollRef: any;
  @Input() currentPageClass: any;

  constructor(private vcRef: ViewContainerRef, private resolver: ComponentResolver) {
  }

  ngOnChanges() {
    if (!this.src) return;

    const metadata = new ComponentMetadata({
      selector: 'dynamic-html',
      template: this.src,
      pipes: [MapToIterable]
    });
    createComponentFactory(this.resolver, metadata).then(factory => {
      const injector = ReflectiveInjector.fromResolvedProviders([], this.vcRef.parentInjector);
      let component = this.vcRef.createComponent(factory, 0, injector, []);
      component.instance.ionAlphaScrollRef = this.ionAlphaScrollRef;
      component.instance.currentPageClass = this.currentPageClass;
    });
  }
}

@Component({
  selector: 'ion-alpha-scroll',
  template: `
    <dynamic-html-outlet
      [src]="alphaScrollTemplate"
      [ionAlphaScrollRef]="ionAlphaScrollRef"
      [currentPageClass]="currentPageClass">
    </dynamic-html-outlet>
  `,
  pipes: [MapToIterable],
  directives: [DynamicHTMLOutlet]
})
export class IonAlphaScroll {
  @Input() listData: any;
  @Input() key: string;
  @Input() itemTemplate: string;
  @Input() currentPageClass: any;
  @Input() triggerChange: any;
  private _scrollEle: HTMLElement;
  sortedItems: any = {};
  alphabet: any = [];
  alphaScrollTemplate: string;
  ionAlphaScrollRef = this;

  constructor(
    @Host() private _content: Content,
    private _elementRef: ElementRef,
    private vcRef: ViewContainerRef,
    private resolver: ComponentResolver
  ) {

  }

  ngOnInit() {
    this.alphaScrollTemplate = `
      <style>
        .ion-alpha-sidebar {
          position: fixed;
          right: 0;
          display: flex;
          flex-flow: column;
          z-index: 50000;
        }

        .ion-alpha-sidebar li {
          flex: 1 1 auto;
          list-style: none;
          width: 15px;
          text-align: center;
        }
      </style>

      <ion-scroll class="ion-alpha-scroll" [ngStyle]="ionAlphaScrollRef.calculateScrollDimensions()" scrollX="false" scrollY="true">
        <ion-list class="ion-alpha-list-outer">
          <div *ngFor="let items of ionAlphaScrollRef.sortedItems | mapToIterable; trackBy:ionAlphaScrollRef.trackBySortedItems">
            <ion-item-divider id="scroll-letter-{{items.key}}">{{items.key}}</ion-item-divider>
            <ion-item *ngFor="let item of items.value">
              ${this.itemTemplate}
            </ion-item>
          </div>
        </ion-list>
      </ion-scroll>
      <ul class="ion-alpha-sidebar" [ngStyle]="ionAlphaScrollRef.calculateDimensionsForSidebar()">
        <li *ngFor="let letter of ionAlphaScrollRef.alphabet" tappable (click)="ionAlphaScrollRef.alphaScrollGoToList(letter)">
          <a>{{letter}}</a>
        </li>
      </ul>
    `;

    setTimeout(() => {
      this._scrollEle = this._elementRef.nativeElement.querySelector('scroll-content');
      this.setupHammerHandlers();
    });
  }

  ngOnChanges(changes: {[propertyName: string]: SimpleChange}) {
    let tmp = {};
    for (let i = 0; i < this.listData.length; i++) {
      let letter = this.listData[i][this.key].toUpperCase().charAt(0);
      if (typeof tmp[letter] === 'undefined') {
        tmp[letter] = [];
      }
      tmp[letter].push(this.listData[i]);
    }

    this.alphabet = this.iterateAlphabet(tmp);
    this.sortedItems = tmp;
  }

  // ....
}