angular - 从 Google API 加载 auth2 时不会运行更改检测

标签 angular google-api angular2-observables angular2-changedetection google-api-js-client

我正在尝试从 Angular 2 服务中加载脚本 https://apis.google.com/js/api.js?onload=initGapi 以便调用Google API,但是每当我尝试在脚本完成加载和初始化时返回一个值时,async 管道不想将值呈现给模板。

我正在使用 EventEmitter,当 gapi.load()gapi.client.init 完成时触发,并且observable 在组件中订阅它。异步管道似乎不想读取该值,直到我单击同一组件内的一个按钮(并且可能触发更改检测)。当我使用 ApplicationRef 中的 tick() 时,强制组件内的更改检测不起作用。

加载 Google API 的服务如下:

google-api.service.ts

import { Injectable, EventEmitter } from '@angular/core';
import { Observable } from 'rxjs/Observable';

import { User, Client } from '../../+home/models';

declare var gapi: any;

@Injectable()
export class GoogleApiService {

    CLIENT_ID: string = '....apps.googleusercontent.com';
    DISCOVERY_DOCS: string[] = ["https://www.googleapis.com/discovery/v1/apis/admin/directory_v1/rest"];
    SCOPES = 'https://www.googleapis.com/auth/admin.directory.user.readonly';

    authEmitter: EventEmitter<boolean> = new EventEmitter();

    constructor() {
        window['initGapi'] = (ev) => {
            this.handleClientLoad();
        };
        this.loadScript();
    }

    loadScript() {
        let script = document.createElement('script');
        script.src = 'https://apis.google.com/js/api.js?onload=initGapi';
        script.type = 'text/javascript';
        document.getElementsByTagName('head')[0].appendChild(script);
    }

    handleClientLoad() {
        gapi.load('client:auth2', this.initClient.bind(this));
    }

    initClient() {
        gapi.client.init({
            discoveryDocs: this.DISCOVERY_DOCS,
            clientId: this.CLIENT_ID,
            scope: this.SCOPES
        }).then(() => {
            this.authEmitter.emit(true);
        });
    }

    get gapi(): Observable<any> {
        return this.authEmitter.map(() => 'hello world');
    }
}

以及从服务中读取的组件

app.component.ts

import { Component, OnInit } from '@angular/core';
import { Observable } from 'rxjs/Observable';

import { GoogleApiService } from '../../+core/services';

@Component({
    template: `
    What does the service say?
    {{ api$ | async }}
    <button (click)="click()">Button</button>`
})
export class InviteEmployeesContainer implements OnInit {
    api$: Observable<string>;

    constructor(private gApiService: GoogleApiService) {}

    ngOnInit() {
        this.api$ = this.gApiService.gapi;

        this.gApiService.gapi.subscribe((result) => {
            console.log(result);
        });
    }

    click() {
        console.log('clicked');
    }
}

生成的页面打印出字符串 What does the service say? 并有一个按钮,但在我单击该按钮之前不会打印文本 hello world,这不是所需的行为,它应该立即在页面上可见。

此外,当我使用 this.gApiService.gapi.subscribe 订阅并登录到控制台时,它会在我期望的时候记录 hello world

有谁知道当 authEmitter 触发事件时,我如何使用异步管道让 hello world 打印到页面。

最佳答案

我最终找到了这个问题的解决方案,这个问题有几个问题。

首先,EventEmitter 应该只用于发射,不应该被订阅,所以我用 Subject 代替了它。通常我会尝试使用可观察对象,但在这种情况下,我发现一个主题更合适。

下面是一个使用 Subject 而不是 EventEmitter 的修改后的 plunker。我还注释掉了 gapi.client.init... 并将其替换为不同的 promise ,因为它实际上并不是问题的一部分。请注意,在 plunker 中,更改检测在单击按钮之前仍不会运行,这不是预期的。

https://plnkr.co/edit/YaFP07N6A4CQ3Zz1SbUi?p=preview

我遇到的问题是因为 gapi.load('client:auth2', this.initClient.bind(this)); 在外部运行 initClient Angular 2 区域,将其排除在变化检测之外。

为了在变化检测中捕获主体,我们必须在 Angular 2 区域内运行主体的 nextcomplete 调用。这是通过导入 NgZone 然后修改行来完成的:

    new Promise((res) => res()).then(() => {
      this.zone.run(() => {
        this.authSubject.next(true);
        this.authSubject.complete(true);
      });
    });

See the final (resolved) plunker here.

关于angular - 从 Google API 加载 auth2 时不会运行更改检测,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42660643/

相关文章:

javascript - 设置 routerLink 参数 url 的格式

javascript - Angular 2,在 ng build --prod 之后,ui-router 停止工作

google-app-engine - 使用常规 Google 帐户作为应用程序拥有的帐户

angular - RxJs 在返回的数组上循环

angular - 错误错误 : Uncaught (in promise): invalid link: LoginPage

javascript - 如何将数据从 API 加载到 Angular 2 中的数组中?

c# - 如何将客户的 IP 地址转发到 Google Places Autocomplete API

javascript - 无法在谷歌地图 API 中的标记之间绘制路线

angularjs - angular 2 observable.subscribe 在第二次调用之前不会执行

angular2 使用 PrimeNG-Scheduler 实现 FullCalendar-Scheduler