【问题标题】:Dynamically append component to div in Angular 5在Angular 5中动态地将组件附加到div
【发布时间】:2018-07-02 19:41:27
【问题描述】:

我有这个

https://angular-dynamic-component-append.stackblitz.io/

我设法动态附加一个元素,但它没有被编译。 看了很多教程this

但这并不是我真正需要的。他们经常使用标签符号来识别容器。

我需要将一个组件附加到任何可能具有 我的自定义指令就可以了。

我还需要使用指令的绑定值来控制附加元素上的 [hidden] 属性。

目标

  1. 覆盖现有组件的行为:
    • 添加属性以显示/隐藏
    • 添加类以自定义外观
  2. 减少 html 编码
    • 无需编写整个组件<my-comp></mycomp>
    • 不需要知道类
    • 类名更改时的自动行为
      1. 更改应用指令的元素
    • 最终目标是为容器元素添加一个类

预期来源

<div [myDirective]="myBoolean">
    <p>some content</p>
</div>

预期编译

<div [myDirective]="myBoolean" class="myDirectiveClass1">
    <p>some content</p>
     <someComponent [hidden]="myBoolean" class="myDirectiveClass2"></someComponent>
</div>

有没有办法做到这一点?

提前谢谢你

【问题讨论】:

  • 这是一个比您尝试过的更复杂和令人费解的过程。在您的情况下,它只是一个&lt;mat-card&gt;,所以您根本不需要这样做。但是,如果您需要在运行时动态创建一个元素而不提前注册它,那您就束手无策了。
  • 没有编译是什么意思?
  • @LaurentSchwitter:上面的垫子卡和我小提琴下面的垫子卡看起来一样吗?你问过自己为什么吗?
  • @AluanHaddad 对不起,但我不明白“hosed”我找不到翻译...你是什么意思? (我是意大利人:D)
  • @AluanHaddad 我设法完成了我需要的一切。我会尽快回答这个问题。

标签: angular typescript angular-directive angular5


【解决方案1】:

这很简单。我只是给你做了一个例子。

请阅读 loader 指令中的 cmets。

https://github.com/garapa/studying/tree/master/loader

编辑:

你的组件:

export class LoaderComponent {

  loading;

  constructor() { }

}

你的指令

export class LoaderDirective implements OnDestroy {

  private componentInstance: ComponentRef<LoaderComponent> = null;

  @Input()
  set appLoader(loading: boolean) {
    this.toggleLoader(loading);
  }

  constructor(
    private viewContainerRef: ViewContainerRef,
    private componentFactoryResolver: ComponentFactoryResolver
  ) { }

  toggleLoader(loading: boolean) {
    if (!this.componentInstance) {
      this.createLoaderComponent();
      this.makeComponentAChild();
    }

    this.componentInstance.instance.loading = loading;
  }

  private createLoaderComponent() {
    const componentFactory = this.componentFactoryResolver.resolveComponentFactory(LoaderComponent);
    this.componentInstance = this.viewContainerRef.createComponent(componentFactory);
  }

  private makeComponentAChild(){
    const loaderComponentElement = this.componentInstance.location.nativeElement;
    const sibling: HTMLElement = loaderComponentElement.previousSibling;
    sibling.insertBefore(loaderComponentElement, sibling.firstChild);
  }

  ngOnDestroy(): void {
    if (this.componentInstance) {
      this.componentInstance.destroy();
    }
  }

}

你的模块

@NgModule({
  ...
  entryComponents: [
    LoaderComponent
  ]
})

【讨论】:

  • 这很好,但我不敢相信我需要在每个父组件上创建一个“getElement”方法...
  • 我设法做了我需要的一切。我会尽快回答这个问题。我受到您的代码的启发。这里的问题是你需要知道你在“LoaderComponent”中。我想要一些更“普遍”的东西。让我知道您是否想在我回答时被标记。再见,谢谢你
  • 我刚刚提高了你的答案,因为它很有用;)
【解决方案2】:

这是我的工作方式

