-1

I have this piece of code, and i need data to change dynamically. So i decided to use stream for that. The problem is that it seems to copy stream, and it doesn't work... Is there any way to pass it there? Or if it is possible, maybe there is some other solution? Any feedback would be appreciated.

warehouse-stack-component.ts

import { CdkDragEnd, CdkDragStart } from '@angular/cdk/drag-drop';
import { Component, Input, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { StackPalletsComponent } from '../modals/stack-pallets/stack-pallets.component';
import { MainServiceService } from '../services/main-service.service';
import { Observable, interval, BehaviorSubject, Subject } from 'rxjs';
import { Pallet } from '../models/pallet';

@Component({
  selector: 'app-warehouse-stack',
  templateUrl: './warehouse-stack.component.html',
  styleUrls: ['./warehouse-stack.component.css']
})
export class WarehouseStackComponent implements OnInit{

  @Input() warehouseStackID = null;
  @Input() name = null;
  @Input() position = null;
  @Input() set pallets(pallets:Pallet[]) {
    this.pallets$.next(pallets);
  }

  public pallets$:BehaviorSubject<Pallet[]> = new BehaviorSubject<Pallet[]>([]);
  
  public dialogRef: MatDialogRef<StackPalletsComponent>;


  constructor(public mainService:MainServiceService,public dialog:MatDialog) { }


  public dragging:boolean;


  
  ngOnInit(): void {

  }

  updatePosition(e: CdkDragEnd) {
    this.mainService.updateStackPosition(this.warehouseStackID, e.source.getFreeDragPosition().x, e.source.getFreeDragPosition().y)
  }

  public handleDragStart(event: CdkDragStart): void {
    this.dragging = true;
  }

  openDialog() {
    this.dialogRef = this.dialog.open(StackPalletsComponent, {
      data: {
        warehouseStackID: this.warehouseStackID,
        pallets$: this.pallets$
      } 
    });
  }

  handleClick(event: MouseEvent): void {
    if (this.dragging) {
      this.dragging = false;
      return
    } 
    this.openDialog();
  }
}

stack-pallets-component.ts

import { Component, Inject, OnInit } from '@angular/core';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { Pallet } from 'src/app/models/pallet';
import { MainServiceService } from 'src/app/services/main-service.service';
import { CreatePalletComponent } from '../create-pallet/create-pallet.component';

@Component({
  selector: 'app-stack-pallets',
  templateUrl: './stack-pallets.component.html',
  styleUrls: ['./stack-pallets.component.css']
})
export class StackPalletsComponent implements OnInit {

  constructor(public mainService:MainServiceService, public dialogRef:MatDialogRef<StackPalletsComponent>, public dialog:MatDialog,@Inject(MAT_DIALOG_DATA) public data: {warehouseStackID: number, pallets$:any}) { }

  ngOnInit(): void {
  }

  showCreationModal() {
    this.dialog.open(CreatePalletComponent, {
      data: {
        isStackPallet: true,
        warehouseStackID: this.data.warehouseStackID
      }
    })
  }
}

stack-pallets-component.html

<div class="pallets">
    <app-pallet *ngFor="let pallet of this.data.pallets$|async" [position]="{x:pallet.positionX, y:pallet.positionY}" [pallet]="pallet"></app-pallet>
    <div class="cubeRow">
        <div class="cube"></div>
        <div class="cube"></div>
    </div>
    <div class="cubeRow">
        <div class="cube"></div>
        <div class="cube"></div>
    </div>
    <div class="cubeRow">
        <div class="cube"></div>
        <div class="cube"></div>
    </div>
    <div class="buttons">
        <button class="btn-floating btn-large waves-effect waves-light red" style="margin-top: 5px;" (click)="showCreationModal()"><i class="material-icons">add</i></button>
    </div>
</div>
kian
  • 1,449
  • 2
  • 13
  • 21

1 Answers1

0

Streams should be accessed from a shared service, and not a component. The reason is generally cause you will run into a cyclical import. Instead of injecting the stream into the next component, just subscribe to the stream that lives within the service from that component. You can use pallets$.next(pallets) from any component that has the service injected in its constructor. Then just inject that service into whatever component you'd like, and subscribe to it.

For memory management, it's important to unsubscribe to your subscriptions. I like to use a helper for this (UntilDestroy); where it will keep subscriptions until destruction of the component.

Here's one way to do it.

warehouse-stack.component.ts

@UntilDestroy({ checkProperties: true })
@Component({
  selector: 'app-warehouse-stack',
  templateUrl: './warehouse-stack.component.html',
  styleUrls: ['./warehouse-stack.component.css']
})
export class WarehouseStackComponent implements OnInit, OnDestroy, OnChanges {
 @Input() set pallets(pallets:Pallet[]) {}
constructor(warehouseService: WarehouseService) {}

  ngOnChanges(changes: SimpleChanges) {
    if (changes.pallets) {
      this.warehouseService.pallets$.next(changes.pallets.currentValue)
    }
  }

 openDialog() {
    this.dialogRef = this.dialog.open(StackPalletsComponent, {
      data: {
        warehouseStackID: this.warehouseStackID
      } 
    });
  }
}

ngOnDestroy() {}

warehouse.service.ts

@Injectable({ providedIn: 'root' })
export class WarehouseService {
  pallets$: BehaviorSubject<Pallet[]> = new BehaviorSubject<Pallet[]>([])
}

stack-pallets.component.ts

@UntilDestroy({ checkProperties: true })
@Component({
  selector: 'app-stack-pallets',
  templateUrl: './stack-pallets.component.html',
  styleUrls: ['./stack-pallets.component.css']
})
export class StackPalletsComponent implements OnInit, OnDestroy {
 pallets: Pallet[] | []
 palletsSub$: Subscription | undefined
constructor(warehouseService: WarehouseService) { 
 this.palletsSub$ = warehouseSevice.pallets$.subscribe((pallets) => {
     this.pallets = pallets
   })
 }
}

ngOnDestroy() {}
francojay
  • 450
  • 3
  • 15
  • A more straight-forward way to pass the data would be to use `this.warehouseService.pallets$.next(pallets)` immediately when that data changes, instead of setting it on the @Input() and running ngOnChanges. – francojay Oct 16 '21 at 18:16