0

I have a nativescript-angular app which runs a bluetooth service to connect to a peripheral (device). The app presents a label with a message indicating that no bluetooth device has been connected yet to the mobile device running the app. What I'm trying to do is to update the UI so that the label will disappear when a connection occurs (and reappear when a disconnection occurs).

app.component.tns.html

<StackLayout orientation="vertical" marginBottom="50">
  <Label text="No bluetooth device detected." id="noBluetoothWarning" horizontalAlignment="center" color="#B11" *ngIf="bluetoothDisconnected" marginTop="30" visibility="{{ isBluetoothConnected ? 'collapsed' : 'visible' }}"></Label>
</StackLayout>

app.component.ts

export class NSAppComponent extends AppComponent implements OnInit {


  pageData: Observable;

  ngOnInit() {
    this.bluetoothDisconnected = true;

    let _page = <Page>topmost().currentPage;
    _page.actionBarHidden = true;
    _page.frame.bindingContext = this.pageData;

    let that = this;

    this.bluetoothScanner.connectionStateChangeEvent.subscribe((connectionState) => {
      that.log.debug(`Am I in an angular zone? ${NgZone.isInAngularZone()}`);
      this.ngZone.run(() => {

        that.log.debug(`connectionStateEvent triggered! state is: ${connectionState}`);

        that.bluetoothDisconnected = !connectionState;

        that.pageData.set("isBluetoothConnected", connectionState);

        that.pageData.notify({ object: that.pageData, eventName: Observable.propertyChangeEvent, propertyName: "isBluetoothConnected", value: connectionState });

        this.pageData.notify({ object: that.pageData, eventName: Observable.propertyChangeEvent, propertyName: "bluetoothDisconnected", value: !connectionState });
      })
    });
  }

 constructor( @Inject(LogService) public log: LogService, private ngZone: NgZone) {
      this.pageData = new Observable();
      this.pageData.set("isBluetoothConnected", false);
      this.bluetoothScanner = new BluetoothScanner(...);
  }
}

bluetoothScanner.ts

export class BluetoothScanner {
  private connectionState = new BehaviorSubject<boolean>(false);
  ...
    that.connectionState.next(true);
}

You can see that the code is a mess, but the reason is that for over a month I've tried to use every single possible combination of actions I've been mentioned that might help me to get the UI to update - but to no avail.

Here are the following things I've previously attempted that did not work as well:

  • wrap callback in zonedCallback()
  • wrap callback in ngZone.run()
  • manually update Component's variable value (which is used by the tns.html template)
    • instead of setting a boolean value, I'd try to pass a literal string of collapsed or visible
  • use *ngIf and Visibility properties
  • use angular's Renderer.listen
  • use nativescript's Observable class (import { Observable } from "data/observable") with the .notify() functionality to emit an event + listen to that event in NSAppComponent. BluetoothScanner would extend Observable
  • use rxjs BehaviorSubject object (import { Observable } from "rxjs/BehaviorSubject")
  • use Angular's changeDetectorRef object to call for an update of the UI after updating the variable
  • set an Observable to be bound to the page. (note it's possible that I haven't properly set bound the Observable to the page object [based on what I've been trying to do in the code mentioned above]).

Through all of these, I was able to update the variable value, but not update the UI. The only thing that actually updated my UI was when I preformed the variable value change in a call that originated from a UI event (e.g. a button tap event handler).

1
  • A minor note - I did try different combinations of accessing the this object in the closure rather than use the that variable as I do in my example, nothing changed in that case. Commented Oct 20, 2016 at 10:37

1 Answer 1

5

No need to use Observable within N+Angular-2 application - use the angular binding techniques instead. Another thing I noticed is that you are also using the binding syntax for vanilla NativeScript which is not compatible with the binding syntax for the N+Angular-2 application.

Here is the difference: NativeScript Core binding syntax:

visibility="{{ isBluetoothConnected ? 'collapsed' : 'visible' }}"

NativeScript + Angular-2 binding syntax:

[visibility]="isItemVisible ? 'visible' : 'collapsed'"

Example on how to bind the data from a callback can be found here basically, you should do the following

export class UsingConnectivityExampleComponent implements OnInit {

public connectionType: string; // use this variable to for binding in your html

constructor(private zone: NgZone) { 
}

ngOnInit() {
    connectivity.startMonitoring((newConnectionType: number) => {
        this.zone.run(() => {
            switch (newConnectionType) {
                case connectivity.connectionType.none:
                    this.connectionType = "None"; // assign value
                    console.log("Connection type changed to none.");
                    break;
                case connectivity.connectionType.wifi:
                    this.connectionType = "Wi-Fi"; // assign value
                    console.log("Connection type changed to WiFi.");
                    break;
                case connectivity.connectionType.mobile:
                    this.connectionType = "Mobile"; // assign value
                    console.log("Connection type changed to mobile.");
                    break;
            }
        });
    });
}

And finally bind it with the ng-2 syntax (the example shows one way binding)

<Label [text]="connectionType"></Label>

more about data binding in NativeScript+Angular-2 in this documentation article

p.s. When using TypeScript lambda you do not need to do the var that = this; to preserve the scope meaning of this within callbacks. Reference: https://basarat.gitbooks.io/typescript/content/docs/arrow-functions.html

Sign up to request clarification or add additional context in comments.

2 Comments

(mobile shenanigans) Thanks for the reply Nick. I've updated the binding in the HTML to the square brackets, and updated the code to use 'NgZone.run()' as well as 'zonedCallback' (separately). Unfortunately, the UI is still not updating in this case.
@NLev did you remove the Observable related code as well? You probably shouldn't even need to use NgZone.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.