为什么我必须调用 NgZone.run 才能使用 breezejs 在 Angular2 中更新我的视图?

Why do i have to call NgZone.run for my view to update in Angular2 with breezejs?

我正在尝试学习 angular2 并创建了一个带有 odata webapi 后端的测试应用程序。 在应用程序中,我有一个视图可以获取一组项目,我想在我的视图中显示这些项目。

为了从前端获取数据,我使用了 breezejs 库,因为它在过去被证明可以节省我很多时间,而且我喜欢将它与 odata 后端一起使用。

调用树和应用程序结构如下所示:

调用从视图中调用服务函数开始获取项目(请注意,我从每次调用中返回一个 es-6 promise):

this._scrumModuleService.fetchActiveSessions().then((sessions: ScrumSession[]) => {
    // Here i have to call zone.run else my view wont update.
    this._zone.run(() => {
        this.sessions = sessions;
    });
}).catch((error: any) => {
     debugger;
});

然后从视图中它将转到调用存储库的服务:

public fetchActiveSessions(): Promise<ScrumSession[]> {
    return this._scrumSessionRepository.fetchActiveSessions();
}

存储库获取函数:

public fetchActiveSessions(): Promise<ScrumSession[]> {
    return this._dataContext.fetch(new breeze.EntityQuery().from("ScrumSessions").expand(['creator', 'scrumRoom','productOwner', 'users']));
}

然后最终存储库调用(通用)datacontext,它将使用 breeze entitymanager 执行查询:

public fetch(query: breeze.EntityQuery, isRetry: boolean = false): Promise<any> {

    return new Promise((resolve, reject) => {
            this.entityManager.executeQuery(query).then((result: breeze.QueryResult): void => {
            // Data has been fetched, resolve the results
            resolve(result.results);
        });
    });
}

现在,正如您在视图中看到的那样,我必须使用 NgZone 中的 运行 函数,否则我的视图将不会更新。我想知道为什么我必须这样做,因为我期待 angular2 自动为我看到这个。 我已经研究了几个类似的问题,但还没有真正找到答案。我还按照另一个线程中的建议包含了 angular2-polyfills 脚本,但这并没有解决它。

我缺少什么或者我必须实现什么才能让我的视图在不调用 zone.run 的情况下自动更新?

Angular 在大多数异步 API 已打补丁的区域中运行。异步调用完成后 Angular 运行更改检测。

不知何故 breeze 代码离开 Angulars 区域和 "breaks" 变化检测。这是因为您从 Angular 外部初始化 breeze 或 breeze 使用了一些未被 Angulars 区域修补的异步 API,因此回调在外部执行Angulars 区。

Breeze 现在在 Angular2 上运行得很好。我们正在使用当前版本的 Breeze 和 Angular Beta 8 开发大型应用程序,没有任何问题。

目前唯一的解决方法是 breeze 尚未使用 Angular2 http 提供程序。但是,您可以填充默认的 'Q' 提供程序,以便它支持 Angular2 期望使用以下代码的 ES6 Promise:

/**
 * Minimum necessary deferred object for breeze Q/ES6 Promise adapter
 * Makes ES6 promise look like Q. 
 */
export interface Deferred {
    promise: Promise<any>;
    resolve: (value?: {} | PromiseLike<{}>) => void;
    reject: (reason?: any) => void;
}

/**
 * Minimum for breeze breeze Q/ES6 Promise adapter
 */
export const Q = {
    defer(): Deferred {
        let resolve: (value?: {} | PromiseLike<{}>) => void;
        let reject: (reason?: any) => void;
        let promise = new Promise((_resolve, _reject) => {
            resolve = _resolve;
            reject = _reject;
        })
        return {
            promise: promise,
            resolve(value: any) { resolve(value); },
            reject(reason: any) { reject(reason); }
        }
    },

    resolve(value?: {} | PromiseLike<{}>) {
        let deferred: Deferred = Q['defer']();
        deferred.resolve(value);
        return deferred.promise;
    },


    reject(reason?: any) {
        let deferred: Deferred = Q['defer']();
        deferred.reject(reason);
        return deferred.promise;
    }
}

然后您可以导入此文件

import { Q } from './q';

然后在应用顶部附近的某个位置

breeze.config.setQ(<breeze.promises.IPromiseService>Q);

此时,您所有的标准 breeze 方法都像今天一样工作,Angular 的变化检测也应该没有问题。

是的,"problem" 是您使用了某种区域不知道的承诺库(例如 Q)。幸运的是,promise 库在 Breeze 中是可插入的,我们编写了一个运行良好的 ES6 promise 插件。

我们还没有发布它(参见上面 Jay 的回答),我们也没有编写 Angular 2 http 插件。

两者都很容易,但我们一直非常忙。看来是时候了。