4

I'm using a TabView component with template syntax to iterate over it and add items inside (using an observable collection and async pipe). My items come from a http request.

My home.component.tns.html as follows:

<ActionBar title="Angular 2 Seed Advanced (nativescript)">
</ActionBar>
<StackLayout>
  <TabView selectedIndex="0" selectedColor="#FF0000">
    <template ngFor let-tab [ngForOf]="tabs | async">
      <StackLayout *tabItem="tab">
          <Label text="tab item"></Label>
      </StackLayout>
    </template>
  </TabView>

  <Button text="call something" (tap)="emptymethod()"></Button>

</StackLayout>

...and my home.component.ts:

...
    export class HomeComponent{

        public tabs: Observable<any[]>;

        constructor(private _menuService: MenuService) {

            this._menuService.getMenu()
                .map((menus) => {
                    let result: Array<any> = [];

                    if (menus) {
                        menus.forEach(menu => {
                            result.push({ 'title': menu.title });
                        });
                    }
                    return result;
                })
                .subscribe(
                data => {
                    this.tabs = Observable.of<any[]>(data);
                },
                error => error => this._log.debug(error));
        }

        emptymethod() {
            this._log.debug('HomeComponent - nada invoked');
        }

        ngOnInit() {

        }
    ...

When the interface is renderized, the TabView menu does not appears, but when i click over call something button and emptymethod is called the items inside the TabView appears, in other words, after the "refresh" that component appears...

This should not happens automatically?

Richard Lee
  • 2,136
  • 2
  • 25
  • 33

3 Answers3

14

I had couple of situations with nativescript where the UI wont refresh (the zone will not trigger a UI refresh or something) and the easiest way to do it is to force the code to run into the zone. Here is small example

export class TestClass {
   constructor(private _ngZone: NgZone) {
   }
   someOperation(){
     this._ngZone.run(() => {
       //Do whatever you want here
     })
   }
}

I know this is not the best solution but thats the cleanest workaround currently I think

Here is another question that I asked months ago related to this: Angular 2 How to get Angular to detect changes made outside Angular?

Community
  • 1
  • 1
Denko Mancheski
  • 2,709
  • 2
  • 18
  • 26
1

There seems to be a problem with how async operations and change detection is being handled in nativescript-angular when third party plugins are involved.

The current suggestion is to wrap all async notify calls within a zoned callback function.

The following video explains the issue and solution. Hope this helps. http://www.nativescriptsnacks.com/videos/2016/06/13/zoned-callbacks.html

More details on a corresponding feature request in Nativescript is present here. https://github.com/NativeScript/NativeScript/issues/2229

abz
  • 11
  • 3
0

I would guess because the change detection is not being triggered when the next block in the subscribe is called so the change to this.tabs is not detected until changed detection triggered by the button press.

You should be able to simplify your Observable and just do:

        this.tabs = this._menuService.getMenu()
            //Convert the Array into a flattened Observable
            .flatMap((menus) => menus ? menus.map(({title}) => {title}) : []);

Which will flatten the array into an Observable implicitly for your ngFor block.

paulpdaniels
  • 18,395
  • 2
  • 51
  • 55
  • Is **flatMap** a function from Observables? I'm receiving a message _flatMap in not a function_ ...or should i return a array from **getMenu** and not a Observable? – Richard Lee May 20 '16 at 17:31
  • Yes `flatMap` is a function on `Observable`, it is an alias for `mergeMap`. You may need to import it see [here](https://github.com/ReactiveX/rxjs#installation-and-usage) – paulpdaniels May 20 '16 at 17:57