问题
I am working on a kanban style drag&drop system with ng2-dragula. And I have an issue, and I think it's because every time you drop an item to a new place it sends the data to the server and redo the whole list of items that you can drag around. And if you do it fast enough you can break the drag&drop cycle. Is there a way to limit the intervall you can make an API call? Similar to RxJS debounceTime, but since the list is almost always changing I cannot pipe a filter to it.
Basic constructor and drag event subscribtion:
constructor(private dragulaService: DragulaService, ) {
this.makeUndragabbles();
this.subs.add(this.dragulaService.dropModel('cardList')
.subscribe(({ item, target, source }) => {
const dragObj: DragObject = {
item: item,
stageId: target['id'],
name: this.pipelineConfig.name
};
this.modifyStage(dragObj);
const drake = this.dragulaService.find('cardList').drake; //debug variable
const sourceModel = drake.models[drake.containers.indexOf(source)]; //debug variable
}));
}
First it was for making non draggable items, not it's a bit more:
private makeUndragabbles() {
if (!this.dragulaService.find('cardList')) {
this.dragulaService.createGroup('cardList',
{
copy: false,
revertOnSpill: true,
moves: (el, container, handle, sibling) => {
return !el.classList.contains('nodrag');
},
isContainer: (el) => {
return el.classList.contains('stage');
}
});
}
}
Dragged item emitting function:
private modifyStage(draggedItem) {
this.drag.emit(draggedItem);
}
Rest call function:
private saveDraggedItem(pipelineType: string, statusChangeDTO: StatusChangeDTO) {
if (pipelineType === 'dealStages') {
this.dealService.savePipelineDealStageUsingPOST(statusChangeDTO).pipe(
debounceTime(1000),
)
.subscribe(res => { }
, (err) => this.error.emit(err)
, () => { this.getAllDealsForPipeline(); });
}
}
Emitted data cacher:
drag(draggedItem: DragObject) {
if (draggedItem.item) {
const statusChange: StatusChangeDTO = {
id: draggedItem.item.id,
newStatusId: +draggedItem.stageId
};
this.saveDraggedItem(draggedItem.name, statusChange);
}
}
回答1:
Here's one possible implementation:
- turn
drag
into anEventEmitter
, maintaining yourmodifyStage
method - create a
destroy$
Subject
which emits inngOnDestroy
Then in ngOnInit
:
this.drag.pipe(
takeUntil(this.destroy$),
debounceTime(1000),
filter(item => !!item.item)
map(item => {
const statusChange: StatusChangeDTO = {
id: draggedItem.item.id,
newStatusId: +draggedItem.stageId
};
return { name: item.name, status: statusChange }
}),
filter(data => data.name === 'dealStages'),
concatMap(data => this.dealService.savePipelineDealStageUsingPOST(data.status))
// depending on requirements, perhaps use switchMap or exhaustMap
).subscribe();
While this is not totally complete, I think it illustrates my approach. What do you think?
回答2:
Since the whole process uses more than one component I managed to get this done in one. With the help of @Will Alexander and this post:debouncing EventEmitter
Final solution is:
subs = new Subscription();
debouncer = new Subject();
Added this into constructor
this.subs.add(this.debouncer.pipe(
debounceTime(500))
.subscribe((val) => this.drag.emit(val)));
And unsubscribe in ngOnDestroy
ngOnDestroy() {
this.subs.unsubscribe();
}
来源:https://stackoverflow.com/questions/57395007/how-to-limit-rest-calls-angular