0

我正在将 Angular 与 Firebase 一起使用。在我的应用程序中,用户可以创建“项目”并邀请其他用户加入该“项目”,换句话说,一个项目可以有多个用户。我在firestore有user收藏和project收藏。

用户集合

users/userID/ -> { name: string }

集合中users是具有 id 值和数据的文档(只是名称)

项目集合

project/projectID/ -> 
{
  projectName: String,
  users: [
    { 
     userID: (here id of user),
     role: String
    }
  ]

集合中project是具有 id 值和数据的文档。数据是 projectName 和用户数组。在这个用户数组中,我有可以访问该项目的用户 ID。

设想

用户登录我的应用程序,现在他在 route 的简单介绍页面中/hello。现在用户可以看到他的项目,当他点击某个项目时,他将被路由到该路径/project/:id,在这里他可以使用其他功能,例如/project/:id/overview。它类似于firebase控制台https://console.firebase.google.com/u/0/project/myprojectid/authentication/users:)我希望我只是澄清我想要实现的目标。如果您仍然感到困惑,请在评论中告诉我。

有什么问题?

在我的项目中,这很好用,但问题是当有人将不同项目的id复制到 url 并点击输入时。我想阻止用户访问其他用户的项目,所以如果我/project/notMyProject/overview在 url 中输入这个路径,我应该被重定向或其他东西。嗯,这很容易,只需创建我认为的路由器保护。于是我开始...

应用程序路由.module.ts

const routes: Routes = [
  { path: 'project/:pid', component: ProjectComponent, 
   children: [
    { path: '', component: OverviewComponent}, 
    { path: 'users', component: ProjectUsersComponent, }, 
   ],
   canActivate: [ProjectGuard, AuthGuard]
  },
  { path: '', component: PageComponent }
];

项目.guard.ts

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthService } from '../core/auth.service';
import { map } from 'rxjs/operators';
import {
 AngularFirestore,
 AngularFirestoreDocument
} from 'angularfire2/firestore';

@Injectable({
 providedIn: 'root'
})
export class ProjectGuard implements CanActivate {

private itemDoc: AngularFirestoreDocument<any>;
item: Observable<any>;

constructor(
 private auth: AuthService,
 private afs: AngularFirestore,
) {}

canActivate(
 next: ActivatedRouteSnapshot,
 state: RouterStateSnapshot): Observable<boolean> | boolean {

  this.itemDoc = this.afs.doc<any>.(`project/${next.paramMap.get('pid')}`);

  this.item = this.itemDoc.valueChanges();

  this.item.subscribe(response => {
    console.log(response)
    response.users.forEach( (element) => {
      if(element.userID == this.auth.user.subscribe( data => { return data.uid })) {
        return true;
      }
    })
  })
 }
}

我在这里。我对 rxjs 和所有可观察的东西都很陌生。该代码是异步的,我不知道如何使它工作,我收到错误,我必须返回 Observable 或布尔值......

问题

我对这个特定问题的一般方法是否正确?我应该考虑一些更好的选择吗?其他页面如何解决这个问题,例如 firebase(试试这个Firebase)。如果我的解决方案很好,你能帮我解决我的 project.guard 吗?非常感谢

我试图尽可能清楚地描述我的问题。如果您遗漏了一些信息,请在评论中告诉我,我会更新我的问题。我搜索了很多小时如何解决这个问题,但我找不到任何东西,或者我只是使用了错误的搜索关键字。我希望这对其他人也有帮助。

4

1 回答 1

1

Observables当你看到两件事时,你可以确定(几乎)你做错了:

  1. 做一个嵌套的subscribe,和
  2. 将结果分配给subsrciptionakavar result == someObservables.subscribe()

这是您在代码中遇到的两个确切错误。

错误 1 ​​发生的原因是因为大部分时间Observables都是异步的,这也是你的情况,你的代码不能保证结果的顺序会返回。您的代码同步运行,不能故意等待异步任务的执行。这就是operators存在(映射、过滤器、concatMap 等)的原因,以便您处理Observables(本身),而不是在Observables最终发出其值时在订阅内部。

错误 2 并不少见,因为新手Observables总是认为 Observables 的结果来自函数Observables- 他们没有。它们来自订阅回调函数中的参数:

var thisIsSubscriptionNotResult = myObservable.subscribe(results=>{
    var thisisTheRealResult = results;
}

在理解了这两个错误之后,解决代码中的问题就相当容易了。您将需要combineLatest这里的帮助,将两个不同的observables.

canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {

    this.itemDoc =  this.afs.doc<any>(`project/${next.paramMap.get('pid')}`);
    return Observable
        .combineLatest(this.itemDoc.valueChanges, this.auth.user)
        .map(([response, authUser]) => {
            return response.users.some(x => x.userID === authUser.uid);
        });
}

上面的代码所做的是它结合了两个 Observable,this.itemDoc.valueChanges并且this.auth.user. 但是角度路由器保护需要 a Observable<boolean>,因此您需要映射它们才能将其转换为布尔值。

如果您使用的是 rxjs 6,则语法略有不同:

return combineLatest(this.itemDoc.valueChanges, this.authU.user)
    .pipe(
        map(([response, authUser]) => {
            return response.users.any(x => x.userID === authUser.uid);
        })
    );
于 2018-07-09T02:48:04.663 回答