GVKun编程网logo

Angular 4 – canActivate observable not invoked

12

如果您对Angular4–canActivateobservablenotinvoked感兴趣,那么这篇文章一定是您不可错过的。我们将详细讲解Angular4–canActivateobservabl

如果您对Angular 4 – canActivate observable not invoked感兴趣,那么这篇文章一定是您不可错过的。我们将详细讲解Angular 4 – canActivate observable not invoked的各种细节,此外还有关于Angular - 从 CanActivate 中的 Observable 服务获取布尔值 更新:来自isActiveUser()的附加逻辑、Angular - 将 Observable 数组作为 @Input 传递、Angular 2 Observable Interval锁定UI、Angular 2 RC 5 – Observable.interval触发更改检测的实用技巧。

本文目录一览:

Angular 4 – canActivate observable not invoked

Angular 4 – canActivate observable not invoked

我试图使用RxJS observables在Angular 2/4中实现 canActivate.我已经阅读了 another SO question.使用以下代码,我的canActivate方法仅在应用程序启动时工作一次,但是当isLoggedIn observable触发新值时,永远不会再打印hello.

canActivate(): Observable<boolean> {
  return this.authService.isLoggedIn().map(isLoggedIn => {
    console.log('hello');
    if (!isLoggedIn) {
      this.router.navigate(['/login']);
    }
    return isLoggedIn;
  }).first();
}

或者这个版本不能正常工作:

canActivate(): Observable<boolean> {
  return this.authService.isLoggedIn().map(isLoggedIn => {
    console.log('hello');
    if (isLoggedIn) {
      this.router.navigate(['/']);
    }
    return !isLoggedIn;
  });
}

但是,它适用于此代码:

canActivate(): Observable<boolean> {
  return Observable.create(obs => {
    this.authService.isLoggedIn().map(isLoggedIn => {
      console.log('hello');
      if (isLoggedIn) {
        this.router.navigate(['/']);
      }
      return !isLoggedIn;
    }).subscribe(isLoggedIn => obs.next(isLoggedIn));
  });
}

我在第一段代码中做错了什么?

编辑:这是isLoggedIn实现

@LocalStorage(AuthService.JWT_TOKEN_KEY)
private readonly token: string;
private tokenStream: Subject<string>;

public isLoggedIn(): Observable<boolean> {
  if (!this.tokenStream) {
    this.tokenStream = new BehaviorSubject(this.token);
    this.storage.observe(AuthService.JWT_TOKEN_KEY)
      .subscribe(token => this.tokenStream.next(token));
  }
  return this.tokenStream.map(token => {
    return token != null
  });
}

使用ngx-webstorage.和RxJS BehaviorSubject.

解决方法

使用RxJs验证AuthService

这是我从AngularJs的承诺转换为Angular的Observable模式时遇到的困难之一.你看到承诺是拉动通知,而观察者是推送通知.因此,您必须重新考虑您的AuthService,以便它使用推送模式.即使在我编写工作Observables时,我一直在考虑拉动.在拉动方面,我无法停止思考.

使用承诺模式更容易.创建AuthService时,它将创建一个解析为“未登录”的promise,或者它将创建一个“还原已记录状态”的异步保证.然后,您可以使用名为isLoggedIn()的方法来返回该promise.这使您可以轻松处理显示用户数据和收到用户数据之间的延迟.

AuthService作为推送服务

现在,我们切换到Observables,动词“is”需要更改为“when”.进行这一小改动有助于您重新思考事情的发展方向.因此,我们将“isLoggedIn”重命名为“whenLoggedIn()”,这将是一个在用户进行身份验证时发出数据的Observable.

class AuthService {
     private logIns: Subject = new Subject<UserData>();

     public setUser(user: UserData) {
          this.logIns.next(user);
     }

     public whenLoggedIn(): Observable<UserData> {
          return this.logIns;
     }
}

// example
AuthService.whenLoggedIn().subscribe(console.log);
AuthService.setUser(new UserData());

当用户传递给setUser时,它会发出以订阅新用户的身份验证.

以上方法的问题

以上介绍了需要修复的几个问题.

>订阅whenLoggedIn将永远收听新用户.拉流永远不会完成.
>没有“现状”的概念.推送给订阅者后,之前的setUser会丢失.
>它仅告知您何时对用户进行身份验证.如果没有当前用户则不会.

我们可以通过从Subject切换到BehaviorSubject来解决一些问题.

class AuthService {
     private logIns: Subject = new BehaviorSubject<UserData>(null);

     public setUser(user: UserData) {
          this.logIns.next(user);
     }

     public whenLoggedIn(): Observable<UserData> {
          return this.logIns;
     }
}

// example
AuthService.whenLoggedIn().first().subscribe(console.log);
AuthService.setUser(new UserData());

这更接近我们想要的.

