Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 2 Change Detection and Zone are Different in Cordova App

We are building an application using Cordova and Angular 2. I have the following code:

    import { Component, OnInit, ChangeDetectorRef, NgZone } from '@angular/core';
    import { Location } from '@angular/common';

    declare var WL : any;

    @Component({
      selector: 'app-store',
      templateUrl: './store.component.html',
      styleUrls: ['./store.component.css']
    })

    export class StoreComponent implements OnInit {
      status: string;
      document: any;

      constructor(private _location: Location,  private changeDetector: ChangeDetectorRef,
        private zone: NgZone) { }

      ngOnInit() {
        var collectionName = 'people';
        this.status = "JSONStore is not yet initialized!";
        if(typeof WL !== "undefined" && typeof WL.JSONStore.get(collectionName) !== "undefined")
                this.status = "JSONStore is initialized!";
        }
      }
      jsonStoreInit(){
                var that = this;
                var collectionName = 'people';

                // Object that defines all the collections.
                var collections = {

                    // Object that defines the 'people' collection.
                    people : {

                        // Object that defines the Search Fields for the 'people' collection.
                        searchFields : {name: 'string', age: 'integer'}
                    }
                };

                // Optional options object.
                var options = { };

                /* // Optional username, default 'jsonstore'.
                username : 'carlos',

                // Optional password, default no password.
                password : '123',

                // Optional local key generation flag, default false.
                localKeyGen : false
                };*/

                WL.JSONStore.init(collections, options).then(function () {

                    // Data to add, you probably want to get
                    // this data from a network call (e.g. MobileFirst Adapter).
                    var data = [{name: 'carlos', age: 10}];

                    // Optional options for add.
                    var addOptions = {

                        // Mark data as dirty (true = yes, false = no), default true.
                        markDirty: true
                    };

                    // Get an accessor to the people collection and add data.
                    return WL.JSONStore.get(collectionName).add(data, addOptions);
                })

                .then(function (numberOfDocumentsAdded) {
                    that.status = "JSONStore is initialized!";
                })

                .fail(function (errorObject) {
                // Handle failure for any of the previous JSONStore operations (init, add).
                    alert("Error");
                    console.log(errorObject);
                });
            }
    }

On a web browser, this works great. When jsonStoreInit() fires, it sets status and updates the UI to "JSONStore is initialized". On the Cordova app, if I don't utilize manual change detection, it will not update the UI. For example, see below where I have //IF THIS ISN'T HERE, IT WILL NOT UPDATE IN CORDOVA:

       ngOnInit() {
            var collectionName = 'people';
            this.status = "JSONStore is not yet initialized!";
            if(typeof WL !== "undefined" && typeof WL.JSONStore.get(collectionName) !== "undefined")
                this.status = "JSONStore is initialized!";

                //IF THIS ISN'T HERE, IT WILL NOT UPDATE IN CORDOVA
                this.changeDetector.markForCheck();
                this.zone.run(()=> function(){});
            }
       }

       jsonStoreInit(){
            var that = this;
            var collectionName = 'people';

            // Object that defines all the collections.
            var collections = {

                // Object that defines the 'people' collection.
                people : {

                    // Object that defines the Search Fields for the 'people' collection.
                    searchFields : {name: 'string', age: 'integer'}
                }
            };

            // Optional options object.
            var options = { };

            /* // Optional username, default 'jsonstore'.
            username : 'carlos',

            // Optional password, default no password.
            password : '123',

            // Optional local key generation flag, default false.
            localKeyGen : false
            };*/

            WL.JSONStore.init(collections, options).then(function () {

                // Data to add, you probably want to get
                // this data from a network call (e.g. MobileFirst Adapter).
                var data = [{name: 'carlos', age: 10}];

                // Optional options for add.
                var addOptions = {

                    // Mark data as dirty (true = yes, false = no), default true.
                    markDirty: true
                };

                // Get an accessor to the people collection and add data.
                return WL.JSONStore.get(collectionName).add(data, addOptions);
            })

            .then(function (numberOfDocumentsAdded) {
                that.status = "JSONStore is initialized!"

                //IF THIS ISN'T HERE, IT WILL NOT UPDATE IN CORDOVA
                this.changeDetector.markForCheck();
                this.zone.run(()=> function(){});
            })

            .fail(function (errorObject) {
            // Handle failure for any of the previous JSONStore operations (init, add).
                alert("Error");
                console.log(errorObject);
            });
        }

I'm also seeing this on simple button clicks to set a variable. Nothing happens in Cordova unless I manually use change detection. I am just learning Angular 2, so any help on what I'm doing wrong is greatly appreciated.

like image 850
Derek Daley Avatar asked Jan 27 '17 21:01

Derek Daley


People also ask

Why we use NgZone in Angular?

There are still some third party APIs that Zone does not handle. In those cases, the NgZone service provides a run() method that allows you to execute a function inside the Angular zone. This function, and all asynchronous operations in that function, triggers change detection automatically at the correct time.

What is NgZone in ionic?

Angular runs inside of its own special zone called NgZone. Running inside of a "zone" allows Angular to detect when asynchronous tasks – things that can alter the internal state of an application, and therefore its views – start and finish.

Is Zone js required for Angular?

Yes, Zone and NgZone is used to automatically trigger change detection as a result of async operations. But since change detection is a separate mechanism it can successfully work without Zone and NgZone . In the first chapter I will show how Angular can be used without zone. js .

What is NgZone service?

NgZone enables us to explicitly run certain code outside Angular's Zone, preventing Angular to run any change detection. So basically, handlers will still be executed, but since they won't run inside Angular's Zone, Angular won't get notified that a task is done and therefore no change detection will be performed.


1 Answers

zone.js patches XHR object and other apis like setInterval, addEventListener, Promise so angular is notified when something happens and it triggers change detection itself.

It looks like JSONStore is using different Promise implementation (jQuery?) which is not patched by zone.js, so you have to trigger change detection manually or wrap you callbacks in zone.run.

like image 185
kemsky Avatar answered Nov 15 '22 04:11

kemsky