在多个函数中重用一个rxjs'主题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在多个函数中重用一个rxjs'主题相关的知识,希望对你有一定的参考价值。

我有一个ApiClientService接收rxjs Service作为其所有方法中的参数之一。然后,此服务将API,subscribe调用返回的Observables,解析返回值,然后回调Subjectnext()回调。

这个模型工作正常,直到在其中一个类中,让我们说UserService,我有一个用于调用多个API的Subject。这个Subjectnext()回调不会在第二个API返回时被调用。我已经确认API确实返回了,它确实调用了Service.next(),传递了所需的值。

这是预期的行为吗? (每个Observable服务一次)或者我的设计有问题吗?

提前致谢!

// ApiClientService.ts

 @Injectable()
 export class ApiClientService {
   postUserLogin(userid: string, password: string): Observable<Object> {
     const url = "/api/getforuser";
     const httpOptions = {
       headers: new HttpHeaders({
         'Content-Type': 'application/json',
       })
     }
     return this.http.post(url, { userid: userid }, httpOptions);
   }

   postSessionLogin(sessionid: string): Observable<Object> {
     const url = "/api/getforsession";
     const httpOptions = {
       headers: new HttpHeaders({
         'Content-Type': 'application/json',
       })
     }
     return this.http.post(url, { sessionid: sessionid }, httpOptions);
   }
 }

UserService.ts

// UserService.ts

@Injectable()
export class UserService {

  currentUser: UserModel;

  constructor(
    private apiclient: ApiClientService
  ) { }

  isLoggedIn(): boolean {
      if ( this.currentUser == null ) {
          return false;
      } else if ( this.currentUser.session != null ) {
          if ( this.currentUser.session.length > 0 ) {
              return true;
          }
      }
      return false;
  }

  sessionLogin(userListener: Subject<UserModel>) {
    console.log("UserService.sessionLogin");
    var session = this.cookieService.get("session");
    if ( session == null ) {
      userListener.error("session not found");
    } else {
      var obRes = this.apiclient.postSessionLogin(session);
      obRes.subscribe( res => {
        console.log("sessionLogin response");
        var payload = res['payload'];
        console.log("payload: " + payload);
        var user = payload['user'];
        console.log("user: " + user);
        this.currentUser = new UserModel;
        this.currentUser.userid = user.userid;
        userListener.next(this.currentUser);
      });
    }
  }

  userLogin(username: string, password: string, userListener: Subject<UserModel>) {
    console.log("UserService.userLogin start");
    var obRes = this.apiclient.postUserLogin(username, password);
    obRes.subscribe( res => {
      console.log("UserService.userLogin response start...");
      console.log(res);
      console.log("userLogin response json...");
      var payload = res['payload'];
      console.log("payload: " + payload);
      var user = payload['user'];
      console.log("UserService.userLogin user: " + user);
      this.currentUser = new UserModel;
      this.currentUser.userid = user.userid;
      userListener.next(this.currentUser);
    }, err => {
      console.log("not even error, nothing...");
      console.log(err);
    });
  }

}

使用UserService的前端组件

// UserLoginComponent
@Component({
  selector: 'app-home-login',
  templateUrl: './home-login.component.html',
  styleUrls: ['./home-login.component.scss']
})
export class HomeLoginComponent implements OnInit {

    @Input() userModel: UserModel;

    loggedinUser: UserModel;

    userloginForm: FormGroup;

    // it doesn't work if it is only 1 Subject (loginListener) used for both userLogin and sessionLogin
    loginListener: Subject<UserModel>;
    loginListenerSubs: Subscription;
    // it will work if I have another Subject (sessionListener) which is used separately for sessionLogin
    // sessionListener: Subject<UserModel>;
    // sessionListenerSubs: Subscription

    constructor(private fb: FormBuilder,
      private router: Router,
      private userService: UserService
    ) {
        this.createForm();
    }

