首页 > 解决方案 > 在 Angular 组件中对导入的服务方法使用 forkJoin() 时,“void”类型的参数不可分配

问题描述

我正在尝试将来自三个不同 API 获取请求的三组数据发送到 Angular 组件。目前,我有一个处理 HTTP 请求的服务,我将该服务导入到我的组件中,以便我可以访问服务方法。

我目前可以订阅并返回任何单个任务服务方法的数据,这样做:

    this.tasksService.getTasks();
    this.tasksSub = this.tasksService.getTaskUpdateListener()
      .subscribe((allTasks: Task[]) => {
        this.allTasks = allTasks;
      });

console.log(allTasks)将返回所需的结果。

但是,我需要将来自三种不同方法的数据获取到我的组件中,但我不能。这是我最近一次尝试使用forkJoin()来完成此操作:

    this.tasksSub = forkJoin(
      this.tasksService.getTasks(),
      this.tasksService.getOpenTasks(),
      this.tasksService.getClosedTasks()
    ).this.tasksService.getTaskUpdateListener()
      .subscribe((allTasks: Task[], openTasks: Task[], closedTasks: Task[]) => {
        this.allTasks = allTasks;
        this.openTasks = openTasks;
        this.closedTasks = closedTasks;
      });

这段代码在第一个参数上给了我这个错误消息forkJoin()

无效错误

问题: 关于为什么会发生这种情况以及如何解决它的任何想法?

下面是我的整个 component.ts 和 service.ts:

任务列表.component.ts

import { Component, OnInit, OnDestroy } from "@angular/core";
import { Subscription, forkJoin } from 'rxjs';
import {CdkDragDrop, moveItemInArray, transferArrayItem} from '@angular/cdk/drag-drop';

import { Task } from "../task.model";
import { TasksService } from "../tasks.service";
import { AuthService } from 'src/app/auth/auth.service';

@Component({
  selector: "app-task-list",
  templateUrl: "./task-list.component.html",
  styleUrls: ["./task-list.component.css"]
})
export class TaskListComponent implements OnInit, OnDestroy {

  constructor(public tasksService: TasksService, private authService: AuthService) {}

  allTasks: Task[] = [];
  openTasks: Task[] = [];
  closedTasks: Task[] = [];

  employeeIsAuthenticated = false;
  eId: string;
  isAdmin: string;

  private tasksSub: Subscription;
  private authStatusSub : Subscription;


  ngOnInit() {
    this.eId = this.authService.getEmployeeId();
    this.isAdmin = this.authService.getIsAdmin();


    this.tasksService.getTasks();
    this.tasksSub = this.tasksService.getTaskUpdateListener()
      .subscribe((allTasks: Task[]) => {
        this.allTasks = allTasks;
      });

    this.tasksSub = forkJoin(
      this.tasksService.getTasks(),
      this.tasksService.getOpenTasks(),
      this.tasksService.getClosedTasks()
    ).this.tasksService.getTaskUpdateListener()
      .subscribe((allTasks: Task[], openTasks: Task[], closedTasks: Task[]) => {
        this.allTasks = allTasks;
        this.openTasks = openTasks;
        this.closedTasks = closedTasks;
      });

    // auth sub
    this.employeeIsAuthenticated = this.authService.getIsAuth();
    this.authStatusSub = this.authService
      .getAuthStatusListener()
      .subscribe( isAuthenticated => {
        this.employeeIsAuthenticated = isAuthenticated;
        this.eId = this.authService.getEmployeeId();
        this.isAdmin = this.authService.getIsAdmin();
      });
  }

  drop(event: CdkDragDrop<string[]>) {
    if (event.previousContainer === event.container) {
      moveItemInArray(event.container.data, event.previousIndex, event.currentIndex);
    } else {
      transferArrayItem(event.previousContainer.data,
                        event.container.data,
                        event.previousIndex,
                        event.currentIndex);
    }
  }

  onDelete(taskId: string) {
    this.tasksService.deleteTask(taskId);
  }

  ngOnDestroy() {
    this.tasksSub.unsubscribe();
    this.authStatusSub.unsubscribe();
  }
}

任务服务.ts

import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { Subject } from "rxjs";
import { map } from "rxjs/operators";
import { Router } from "@angular/router";

import { Task } from "./task.model";

@Injectable({ providedIn: "root" })
export class TasksService {
  private tasks: Task[] = [];
  private tasksUpdated = new Subject<Task[]>();

  // we will need to construct this to use CRUD operations and router capabilities
  constructor(private http: HttpClient, private router: Router) {}

  // gets all tasks
  getTasks() {
    this.http
      .get<{ message: string; tasks: any }>("http://localhost:3000/api/tasks/alltasks")
      .pipe(
        map(taskData => {
          return taskData.tasks.map(task => {
            return {
              title: task.title,
              content: task.content,
              id: task._id,
              creator: task.creator,
              done: task.done
            };
          });
        })
      )
      .subscribe(transformedTasks => {
        console.log(transformedTasks);
        this.tasks = transformedTasks;
        this.tasksUpdated.next([...this.tasks]);
      });
  }
 // get tasks for single employee
  getMyTasks(creator: string) {
    this.http
      .get<{ message: string; tasks: any }>("http://localhost:3000/api/tasks/mytasks/"+creator)
      .pipe(
        map(taskData => {
          return taskData.tasks.map(task => {
            return {
              title: task.title,
              content: task.content,
              id: task._id,
              creator: task.creator,
              done: task.done
            };
          });
        })
      )
      .subscribe(transformedTasks => {
        console.log(transformedTasks);
        this.tasks = transformedTasks;
        this.tasksUpdated.next([...this.tasks]);
      });
  }

