Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

I am trying to learn angular2 and created a test application with an odata webapi backend. In the application i have a view which fetches an array of items and i want to show these in my view.

For fetching data from the frontend i am using breezejs library since it has proved to save me alot of time in the past and i like using it with an odata backend.

The call tree and application structure looks like this:

Call starts by calling a service function from the view to start fetching the items (note that i am returning an es-6 promise from every call):

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;
});

Then from the view it will go to the service which in turn calls the repository:

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

The repository fetch function:

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

Then eventually the repository calls the (generic) datacontext which will execute the query with the 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);
        });
    });
}

Now as you can see in the view i have to use the run function from NgZone or else my view wont update. I am wondering why i have to do this since i was expecting angular2 to see this for me automatically. I have digged through hseveral similair questions and could not really find an answer yet. I have also included the angular2-polyfills script as suggested in another thread but that did not solve it.

What am i missing or what do i have to implement for my view to update automatically without calling zone.run?

like image 281
Jurgen Welschen Avatar asked Apr 09 '16 16:04

Jurgen Welschen


3 Answers

Breeze runs just fine with Angular2 now. We are working on a large application using the current version of Breeze and Angular Beta 8 with no problems.

The only slight workaround right now is that breeze does not yet use the Angular2 http provider. However, you can shim in the default 'Q' provider so that it supports the ES6 Promises that Angular2 expects with following code:

/**
 * 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;
    }
}

You can then import this file

import { Q } from './q';

and then somewhere near the top of your app

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

At this point, all of your standard breeze methods work exactly as they do today and Angular's change detection should also have no issues.

like image 138
Jay Traband Avatar answered Nov 12 '22 11:11

Jay Traband


Angular runs in a zone where most async APIs are patched. When an async call is completed Angular runs change detection.

Somehow breeze code leaves Angulars zone and "breaks" change detection. This is either because you initialize breeze from outside Angular or breeze uses some async API that is not patched by Angulars zone and therefore callbacks are executed outside Angulars zone.

like image 22
Günter Zöchbauer Avatar answered Nov 12 '22 10:11

Günter Zöchbauer


Yes, the "problem" is that you were using some kind of promise library that zones doesn't know about (e.g. Q). Fortunately the promise library is pluggable in Breeze and we wrote an ES6 promise plugin that works great.

We haven't published it yet (see Jay's answer above) nor have we written the Angular 2 http plugin yet.

Both very easy but we've been super busy. Looks like it's about time.

like image 28
Ward Avatar answered Nov 12 '22 10:11

Ward