    createForm() {
        this.userloginForm = this.fb.group({
            username: [ '', Validators.required ],
            password: [ '', Validators.required ]
        });
    }

    onSubmitLogin() {
        this.userModel = this.pullFormContent();
        this.userService.userLogin(this.userModel.username, this.userModel.password, this.loginListener);
        // same result if it is placed here as well
        // this.loginListenerSubs = this.loginListener.subscribe(
        //   user => this.onLoginSuccess(user),
        //   error => this.onLoginFailed(error),
        //   () => this.onLoginCompleted()
        // );
    }

    pullFormContent() {
        const formModel = this.userloginForm.value;
        console.log("home-login component: pullformContent formModel");
        console.log(formModel);

        const user: UserModel = {
          userid: 0,
          username: formModel.username,
          password: formModel.password,
        }
        return user;
    }

    onLoginSuccess(user) {
      console.log("loginListener next"); // never get called the second api call
      console.log(user);
      this.loggedinUser = user;
      console.log(this.loggedinUser);
      if ( this.router == null ) {
          console.log("router is null");
      } else {
          this.router.navigate(['/some/user/view']);
      }
    }
    onLoginFailed(error) {
      console.log("loginListener error: " + error);
    }
    onLoginCompleted() {
      console.log("loginListener complete");
    }

    ngOnInit() {
      console.log("home-login-component ngOnInit");
      this.loginListener = new Subject<UserModel>();
      this.loginListenerSubs = this.loginListener.subscribe(
        user => this.onLoginSuccess(user),
        error => this.onLoginFailed(error),
        () => this.onLoginCompleted()
      );
      // try to login automatically
      this.userService.sessionLogin(this.loginListener);

      if ( this.userService.isLoggedIn() ) {
            console.log("user is logged in, todo: auto redirect");
            this.router.navigate(['/some/user/view']);
      }
    }

    ngOnDestroy() {
      console.log("ngOnDestroy");
      this.loginListenerSubs.unsubscribe();
    }

}
答案

我看不出问题的原因,但我可能会试着给你一些建议。

为何使用主题?

你为什么不直接返回你的组件http客户端返回的Observable,可能通过map运算符转换?例如,这意味着这样的事情

sessionLogin() {
    console.log("UserService.sessionLogin");
    var session = this.cookieService.get("session");
    if ( session == null ) {
      Observable.throw("session not found");
    } else {
      return this.apiclient.postSessionLogin(session)
      .map( res => {
        console.log("sessionLogin response");
        return this.buildUser(res);
      });
    }
  }

userLogin(username: string, password: string) {
    console.log("UserService.userLogin start");
    return this.apiclient.postUserLogin(username, password)
    .map( res => {
      console.log("UserService.userLogin response start...");
      console.log(res);
      console.log("userLogin response json...");
      return this.buildUser(res);
    };
  }

buildUser(res) {
      const payload = res['payload'];
      console.log("payload: " + payload);
      const user = payload['user'];
      console.log("UserService.userLogin user: " + user);
      this.currentUser = new UserModel;
      this.currentUser.userid = user.userid;
      return this.currentUser;
}

然后,您只需订阅sessionLogin或userLogin方法返回的Observable。

自动登录

这是你的ngOnInit()方法的一部分

// try to login automatically
  this.userService.sessionLogin(this.loginListener);

  if ( this.userService.isLoggedIn() ) {
        console.log("user is logged in, todo: auto redirect");
        this.router.navigate(['/some/user/view']);
  }

您正在调用sessionLogin并立即检查是否有用户使用isLoggedIn方法登录。目前尚不清楚:如果用户已登录,则无需致电

以上是关于在多个函数中重用一个rxjs'主题的主要内容,如果未能解决你的问题,请参考以下文章

RxJS:如何切换以在多个行为主题源之间切换

用于在多个活动/片段中重用的全局加载器 (LoaderManager)

Laravel:如何在控制器的几种方法中重用代码片段

RxJS主题(Subject)

rxjs 生成函数

rxjs 生成函数