  // get open tasks
  getOpenTasks() {
    this.http
      .get<{ message: string; tasks: any }>("http://localhost:3000/api/tasks/opentasks/")
      .pipe(
        map(taskData => {
          return taskData.tasks.map(task => {
            return {
              title: task.title,
              content: task.content,
              id: task._id,
              creator: task.creator,
              done: task.done
            };
          });
        })
      )
      .subscribe(transformedTasks => {
        console.log(transformedTasks);
        this.tasks = transformedTasks;
        this.tasksUpdated.next([...this.tasks]);
      });
  }

  // get closed tasks
  getClosedTasks() {
    this.http
      .get<{ message: string; tasks: any }>("http://localhost:3000/api/tasks/closedtasks/")
      .pipe(
        map(taskData => {
          return taskData.tasks.map(task => {
            return {
              title: task.title,
              content: task.content,
              id: task._id,
              creator: task.creator,
              done: task.done
            };
          });
        })
      )
      .subscribe(transformedTasks => {
        console.log(transformedTasks);
        this.tasks = transformedTasks;
        this.tasksUpdated.next([...this.tasks]);
      });
  }

  getTaskUpdateListener() {
    return this.tasksUpdated.asObservable();
  }
  // get specific task with id passed in
  getTask(id: string) {
    return this.http.get<{
      _id: string;
      title: string;
      content: string;
      creator: string;
      done: boolean;
      }>(
      "http://localhost:3000/api/tasks/" + id
    );
  }
  // create a task
  addTask(title: string, content: string, creator: string, done: boolean) {
    const task: Task = { id: null, title: title, content: content, creator: creator,  done: done};
    this.http
      .post<{ message: string; taskId: string }>(
        "http://localhost:3000/api/tasks",
        task
      )
      .subscribe(responseData => {
        const id = responseData.taskId;
        task.id = id;
        this.tasks.push(task);
        this.tasksUpdated.next([...this.tasks]);
        // return to tasks
        this.router.navigate(["/mytasks/"+creator]);
      });
  }
  // update a task
  updateTask(id: string, title: string, content: string, creator: string, done: boolean ) {
    const task: Task = { id: id, title: title, content: content, creator:creator, done: done };
    this.http
      .put("http://localhost:3000/api/tasks/" + id, task)
      .subscribe(response => {
        const updatedTasks = [...this.tasks];
        const oldTaskIndex = updatedTasks.findIndex(t => t.id === task.id);
        updatedTasks[oldTaskIndex] = task;
        this.tasks = updatedTasks;
        this.tasksUpdated.next([...this.tasks]);
        // return to tasks
        this.router.navigate(["/mytasks/"+creator]);
      });
  }
 // delete a task
  deleteTask(taskId: string) {
    this.http
      .delete("http://localhost:3000/api/tasks/" + taskId)
      .subscribe(() => {
        const updatedTasks = this.tasks.filter(task => task.id !== taskId);
        this.tasks = updatedTasks;
        this.tasksUpdated.next([...this.tasks]);
      });
  }
}

标签: angulartypescriptrxjssubscription

解决方案


forkJoin接受 observables 作为参数。在此场景中,您展示的情况并非如此。为了使它工作,TaskService中的所有 3 个方法都应该返回Observables。请在下面找到代码片段:

在服务文件(TaskService)中:

import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { Observable } from "rxjs";
import { Post } from "../models/index";

@Injectable({
 providedIn: "root"
})
export class TaskService {
constructor(private http: HttpClient) {}

public getTasks() {
  const url = "https://jsonplaceholder.typicode.com/posts"; // URL just to demonstrate
  return this.http.get<Post[]>(url);
}
public getOpenTasks() {
  const url = "https://jsonplaceholder.typicode.com/posts"; // URL just to demonstrate
  return this.http.get<Post[]>(url);
}
public getClosedTasks() {
  const url = "https://jsonplaceholder.typicode.com/posts"; // URL just to demonstrate
  return this.http.get<Post[]>(url);
}
}

在组件中:

constructor(private taskService:TaskService){}
ngOnInit(){
  forkJoin(
    this.taskService.getTasks(),
    this.taskService.getOpenTasks(),
    this.taskService.getClosedTasks(),
  ).subscribe(response=>{
    console.log(response);
  }, error=>{
    console.log(error)
  })
}

在这里,服务文件中的函数返回 observables。在组件中,我们进行 3 次并行 API 调用并有一个共同的订阅。我们将以数组的形式返回响应。请在下面的演示网址中找到:

https://stackblitz.com/edit/angular-sf-forkjoin-issue?file=src/app/app.component.ts

https://angular-sf-forkjoin-issue.stackblitz.io

希望这会给你一个清晰的认识。


推荐阅读