角度 2 ngIf 与可观察?

Posted

技术标签:

【中文标题】角度 2 ngIf 与可观察?【英文标题】:angular 2 ngIf with observable? 【发布时间】:2017-04-20 01:23:07 【问题描述】:

我有一个非常简单的服务,它的工作是从 api/authenticate url 获取 200 或 401。

auth.service.ts

@Injectable()
export class AuthService 

    constructor(private http: Http) 

    

    authenticateUser(): Observable<any> 
        return this.http.get(AppSettings.authenitcationEnpoint)
            .map(this.extractData)
            .catch(this.handleError);
    

    private extractData(response: Response) 
        let body = response;
        return body || ;
    

    private handleError(error: Response) 
        let errMsg: string;
        if (error instanceof Response) 
            errMsg = `$error.status - $error.statusText || ''`;
         else 
            errMsg = error.toString();
        
        return Observable.throw(errMsg);
    

现在我想在我的 html 中使用它,我知道我可以创建一个订阅者并根据响应或错误代码设置一个变量,但我的问题是我将不得不在整个地方复制代码。我要的很简单

<div *ngIf="authService.AuthenticateUser()">my content</div>

如果它的 401 来自 handleError 则隐藏,否则显示 div。

这就像从 Observable 获取布尔值。我也有 AuthGuard。

authguard.ts

@Injectable()
export class AuthGuard implements CanActivate 

    private isActive: boolean = true;
    constructor(private authService: AuthService, private router: Router) 
    

    canActivate(): boolean 
        this.authService.authenticateUser()
            .subscribe(() => ,
            error => 
                if (error.indexOf("401") >= 0) 
                    let link = ['contactus'];
                    this.router.navigate(link);
                    this.isActive = false;
                
            );
        return this.isActive;
       


我不能在 ngIf 中使用 authGuard.canActive()。有没有更简单的方法可以在不重复代码的情况下做到这一点。我很确定 AuthGuard 不起作用,因为它每次都必须返回 true,因为订阅需要时间。

app.component.ts

export class AppComponent 
    private isAuthenticated: boolean = false;

    constructor(private authService: AuthService,private authGuard: AuthGuard) 
        this.authService.authenticateUser()
            .subscribe(response => 
                if (response.status == 200)
                    this.isAuthenticated = true;
            ,
            error => 
                if (error.indexOf("401") >= 0)
                    this.isAuthenticated = false;
            );
    

home.component.ts

export class HomeComponent implements OnInit 

    constructor(private http: Http, private authService: AuthService, private utilityService: UtilityService, private homeService: HomeService, private factoryService: FactoryService, private supplierService: SupplierService, private businessAreaService: BusinessAreaService, private router: Router) 
        this.authService.authenticateUser()
            .subscribe(response => 
                if (response.status == 200)
                    this.isAuthenticated = true;
            ,
            error => 
                if (error.indexOf("401") >= 0)
                    this.isAuthenticated = false;
            );
        this.customData = new CustomData(http);
    

您可以看到很多重复的代码。我正在努力避免重复。

当我调用我的 api 时,会出现一个弹出窗口,输入 windows 用户名/密码以进行 windows 身份验证。在我取消该弹出窗口之前,我没有得到 401。所以我想隐藏我的菜单 + 主页组件。

在我的服务中,我得到 response:200 in ma​​p 和未经授权:401 在 catch

【问题讨论】:

这个 ngIf 来自哪里?我假设的一个组件?拥有该组件的.ts 会很好。 如果你不subscribe 到可观察对象,你永远不会有任何结果。 最后,一旦你有一个组件(从服务中)获取 http 调用的结果,你可以订阅它并保存值以将其显示到你的视图中,或者使用@987654328 @管道。 【参考方案1】:

这里,你返回的是一个 observable 而不是值:

authenticateUser(): Observable<any> 
    return this.http.get(AppSettings.authenitcationEnpoint)
        .map(this.extractData)
        .catch(this.handleError);

你应该做的是在你的服务中创建一个属性,然后在你的 html 中使用这个属性,而不是订阅 observable 的函数。比如:

@Injectable()
export class AuthService 

    isAuthenticated: boolean = false; //The variable

    constructor(private http: Http) 
    

    authenticateUser(): Observable<any> 
        return this.http.get(AppSettings.authenitcationEnpoint)
            .map(this.extractData)
            .catch(this.handleError);
    

    private extractData(response: Response) 
        let body = response;
        this.isAuthenticated = body.isAuthenticated; //here you assign a value to variable
        return body || ;
    

    private handleError(error: Response) 
        let errMsg: string;
        if (error instanceof Response) 
            errMsg = `$error.status - $error.statusText || ''`;
         else 
            errMsg = error.toString();
        
        return Observable.throw(errMsg);
    

然后,在您的 html 中,只需使用:

<div *ngIf="authService.isAuthenticated">my content</div>

另一种方法是使用async 管道。因此,视图将等待观察返回,然后再制作摘要循环。比如:

<div *ngIf="authService.AuthenticateUser() | async">my content</div> // Here you use the async pipe

【讨论】:

因为如果值发生变化,它不会被更新。这是 observable 的主要兴趣。 @Maxime 不会更新了?你为什么这么说? Arf 这不是我的意思,完全是我的错。他将无法订阅该值的更改。在这种情况下和一般情况下,With 太糟糕了。如果他有一个可观察的,他应该保留它:) 这是个好建议,但是 401 会去 catch 块,所以我应该在 catch 中将其设为 false 并在进入 map 时设为 true 吗? 这取决于你的实现。如果您查看声明级别,则该变量默认为 false。因此,只有当您有“200/201”状态代码时,您才需要将变量更改为 true。如果返回“401”,则变量已经为假,因此您无需在“catch()”中执行任何操作。我假设您只有在未通过身份验证时才能进行身份验证。另一件事是这个服务应该是一个单例,所以变量只分配一次。将服务加载器放在应用程序提供者级别,而不是子模块中......

以上是关于角度 2 ngIf 与可观察?的主要内容,如果未能解决你的问题,请参考以下文章

javascript 使用与可观察模式相同的代码概念的不可观察解决方案。

我可以将 async/await Promises 与可观察的 RXJS 混合使用吗?

Ionic 4 Angular模板与异步管道绑定到可观察

以角度声明对象的可观察数组

观察者模式

是否需要使用加载可观察切换来进行必要的观察?