变化

> BehaviorSubject将始终为每个新订阅发出最后一个值.
> whenLoggedIn().first()被添加到订阅并在收到第一个值后自动取消订阅.如果我们没有使用BehaviorSubject,那么会阻塞,直到有人调用setUser,这可能永远不会发生.

BehaviorSubject的问题

BehaviorSubject不适用于AuthService,我将在此处演示此示例代码.

class AuthService {
     private logIns: Subject = new BehaviorSubject<UserData>(null);

     public constructor(userSessionToken:string,tokenService: TokenService) {
          if(userSessionToken) {
              tokenService.create(userSessionToken).subscribe((user:UserData) => {
                    this.logIns.next(user);
               });
         }
     }

     public setUser(user: UserData) {
          this.logIns.next(user);
     }

     public whenLoggedIn(): Observable<UserData> {
          return this.logIns;
     }
}

以下是问题在代码中的显示方式.

// example
let auth = new AuthService("my_token",tokenService);
auth.whenLoggedIn().first().subscribe(console.log);

上面创建了一个带有令牌的新AuthService来恢复用户会话,但是当它运行时,控制台只打印“null”.

发生这种情况是因为使用初始值null创建了BehaviorSubject,并且在HTTP调用完成后将恢复用户会话的操作.在会话恢复之前,AuthService将继续发出null,但是当您想要使用路由激活器时,这是一个问题.

ReplaySubject更好

我们想要记住当前用户,但在我们知道是否有用户之前不会发出任何内容. ReplaySubject是这个问题的答案.

这是你如何使用它.

class AuthService {
     private logIns: Subject<UserData> = new ReplaySubject(1);

     public constructor(userSessionToken:string,tokenService: TokenService) {
          if(userSessionToken) {
              tokenService.create(userSessionToken).subscribe((user:UserData) => {
                    this.logIns.next(user);
               },()=> {
                    this.logIns.next(null);
                    console.error('Could not restore session');
               });
         } else {
             this.logIns.next(null);
         }
     }

     public setUser(user: UserData) {
          this.logIns.next(user);
     }

     public whenLoggedIn(): Observable<UserData> {
          return this.logIns;
     }
}

// example
let auth = new AuthService("my_token",tokenService);
auth.whenLoggedIn().first().subscribe(console.log);

上述内容不会等到whenLoggedIn发出第一个值.它将获得第一个值并取消订阅.

ReplaySubject可以正常工作,因为它可以记住1个项目或者什么都不发出.这不是重要的部分.当我们在canActivate中使用AuthService时,我们希望等到用户状态已知.

CanActivate示例

现在,这使得编写重定向到登录屏幕或允许路由更改的用户警卫变得更加容易.

class UserGuard implements CanActivate {
      public constructor(private auth: AuthService,private router: Router) {
      }

      public canActivate(route: ActivatedRouteSnapshot,state: RouterStateSnapshot): Observable<boolean> {
           return this.auth.whenLoggedIn()
                      .first()
                      .do((user:UserData) => {
                          if(user === null) {
                              this.router.navigate('/login');
                          }
                      })
                      .map((user:UserData) => !!user);
      }

如果存在用户会话,则将产生Observable为true或false.它还会阻止路由器更改,直到该状态已知(即我们是否从服务器获取数据?).

如果没有用户数据,它还会将路由器重定向到登录屏幕.

Angular - 从 CanActivate 中的 Observable 服务获取布尔值 更新:来自isActiveUser()的附加逻辑

Angular - 从 CanActivate 中的 Observable 服务获取布尔值 更新:来自isActiveUser()的附加逻辑

如何解决Angular - 从 CanActivate 中的 Observable 服务获取布​​尔值 更新:来自isActiveUser()的附加逻辑?

我正在尝试获取布尔值并将其以 angular 形式返回到 CanActivate 内。

这是我的示例服务:

isActiveUser<bool>(){
    return this.http.get<boolean>(apiGetUserStatus);
}

这是我的canActivateMethod

canActivate(route: ActivatedRouteSnapshot,state: RouterStateSnapshow): Observable<boolean> | Promise<boolean> | boolean){
    var status = this.myService.isActiveUser().subscribe((active: boolean)=>{
        return active;
    })

    return status;
}

不幸的是,当我尝试控制台记录 status 时,它显示了可观察的内容,而不是实际的布尔值。对此有什么帮助吗?

解决方法

canActivate 也可以返回一个 observable。因此,只需以这种方式更改您的代码。

canActivate(route: ActivatedRouteSnapshot,state: RouterStateSnapshot): boolean | Observable<boolean> | Promise<boolean> {
        const subject = new Subject<boolean>();
        this.myService.isActiveUser().subscribe((result) => {
            subject.next(result);
        });
        return subject;
    }
,

