使用ng-content创建Angular2组件的“克隆”

时间:2017-04-11 13:20:16

标签: javascript angular

我正在使用角度2创建一个3D“卡片翻转”。父级“卡片翻转”组件包含嵌套的“卡片翻转前”和“卡片翻转”组件。

<card-flip card-flip-id="demo-1" class="grid_col-6">
  <card-flip-front class="card">
    <div class="card__inner">
      Card Front
    </div>
  </card-flip-front>
  <card-flip-back class="card">
    <div class="card__inner">
      Card Back
    </div>
  </card-flip-back>
</card-flip>

我想创建一个卡片翻转前组件的“克隆”,内容投影和数据绑定。 “克隆”将用于动画,“原始”将保留在隐藏的原始位置。这样我就可以参考“返回到原始位置时”克隆“应该动画的位置(即使用户滚动窗口或调整窗口大小)。

我面临的主要挑战是我需要将ng-content标签内的内容投射到“克隆”中。问题是Angular将使用第一个ng-content标记进行内容投影,而其他未标记的ng-content标记将为空(我知道这是预期的行为)。

有人可能会问,“为什么不在DOM中创建一个愚蠢的,静态的元素副本?”。我想避免这种情况,以便注入数据的嵌套组件和数据绑定(从而修改元素的维度)将继续有效。

这是我到目前为止的工作,它通过ComponentFactory创建CardFlipFront组件的实例作为“克隆”,并简单地插入“原始”CardFlipFront的innerHTML。

import { 
  Component,
  ComponentFactory,
  ComponentFactoryResolver,
  ComponentRef,
  ContentChild,
  Inject,
  Input,
  OnInit,
  ViewChild,
  ViewContainerRef 
} from '@angular/core';
import { CardFlipFrontComponent } from './card-flip-front.component';
import { CardFlipBackComponent } from './card-flip-back.component';
import { CardFlipService } from './card-flip.service';

@Component({
  selector: 'card-flip',
  templateUrl: './card-flip.component.html',
  styleUrls: ['./card-flip.component.css'],
  entryComponents: [
    CardFlipFrontComponent
  ]
})
export class CardFlipComponent implements OnInit {
  @Input('card-flip-id') public id: string;
  @ContentChild(CardFlipFrontComponent) private front: CardFlipFrontComponent;
  @ContentChild(CardFlipBackComponent) private back: CardFlipBackComponent;
  @ViewChild('frontCloneContainer', { read: ViewContainerRef }) private frontCloneContainer: ViewContainerRef;
  private frontComponentRef: ComponentFactory<CardFlipFrontComponent>;
  private frontClone: ComponentRef<CardFlipFrontComponent>;

  constructor(
    @Inject(CardFlipService) private _cardFlipService: CardFlipService,
    private _componentFactoryResolver: ComponentFactoryResolver
  ) {
    this.frontComponentRef = this._componentFactoryResolver.resolveComponentFactory(CardFlipFrontComponent);
  }

  ngOnInit() {
    this._cardFlipService.register(this.id);
  }

  ngAfterViewInit() {
    // Create a card-flip-front component instance to serve as a "clone"
    this.frontClone = this.frontCloneContainer.createComponent(this.frontComponentRef);
    // Copy the innerHTML of the "original" into the "clone"
    this.frontClone.instance.el.nativeElement.innerHTML = this.front.el.nativeElement.innerHTML;
  }

  ngOnDestroy() {
    this.frontClone.destroy();
  }
}
<ng-content select="card-flip-front"></ng-content>
<ng-container #frontCloneContainer></ng-container>
<ng-content select="card-flip-back"></ng-content>

import {
  Component,
  ElementRef,
  HostBinding,
  Input,
  OnInit,
  Renderer
} from '@angular/core';

@Component({
  selector: 'card-flip-front',
  templateUrl: './card-flip-front.component.html',
  styleUrls: ['./card-flip-front.component.css']
})
export class CardFlipFrontComponent implements OnInit {
  constructor(private _el: ElementRef, private _renderer: Renderer) { }

  public get el(): ElementRef {
    return this._el;
  }

  public get renderer(): Renderer {
    return this._renderer;
  }

  ngOnInit() { }
}
<ng-content></ng-content>

更新

好的,所以在阅读了一些类似的挑战和github issue here后,我尝试了以下内容。

<ng-template #frontTemplate>
  <ng-content select="card-flip-front"></ng-content>
</ng-template>

<ng-container *ngIf="isOpen == true" #front1>
  <ng-container *ngTemplateOutlet="frontTemplate"></ng-container>
</ng-container>

<ng-container *ngIf="isOpen == false" #front2>
  <ng-container *ngTemplateOutlet="frontTemplate"></ng-container>
</ng-container>

<ng-content select="card-flip-back"></ng-content>

基本上,我们可以通过将ng-content置于模板中并使用两个带有ng-container语句的*ngIf标记来解决单个投影问题,该语句只会显示一个基于类属性isOpen的模板。

这并不能解决整个问题,因为在任何给定时间只会呈现一个容器。因此,我无法获得“原始”的当前位置,以确定在上述返回动画期间将“克隆”设置为动画的位置。

1 个答案:

答案 0 :(得分:0)

我认为您可以在<card-flip-content>模板中设置一个中间组件<card-flip>,该组件是重复的,并且会收到<ng-content>的{​​{1}}。

类似的东西:

<card-flip>

然后根据需要将数据绑定到#theOne和#theClone,并仅动画#theClone。 这种方式可以有@Input和@Output,从而让一个组件的动作由父级解释,以便作用于另一个组件。

那会有用吗?