Search code examples
angularrenderngforangular-cdk

How to render single item from a loop based on some condition at a another area of a screen in angular?


any one know how to render this kind of scenario :

enter image description here

I know dragable library provide an option for disable drag. But it is not better solution while item is disabled to drag but another item can change position of non-dragable item while draging.

So is there any way to render item at different place some kind of hack or stuff or feature through which, this kind of scenario can be accomplish ?

please guide me on the right path to render this kind of scenario in angular


Solution

  • Splitting a backing array into two observables

    Since we are concerned with one source but need to output a subset of items in two different locations we will adopt an approach of keeping an internal BehaviorSubject which we can derive multiple observables from, in order to create our filtered subsets.

    dependencies

    • Angular Material Components source

    to add angular material run ng add @angular/material in your angular project root. When prompted to add animations select Yes

    Modify your module: default is app.module

    Before you can utilize the CDK drag and drop directive, you must add the DragDropModule to your imports.

    @NgModule({
      declarations: [
       ...
      ],
      imports: [
        ...
        DragDropModule
      ],
      providers: [],
      bootstrap: [AppComponent]
    })
    export class AppModule { }
    

    Create a component

    controller

    
    @Component({
      selector: 'drag-drop-demo',
      templateUrl: './drag-drop-demo.component.html',
      styleUrls: ['./drag-drop-demo.component.scss']
    })
    
    export class DragDropDemoComponent implements OnInit, OnChanges, OnDestroy {
      // utilize behaviorSubject so we always get the latest input in our observables regardless of lifecycle timing.
      private internal!: BehaviorSubject<number[]>;
      @Input() listItems!: number[];
      conditionAppliesList!: number[];
      conditionDoesNotApplyList!: number[];
      subscriptions: Subscription;
    
    
      constructor() {
        this.internal = new BehaviorSubject<number[]>([]);
        this.subscriptions = new Subscription();
      }
    
      ngOnInit(): void {
        // setup two distinct observables that apply some filter condition to our list.
        this.subscriptions.add(
          this.internal.asObservable()
            .pipe(map(items => items.filter(number => number % 2 == 0))).subscribe(numbers => {
            this.conditionAppliesList = numbers;
          })
        );
        this.subscriptions.add(
          this.internal.asObservable()
            .pipe(map(items => items.filter(number => number % 2 != 0))).subscribe(numbers => {
            this.conditionDoesNotApplyList = numbers;
          })
        );
      }
    
      ngOnChanges(changes: SimpleChanges): void {
        // when listItems are detected in our input, emit the values in the internal BehaviorSubject
        if ('listItems' in changes) {
          // emit next value
          this.internal.next(this.listItems);
        }
      }
    
      ngOnDestroy() {
        // cleanup the subscriptions.
        this.subscriptions.unsubscribe();
      }
    
      // cdkHandler.
      drop(event: CdkDragDrop<string[]>) {
        moveItemInArray(this.conditionAppliesList, event.previousIndex, event.currentIndex);
      }
    }
    

    template

    <h2>Drag and drop</h2>
    <div class="list-outlet">
      <h3>Draggable</h3>
      <div class="list-container" cdkDropList (cdkDropListDropped)="drop($event)">
        <div class="list-item" *ngFor="let num of conditionAppliesList" cdkDrag>
          {{num}}
        </div>
      </div>
      <h3>Non-Draggable</h3>
      <div class="list-container">
        <div class="list-item" *ngFor="let num of conditionDoesNotApplyList">
          {{num}}
        </div>
      </div>
    </div>
    

    See demonstration on StackBlitz