import {
  Renderer2,
  Directive,
  Input,
  ElementRef,
  OnChanges,
  ViewEncapsulation
} from "@angular/core";
import { MatSpinner } from "@angular/material";

@Directive({
  selector: "[myDirective]"
})
export class MyDirective {

  @Input()
  set myDirective(newValue: boolean) {
    console.info("myDirectiveBind", newValue);
    if (!!this._$matCard) {
      const method = newValue ? "removeClass" : "addClass";
      this.renderer[method](this._$matCard, "ng-hide");
    }
    this._myDirective = newValue;
  }

  private _myDirective: boolean;
  private _$matCard;

  constructor(private targetEl: ElementRef, private renderer: Renderer2) {
    this._$matCard = this.renderer.createElement('mat-card');
    const matCardInner = this.renderer.createText('Dynamic card!');
    this.renderer.addClass(this._$matCard, "mat-card");
    this.renderer.appendChild(this._$matCard, matCardInner);
    const container = this.targetEl.nativeElement;
    this.renderer.appendChild(container, this._$matCard);
  }


}

import {
  Component,
  ElementRef,
  AfterViewInit,
  ViewEncapsulation
} from '@angular/core';

@Component({
  selector: 'card-overview-example',
  templateUrl: 'card-overview-example.html',
  styleUrls: ['card-overview-example.css']
})
export class CardOverviewExample {
  
  hideMyDirective = !1;

  constructor(private _elementRef: ElementRef) { }

  getElementRef() {
    return this._elementRef;
  }

  ngAfterViewInit() {
    let element = this._elementRef.nativeElement;
    let parent = element.parentNode;
    element.parentNode.className += " pippo";

  }
}
.ng-hide {
  display: none;
}
<mat-card>Simple card</mat-card>
<div class="text-center">
  <button (click)="hideMyDirective = !hideMyDirective">
    Toggle show dynamic card
</button>
</div>
<br />
<span>hideMyDirective: {{hideMyDirective}}</span>
<hr />
<div class="myDiv" [myDirective]="hideMyDirective">
    <ul>
      <li>My content</li>
      </ul>
</div>

【讨论】:

  • 当 mat-card 返回时,这是否也适用于角材料 css ?因为当我尝试做同样的事情时,没有应用 css。在此先感谢...
  • 我必须用这段代码进行一次闪电战……老实说,我不记得了。我现在使用不同的方法来动态附加组件!
【解决方案3】:

在要插入组件的组件html文件中:

<div #target>
</div>

在要插入组件的组件ts文件中:

'Component_to_insert' -> 是要插入到另一个组件中的组件。

import { Component_to_insert } from 'path';
import { Component, ViewChild, ViewContainerRef, ComponentFactoryResolver, AfterViewInit } from '@angular/core';

@Component({
selector: 'component-name',
templateUrl: 'component.html',
styleUrls: ['component.scss'],
entryComponents: [Component_to_insert]
})

export class ManagetemplatesPanelComponent implements AfterViewInit {

    @ViewChild('target', { read: ViewContainerRef }) entry: ViewContainerRef;

    constructor(private resolver: ComponentFactoryResolver) { }

    ngAfterViewInit() {
     this.createComponent();
    }

    createComponent() {
      this.entry.clear();
      const factory = this.resolver.resolveComponentFactory(Component_to_insert);
      const componentRef = this.entry.createComponent(factory);
    }
}

【讨论】:

  • 这并不能完全回答。我已经遇到过这种方法。它有效。但我需要的是通用的,不需要知道要插入的组件。或多或少像 $compile 在 AngularJS 中工作的东西。附加 HTML 并对其进行编译以使其工作就足够了..
  • 这种方法在 Angular 6+ 版本中效果很好。这是一种显示组件的有效方法,无需将组件包含在具有角度路由的子组件中。
  • 它甚至适用于 Angular 4 和 5。我并不是说它不是一种有效的方法。但它不符合我的要求。
猜你喜欢
  • 1970-01-01
  • 2018-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-22
  • 2018-12-05
  • 2019-02-06
相关资源
最近更新 更多