【问题标题】:Angular 7 - Add drag and drop behaviour to dynamically created componentsAngular 7 - 向动态创建的组件添加拖放行为
【发布时间】:2019-09-30 04:11:01
【问题描述】:

这是我在 SO 上提出的上一个问题的延续: Add directives to component selector when it is declared - Angular 7

我在单击按钮时动态创建组件。组件以类似列表的方式在另一个下方显示。我想引入拖放行为,以便用户可以在创建组件后重新排列组件。

在上一个问题中,我尝试使用 Angular-Material,但意识到可能无法将它用于组件,因为在组件的选择器标签中添加“cdkDrag”指令的问题,以及 cdkDropList和 cdkDrag 可能需要在同一个模板中。

我在模板中有一个这样的 div:

<div cdkDropList style="margin: 20px" (cdkDropListDropped)="drop($event)">
    <div #container></div>
</div>

而且,我正在按如下方式创建自定义组件:

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

const childComponent = this.componentFactoryResolver.resolveComponentFactory(CustomComponent);
const component = this.container.createComponent(childComponent);

这很好用。是否有可能创建可拖动的动态创建的组件?

谢谢。

【问题讨论】:

    标签: javascript html angular drag-and-drop angular-components


    【解决方案1】:

    我通过使用 createComponent 方法动态生成组件并通过 ViewComponentRef 方法处理移动来解决这个问题:

    container.component.html

    <div cdkDropList (cdkDropListDropped)="drop($event)">
        <ng-container #cmpContainer></ng-container>
    </div>
    

    container.component.ts

    import {CdkDragDrop, moveItemInArray} from "@angular/cdk/drag-drop";
    import {DynamicComponent} from './dynamic.component.ts';
    
    @ViewChild('cmpContainer', {static: true, read: ViewContainerRef}) cmpContainer: ViewContainerRef;
    components: ComponentRef<DynamicComponent>[] = [];
    
    addComponent() {
        const factory = this.cfr.resolveComponentFactory(DynamicComponent);
        const component: ComponentRef<DynamicComponent> = this.cmpContainer.createComponent(factory);
        this.components.push(component);
    }
    
    drop(event: CdkDragDrop<DynamicComponent[]>) {
        this.cmpContainer.move(this.components[event.previousIndex].hostView, event.currentIndex);
        moveItemInArray(this.components, prevIndex, currentIndex);
    }
    

    dynamic.component.html

    <div cdkDrag>
        <div cdkDragHandle></div>
    </div>
    
    • 在这种情况下,您可以直接通过 components 数组访问组件实例。

    【讨论】:

      【解决方案2】:

      更新

      虽然这适用于单一类型的组件,但如果您需要使用不同的动态类型的组件,请阅读下面 Chaitanya Bangera 的评论!

      原评论

      应该使用这样的东西(CmpComponent 将是您要插入的组件):

        components: CmpComponent[];
      
      const childComponent = this.componentFactoryResolver.resolveComponentFactory(CustomComponent);
      this.components.push(childComponent);
      
      
      drop(event: CdkDragDrop<CmpComponent[]>) {
        moveItemInArray(this.components, event.previousIndex, event.currentIndex);
      }
      <div cdkDropList style="margin: 20px" (cdkDropListDropped)="drop($event)">
          <div cdkDrag *ngFor="let cmp of components">
              <app-cmp></app-cmp>
          </div>
      </div>

      【讨论】:

      • 谢谢,我看看,虽然我觉得我可能会遇到困难,因为用户可以动态创建几个可用组件之一。它就像用户可以单击和创建的小部件列表,因此模板中的 可能不起作用。编辑:不过,我会为单个组件尝试它,以检查该方法是否有效。
      • 我让它使用你的代码作为基础并使用 ng-container 而不是 *ngFor 的组件选择器来工作,它终于可以工作了。我会将您的答案标记为已接受,因为它适用于单个模板。我在下面添加了适用于不同组件的答案。
      • @ChaitanyaBangera 很高兴它成功了!我更新了我的答案,以便有相同问题的人更容易找到您的评论。
      • @ChaitanyaBangera 你能告诉我你是如何以这种方式处理组件参数的吗?我的意思是,您是使用动态生成值的输入/输出,还是有某种方法可以访问组件实例?和你有同样的问题,不知道如何处理组件参数。
      【解决方案3】:

      感谢 MauriceNino 的回复,终于可以使用了。我将 Maurice 的回答标记为已接受,因为他们的解决方案适用于单个组件。

      在让 Maurice 的解决方案适用于多个组件时,我遇到了一个名为 ng-container 的神奇概念!多么救命啊!!我的解决方案如下:

      components=[];
      
      const childComponent = this.componentFactoryResolver.resolveComponentFactory(CustomComponent);
      this.components.push(childComponent);
      
      
      drop(event: CdkDragDrop<CmpComponent[]>) {
        moveItemInArray(this.components, event.previousIndex, event.currentIndex);
      }
      

      现在是模板:

      <div cdkDropList class="example-list" style="margin: 20px" (cdkDropListDropped)="drop($event)">
          <ng-container *ngFor="let cmp of components">
              <ng-container *ngIf="cmp.componentType.name=='Component1'">
                  <app-Component1 cdkDrag></app-Component1>
              </ng-container>
              <ng-container *ngIf="cmp.componentType.name=='Component2'">
                  <app-Component2 cdkDrag></app-Component2>
              </ng-container>
              <ng-container *ngIf="cmp.componentType.name=='Component3'">
                  <app-Component3 cdkDrag></app-Component3>
              </ng-container>
      
          </ng-container>
      </div>
      

      终于,经过一周的搜索,终于成功了! 谢谢!

      【讨论】:

      • drop(event: CdkDragDrop) 你能告诉我CMpCOmponet[]是指的吗?
      【解决方案4】:

      您可以在每个ng-container 周围创建div 并为其设置cdkDrag 属性。

      【讨论】:

        【解决方案5】:

        要求是创建可拖动的可编辑行。用户可以添加/删除行。

        在这里将 cdk drag(带有 cdkdraglist 指令)指令应用于所有动态创建的元素。所以这个 cdk 拖放将起作用。但是 Angular 不允许运行时将指令添加到模板中的 am 元素。结论是,为了实现这个功能,我们必须支持网格框架(如 ag-grid)。

        【讨论】:

        猜你喜欢
        • 2019-04-13
        • 2018-08-28
        • 2019-05-26
        • 2018-04-30
        • 1970-01-01
        • 1970-01-01
        • 2020-12-24
        • 2019-05-31
        • 1970-01-01
        相关资源
        最近更新 更多