尝试只返回 isActiveUser() 函数。

canActivate(
  route: ActivatedRouteSnapshot,state: RouterStateSnapshow
): Observable<boolean> {
  return this.myService.isActiveUser();
}

更新:来自isActiveUser()的附加逻辑

您可以使用 maptap 等 RxJS 运算符来转换数据或根据 isActiveUser() 的结果执行某些效果。

canActivate(
  route: ActivatedRouteSnapshot,state: RouterStateSnapshow
): Observable<boolean> {
  return this.myService.isActiveUser().pipe(
    map((activeUser: boolean) => {
      // do something
      return isUserAdmin();   // <-- eg.: function that returns boolean
    }),tap((user: boolean) => {
      // do some side-effects
      // tap doesn''t have to return anything
    }),switchMap((user: boolean) => {
      // you could also map to another observable using `switchMap`
      return someFunc(); // <-- eg: function returns an `Observable<boolean>`
    })
  );
}

Angular - 将 Observable 数组作为 @Input 传递

Angular - 将 Observable 数组作为 @Input 传递

如何解决Angular - 将 Observable 数组作为 @Input 传递?

由于@Input 的数据结构发生变化,我正在尝试将模板中绑定数据的当前 NGRX 实现替换为可重用组件。

HTML:
//replace
"<child [summary]="summaryTitle$ | async" ></child >"

//with
"<child  [summary]="summaryTitles" ></child>"
parent.comp.ts
//replace
summaryTitle$ = this.store.select(Title)

//with
summaryTitles = [{title:summaryTitle$,text: "someConstantValue"},...]

在这里,我需要在将 summaryTitles 数组传递给 <child> 之前解析它,任何简单的方法都可以这样做,例如对单个结果使用异步管道。

这个想法是重新使用现有的 <child> 而不做太多更改,我也无法向 @store 添加任何更改

解决方法

编辑:如果 text 永远不是可观察的:

const result$ = combineLatest(
  summaryTitles.map(({ title,text }) => 
    title.pipe(map(title => ({ title,text })));
  )
);

A minimal reproducible example 与 stackblitz 之类的东西会很好,重现 ngrx 状态和所有内容很痛苦,但这样的事情应该可行:

import { of,map,combineLatest } from ''rxjs'';

const summaryTitles = [
  { title: of(''1''),text: of(''2'') },{ title: of(''3''),text: of(''4'') }
];

const result$ = combineLatest(
  summaryTitles.map(({title,text}) =>
    combineLatest([title,text]).pipe(
      map(([title,text]) => ({ title,text }))
    )
  )
);

result$.subscribe(console.log);

您可能希望将 combineLatest 更改为 forkJoinzip。该文档的 operator decision tree 和 learnrxjs.io 对此非常有用。

Stackblitz:https://stackblitz.com/edit/rxjs-9gah4b?file=index.ts

编辑: 如果 text 字段可能是可观察的,请使用 isObservable,例如:

const result$ = combineLatest(
  summaryTitles.map(({ title,text }) => {
    const text$ = isObservable(text) ? text : of(text);
    return combineLatest([title,text$]).pipe(
      map(([title,text }))
    );
  })
);

但在此之前确保它在其他地方是可观察的可能更容易。

旁注:我现在在我的 stackblitz 中注意到最终结果,它应该是一个数组,是一个“索引对象”({ 0: ''foo'',1: ''bar'' })。当我尝试以在终端中工作的方式手动将其转换为数组时,它仍然是一个对象?。我假设这是 stackblitz 中的一个怪癖。检查您的直播内容。

Angular 2 Observable Interval锁定UI

Angular 2 Observable Interval锁定UI

当我使用Observable.Interval来执行UI的http刷新时,如果间隔太快,它会锁定UI上的按钮.按钮没有注册点击,似乎是一个时间问题.如果我增加时间并因此错过了获取调用按钮的工作,但数据在更新时会延迟.

间隔

this.dataSub = Observable.interval(1000).subscribe(x => {
  this.getData();
})

的getData

getData(): void {
     this.dataService.getData()
         .subscribe(
              data => this.data = data,error => console.log("Error HTTP Get Service" + this.data),() => {});
 }

有没有最好的做法,或者我缺少的东西,我应该做的是刷新UI而不是锁定按钮

解决方法

理论

通常,您应该尽可能避免明确订阅observable.相反,使用所有的运算符(是的,找出正确的运算符可能非常棘手)可以将所有源/输入可观察量组合成一个或多个在异步管道视图中使用的可观察对象.

Don’t forget that JS is single threaded (aside from workers). Your UI and most Angular code has to share the one thread,so long running JS locks up your UI.

这有三个主要好处:

