Angular+TypeScript级联删除问题:删除项目需同步删子项目及职位
Got it, let's tackle this cascading deletion issue you're facing with your Angular/TypeScript app. The core problem right now is that you're only deleting the Project itself, but not cleaning up its linked SubProjects and their associated Positions. Here's how to fix this step by step:
1. Understand the Cascading Order
First, we need to delete from the bottom up:
- Delete all Positions linked to each SubProject of the target Project
- Delete all SubProjects linked to the target Project
- Finally, delete the Project itself
2. Component-Level Fix (Quick Implementation)
If you want to adjust your existing component code directly, modify the deleteProject method to handle the cascading logic. You'll need to fetch the linked SubProjects from your store first, then iterate through their Position IDs:
deleteProject(project) { const text = 'Are you sure to delete this project ?'; this.dialog .open(ConfirmationDialogComponent, { data: text, disableClose: true }) .afterClosed() .subscribe((result: boolean) => { if (!result) return; // Fetch all subprojects linked to this project from the store this.store.select(selectSubProjects).pipe( take(1), // Grab only the current state snapshot to avoid memory leaks map(subProjects => subProjects.filter(sp => project.subProjectIds.includes(sp.id))) ).subscribe(linkedSubProjects => { // Step 1: Delete all positions for each linked subproject linkedSubProjects.forEach(subProject => { subProject.positionIds.forEach(positionId => { this.store.dispatch(new DeletePositionInternalAction(positionId)); }); // Step 2: Delete the subproject itself this.store.dispatch(new DeleteSubProjectInternalAction(subProject.id)); }); // Step 3: Delete the main project this.store.dispatch(new DeleteProjectInternalAction(project.id)); }); }); }
3. Better Approach: Handle Logic in NgRx Effects
For cleaner code (and to follow NgRx best practices), move the cascading deletion logic to an Effect. This keeps your component focused on UI interactions, not business logic:
@Injectable() export class ProjectEffects { deleteProject$ = createEffect(() => this.actions$.pipe( ofType(DeleteProjectInternalAction), withLatestFrom( this.store.select(selectSubProjects), this.store.select(selectProjectById(action => action.payload)) ), switchMap(([action, allSubProjects, targetProject]) => { const deletionActions = []; // Get all subprojects linked to the target project const linkedSubProjects = allSubProjects.filter(sp => targetProject.subProjectIds.includes(sp.id)); // Add actions to delete positions first linkedSubProjects.forEach(subProject => { subProject.positionIds.forEach(posId => { deletionActions.push(new DeletePositionInternalAction(posId)); }); // Add action to delete the subproject deletionActions.push(new DeleteSubProjectInternalAction(subProject.id)); }); // Add final action to delete the project deletionActions.push(new DeleteProjectInternalAction(action.payload)); return of(...deletionActions); }) ) ); constructor(private actions$: Actions, private store: Store<AppState>) {} }
Key Notes to Ensure This Works:
- Make sure you have
DeletePositionInternalActiondefined (since you already have delete actions for Project/SubProject). - Your reducers should correctly handle these delete actions (e.g., remove the corresponding items from their respective state slices).
- If your SubProjects are stored as an object (instead of an array) in the store, adjust the filtering logic to use
Object.values(allSubProjects).filter(...). - If your deletion flows involve backend API calls, extend the Effect to handle API requests first (e.g., call
deletePositionAPI, wait for success, then proceed to delete SubProjects, etc.).
内容的提问来源于stack exchange,提问作者user9049996

