Can I make my Angular2 UI bindings affect the UI when the device orientation changes?
Because of the issue where layout qualifiers are not yet supported in Angular2 apps, I tried using this nativescript-orientation plugin and one-way bindings to change my UI when the device's orientation changes.
<DockLayout>
<Label dock="{{isLandscape ? 'left' : 'top'}}" text="Controls" backgroundColor="red"></Label>
<Label dock="{{isLandscape ? 'right' : 'bottom'}}" text="Results" backgroundColor="yellow"></Label>
</DockLayout>
and:
import { Component, OnInit, OnDestroy } from "@angular/core";
import * as application from "application";
import { DeviceOrientation } from "ui/enums";
//import * as orientation from "nativescript-orientation";
var orientation = require("nativescript-orientation");
@Component({
templateUrl: "pages/dock-layout.html"
})
export class DockLayoutComponent implements OnInit, OnDestroy {
isLandscape: boolean = false;
constructor() {
}
ngOnInit() {
this.isLandscape = orientation.getOrientation() == DeviceOrientation.landscape;
application.on(application.orientationChangedEvent, this.onOrientationChanged);
}
ngOnDestroy() {
application.off(application.orientationChangedEvent, this.onOrientationChanged);
}
onOrientationChanged = (args: application.OrientationChangedEventData) => {
this.isLandscape = args.newValue == DeviceOrientation.landscape;
console.log("onOrientationChanged():"+args.newValue+" isLandscape:"+this.isLandscape);
};
}
The result is:


I expected the 'Controls' label to appear on the left, and the 'Results' label to appear on the right.
I initially thought that my bindings were written incorrectly. But when I added:
<ActionBar title="{{!isLandscape ? 'Portrait' : 'Landscape'}}">
<ActionItem text="{{isLandscape ? 'Portrait' : 'Landscape'}}" (tap)="onRotateTapped($event)" ios.position="right"></ActionItem>
</ActionBar>
and
onRotateTapped(event) {
this.isLandscape = !this.isLandscape;
}
By tapping the ActionItem, I found that the bindings did work properly and the UI elements moved to the left or right (ie. that the dock property of a child of a DockLayout is bindable), but the UI doesn't update despite the log saying isLandscape:true.
How can I update the UI during an orientation change, preferably without using setTimeout?
Project is here.
I just tested your project, and it turns out to be an Ng zone issue. The nativescript-orientation plugin isn't angular-aware, and its callbacks do not execute in the zone. The resolution here is to wrap your event handler in our global.zonedCallback utility method:
application.on(application.orientationChangedEvent, global.zonedCallback(this.onOrientationChanged));
Ideally that would be done by the plugin and you shouldn't have to call that method yourself (global.zonedCallback is a noop when running without Angular). Details here:
http://www.nativescriptsnacks.com/videos/2016/06/13/zoned-callbacks.html
Magically, by toggling isLandscape within an NgZone resolved the issue:
import { Component, OnInit, OnDestroy, NgZone } from "@angular/core";
...
constructor(private zone: NgZone) {
}
...
onOrientationChanged = (args: application.OrientationChangedEventData) => {
this.zone.run(() => {
this.isLandscape = args.newValue == DeviceOrientation.landscape;
console.log("onOrientationChanged():"+args.newValue+" isLandscape:"+this.isLandscape);
});
};
Commit to project is here.
Thanks for the help, @tsonevn.
Most helpful comment
Magically, by toggling
isLandscapewithin anNgZoneresolved the issue:Commit to project is here.
Thanks for the help, @tsonevn.