>几乎不可能导致内存泄漏.如果您不记得在ngOnDestroy()挂钩中总是取消订阅,或者当您不再关心它时,则无论何时手动订阅都会产生内存泄漏.异步管道在使用它的组件/元素被破坏时将正确取消订阅 – 您无需担心它.
>减少工作量.使用switchMap(),switchLatest()等运算符,您可以取消和清除超级HTTP调用和其他昂贵的操作,甚至可以在它们启动之前停止它们(如果不再需要它们).不要做比你必须做的更多.这通常意味着更改检测不必运行太多,这意味着更好的性能.
>清洁代码.较少的成员变量,更像功能的代码.是的,在学习Rx时可能会有点难以理解,但它会变得更容易.

在实践中

考虑到所有这些,您如何将其应用于您的代码?

您可能没有注意到的一件事(很多人不是),如果你的DataService.getData()方法是这样的:

getData(): Observable<MyData[]> {
    return this.http.get('http://some.url/data').map(res => res.json());
}

然后,每次订阅Http服务创建的observable时,都会发出新请求.这就是你想要的,但是你不想要的是在新的请求发出后立即处理任何先前请求的结果.

因此,您可以使用控制器中的类似内容,使用最新请求中的最新数据组成一个observable:

ngOnInit() {
     // (I follow a convention where observable properties end in $)
     this.data$= Observable.interval(1000).flatMapLatest(() => {
         return this.dataService.getData();
     });
 }

没有订阅,只是一个已创建的可观察对象.然后在您的视图中,只需使用带有数据$属性的异步管道即可.

例如:

<ul *ngFor="let d of (data$| async); trackBy: d?.id">
    <li>{{d.name}}</li>
</ul>

Angular 2 RC 5 – Observable.interval触发更改检测

Angular 2 RC 5 – Observable.interval触发更改检测

我在组件树的某处创建了一个带有500毫秒的Observable.interval并订阅了它.该组件没有输入或输出属性.该间隔在每次发送滴答时从根组件触发更改检测.这导致我的应用程序中的大量开销,这是不需要的.我没有找到任何关于该行为的文档.

是否可以关闭由此Observable引起的更改检测?

编辑:添加代码

以下代码演示了我想要做的事情.我按照Günter的建议将区间放在Angular区域之外,但现在对阵列的修改不会在模板中发布.有没有办法在不触发更改检测的情况下更新模板?

import {NotificationList} from "./NotificationList";
import {Notification} from "./Notification";
import {Component,OnDestroy,ChangeDetectorRef,ngzone} from "@angular/core";
import { Subscription } from 'rxjs/Subscription';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/interval';

class Timednotification {
    notification: Notification;
    remainingTime: number;
}

@Component({
    selector: "notifications",template: `
        <ul>
            <li *ngFor="let notification of notifications">notification.message</li>
        </ul>
    `
})
export class NotificationComponent implements OnDestroy {
    notifications: Array<Timednotification> = [];
    private subscription: Subscription;
    private timer: Subscription = null;
    private delay: number = 2000;
    private tickDelay: number = 500;

    constructor(notificationQueue: NotificationList,private zone: ngzone) {
        this.subscription = notificationQueue.getobservable().subscribe(notification => this.onNotification(notification));
        this.zone.runOutsideAngular(() => {this.timer = Observable.interval(500).subscribe(x => this.onTimer())});
    }

    private onTimer(): void {
        if(this.notifications.length == 0) {
            return;
        }
        let remainingNotifications: Array<Timednotification> = [];
        for(let index in this.notifications) {
            let timednotification = this.notifications[index];
            timednotification.remainingTime -= this.tickDelay;
            if(timednotification.remainingTime <= 0) {
                continue;
            }
            remainingNotifications.push(timednotification);
        }
        this.notifications = remainingNotifications;
    }

    private onNotification(notification: Notification): void {
        let timednotification = new Timednotification();
        timednotification.notification = notification;
        timednotification.remainingTime = this.delay;
        this.notifications.push(timednotification);
    }

    ngOnDestroy(): void {
        this.subscription.unsubscribe();
        if(this.timer !== null) {
            this.timer.unsubscribe();
        }
    }
}

解决方法

您可以使用ChangeDetectionStrategy.OnPush将组件关闭.

每个事件都会导致更改检测运行(和setTimeout以及ngzone所涵盖的任何其他异步API).

如果您使用OnPush,那么只会更改来自observables订阅的输入和事件|异步导致变化检测.

我们今天的关于Angular 4 – canActivate observable not invoked的分享就到这里,谢谢您的阅读,如果想了解更多关于Angular - 从 CanActivate 中的 Observable 服务获取布尔值 更新:来自isActiveUser()的附加逻辑、Angular - 将 Observable 数组作为 @Input 传递、Angular 2 Observable Interval锁定UI、Angular 2 RC 5 – Observable.interval触发更改检测的相关信息,可以在本站进行搜索。

本文标签: