Ionic 2 UI not updating after change to model

When I was creating my beacon-starter application that I blogged about here, I came across a problem that I had not encountered before in any of my Ionic 2 apps.

In my home.ts component I subscribe to an event which is fired when beacons are discovered.  My code was (originally):

listenToBeaconEvents() {
this.events.subscribe(‘didRangeBeaconsInRegion’, (data) => {

this.beacons = [];

let beaconList = data.beacons;
beaconList.forEach((beacon) => {
let beaconObject = new BeaconModel(beacon);
this.beacons.push(beaconObject);
});

});
}

What I expected to happen here was that my beacons array (which was represented on my template using *ngFor in the standard way) would be refreshed with the values from my beacon objects.  Unfortunately, nothing was displayed on screen despite the objects being created successfully.

What I discovered was that for the first time (for me) I needed to look at Angular Zone Change Detection which you can read about here.  In my case I needed to make three small changes to my component to get it to work:

first, import the NgZone module:

import { NgZone } from @angular/core;

second, create an NgZone:

this.zone = new NgZone({ enableLongStackTrace: false });

third, wrap my update code inside of this.zone.run() :

listenToBeaconEvents() {
this.events.subscribe(‘didRangeBeaconsInRegion’, (data) => {
this.zone.run(() => {

this.beacons = [];

let beaconList = data.beacons;
beaconList.forEach((beacon) => {
let beaconObject = new BeaconModel(beacon);
this.beacons.push(beaconObject);
});

});

});
}

Once these changes were in place the model changes were detected successfully and the UI updated.

I need to read up on Angular Zones and Change Detection I think because this was a new problem for me.  Hopefully, this may help someone else who has a similar issue.

 

 

 

 

 

 

 

 

Creating a Beacon application with Ionic 2

This is a very quick “get going with beacons and Ionic 2” post.

The code can be found in a github repository here.

First create an Ionic 2 app:

ionic start –v2 beacon-starter blank

Now, from the newly created application directory add the cordova beacon plugin:

ionic plugin add cordova-plugin-ibeacon

Next, create a provider (beacon-provider.ts) in a providers folder:

import { Injectable } from ‘@angular/core’;
import { Platform, Events } from ‘ionic-angular’;
import { IBeacon } from ‘ionic-native’;
/*
Generated class for the BeaconProvider provider.

See https://angular.io/docs/ts/latest/guide/dependency-injection.html
for more info on providers and Angular 2 DI.
*/
@Injectable()
export class BeaconProvider {

delegate: any;
region: any;

constructor(public platform: Platform, public events: Events) {
}

initialise(): any {
let promise = new Promise((resolve, reject) => {
// we need to be running on a device
if (this.platform.is(‘cordova’)) {

// Request permission to use location on iOS
IBeacon.requestAlwaysAuthorization();

// create a new delegate and register it with the native layer
this.delegate = IBeacon.Delegate();

// Subscribe to some of the delegate’s event handlers
this.delegate.didRangeBeaconsInRegion()
.subscribe(
data => {
this.events.publish(‘didRangeBeaconsInRegion’, data);
},
error => console.error()
);

// setup a beacon region – CHANGE THIS TO YOUR OWN UUID
this.region = IBeacon.BeaconRegion(‘deskBeacon’, ‘F7826DA6-4FA2-4E98-8024-BC5B71E0893E’);

// start ranging
IBeacon.startRangingBeaconsInRegion(this.region)
.then(
() => {
resolve(true);
},
error => {
console.error(‘Failed to begin monitoring: ‘, error);
resolve(false);
}
);
} else {
console.error(“This application needs to be running on a device”);
resolve(false);
}
});

return promise;
}
}

 

Now import this provider into app.module.ts and add it to the providers folder.  Your app.module.ts file should then look like this:

 

import { NgModule } from ‘@angular/core’;
import { IonicApp, IonicModule } from ‘ionic-angular’;
import { MyApp } from ‘./app.component’;
import { HomePage } from ‘../pages/home/home’;
import { BeaconProvider } from ‘../providers/beacon-provider’

@NgModule({
declarations: [
MyApp,
HomePage
],
imports: [
IonicModule.forRoot(MyApp)
],
bootstrap: [IonicApp],
entryComponents: [
MyApp,
HomePage
],
providers: [BeaconProvider]
})
export class AppModule { }

 

Next, create a model called beacon-module.ts in a models folder:

export class BeaconModel {

uuid: string;
major: number;
minor: number;
rssi: number;

constructor(public beacon: any) {
this.uuid = beacon.uuid;
this.major = beacon.major;
this.minor = beacon.minor;
this.rssi = beacon.rssi;
}
}

(we don’t really need a model for this app but creating one makes me feel more grown up)

Now change home.ts so it looks like this:

// core stuff
import { Component } from ‘@angular/core’;
import { NavController, Platform, Events } from ‘ionic-angular’;
import { NgZone } from “@angular/core”;

// plugins
import { IBeacon } from ‘ionic-native’;

// providers
import { BeaconProvider } from ‘../../providers/beacon-provider’

// models
import { BeaconModel } from ‘../../models/beacon-model’;

@Component({
selector: ‘page-home’,
templateUrl: ‘home.html’
})
export class HomePage {

beacons: BeaconModel[] = [];
zone: any;

constructor(public navCtrl: NavController, public platform: Platform, public beaconProvider: BeaconProvider, public events: Events) {
// required for UI update
this.zone = new NgZone({ enableLongStackTrace: false });
}

ionViewDidLoad() {
this.platform.ready().then(() => {
this.beaconProvider.initialise().then((isInitialised) => {
if (isInitialised) {
this.listenToBeaconEvents();
}
});
});
}

listenToBeaconEvents() {
this.events.subscribe(‘didRangeBeaconsInRegion’, (data) => {

// update the UI with the beacon list
this.zone.run(() => {

this.beacons = [];

let beaconList = data.beacons;
beaconList.forEach((beacon) => {
let beaconObject = new BeaconModel(beacon);
this.beacons.push(beaconObject);
});

});

});
}

}

and change home.html so it looks like this:

<ion-header>
<ion-navbar>
<ion-title>
My Beacons
</ion-title>
</ion-navbar>
</ion-header>

<ion-content>
<ion-list no-lines>

<ion-item *ngFor=”let beacon of beacons”>
<ion-grid>
<ion-row>
<ion-col>Major</ion-col>
<ion-col>{{beacon.major}}</ion-col>
</ion-row>
<ion-row>
<ion-col>Minor</ion-col>
<ion-col>{{beacon.minor}}</ion-col>
</ion-row>
<ion-row>
<ion-col>RSSI</ion-col>
<ion-col>{{beacon.rssi}}</ion-col>
</ion-row>
</ion-grid>
</ion-item>

</ion-list>
</ion-content>

That’s all there is to it.  You will need to change the UUID in the beacon provider to the UUID of your own beacons.   If you run the app it should detect your beacons and display a list of them on screen (showing major, minor and RSSI):

image1-1

 

Hopefully, that will get you started!  Good luck.

 


I’m a mobile applications developer based in the UK, concentrating primarily on hybrid application development with Ionic and Ionic 2 but also with native development skills. Please visit www.crossplatformsolutions.co.uk for more information about me and how I may be able to help you with mobile application development, particularly with Ionic 2 but also with other mobile frameworks and technologies.  Thanks for visiting.

 

Calling a function in app.component.ts from another component

Someone in the Ionic Slack technical-questions channel last night asked how you call functions in app.component.ts from another Page.  I was happy to help out.

The answer (well, the way I do this anyway) is to subscribe to an event in your app.component.ts and publish that event from the page you want to call the function from.

in app.component.ts :

import { Events } from 'ionic-angular';

constructor(public events: Events) {
events.subscribe('user:login', () => {
  this.loggedIn();
});
}

loggedIn() {
console.log("logged in");
}

 

in the calling Page:

import { Events } from 'ionic-angular';
constructor(public events: Events) {
}

logIn() {
events.publish('user:login');
}

 

The calling page fires the event which is subscribed to in app.component.  See the Ionic Conference app to see an example of event handling in app.component.ts.

Objects can also be passed as parameters with events like this which can be really handy.

For more info on Events see the official Ionic documentation here.

Ionic 2 scrollToBottom issue

I have been developing a chat application recently and when I open a chat page to show a current conversation I wanted the page to automatically scroll to the bottom so the user can see the last chat message entered.  So I added a call to the scrollToBottom method in the ionViewDidEnter() lifecycle event as follows:

export class ChatPage {
  @ViewChild(Content) content: Content;

  constructor() {
  }

  ionViewDidEnter() {
    this.content.scrollToBottom();
  }
}

Unfortunately, the scroll occasionally did not execute.  It seems to work most of the time but occasionally when you opened the page it would scroll but not all the way to the bottom of the chat content.    I found that this only ever happened when the conversation contained images (photos etc that the users had uploaded as part of the chat).  Occasionally the image loading would interfere with the scroll.  I solved it simply by adding a 300 ms delay before the scrollToBottom() is executed.

 

 



I’m a mobile applications developer based in the UK, concentrating primarily on hybrid application development with Ionic and Ionic 2 but also with native development skills. Please visit www.crossplatformsolutions.co.uk for more information about me and how I may be able to help you with mobile application development, particularly with Ionic 2 but also with other mobile frameworks and technologies.  Thanks for visiting.

AngularFire2 Cannot read property ‘indexOf’ of undefined

If you are using AngularFire in your project and you are getting the error:

Cannot read property ‘indexOf’ of undefined

you may have run into the same problem I did.  I previously had Firebase installed in my project as well as AngularFire.  The new version of AngularFire (AF2) already has the Firebase module pre-installed.  So to get your project compiling you will have to remove the standalone install of Firebase.  See more here.

 

 

 



I’m a mobile applications developer based in the UK, concentrating primarily on hybrid application development with Ionic and Ionic 2 but also with native development skills. Please visit www.crossplatformsolutions.co.uk for more information about me and how I may be able to help you with mobile application development, particularly with Ionic 2 but also with other mobile frameworks and technologies.  Thanks for visiting.

Upgrading to Ionic 2.0.0-rc.4

If you are having problems installing Ionic 2.0.0-rc.4 you may want to check out this issue.  I had problems myself and could only get everything working by uninstalling cordova as well as Ionic.  So the commands I ran were:

npm uninstall -g cordova
npm uninstall -g ionic
sudo npm install -g cordova ionic

 

All works fine now….

 

 



I’m a mobile applications developer based in the UK, concentrating primarily on hybrid application development with Ionic and Ionic 2 but also with native development skills. Please visit www.crossplatformsolutions.co.uk for more information about me and how I may be able to help you with mobile application development, particularly with Ionic 2 but also with other mobile frameworks and technologies.  Thanks for visiting.

 

Ionic 2 and Azure

I’m building an Ionic 2 application at the moment using the very latest version of the framework (RC0) and one of the problems I encountered was how to include and reference the Azure Javascript SDK so that my app could use an Azure back-end.

I had some problems with this initially but finally got it to work in the end.  Two things came to my rescue.

Firstly, just at the time I was struggling with this, the Ionic team published an article “Using 3rd Party Libraries”.  This was a great help and I urge you to check it out of you are having any difficulties in using 3rd party libraries with Ionic 2.

Following that article I knew that all I had to do to use the library  was run the following command from the CLI:

npm install azure-mobile-apps-client –save

and then add:

declare module ‘azure-mobile-apps-client’;

to my declarations.d.ts file (as there was no Typings file available for the library)

I then had some problems trying to reference the library and use it within my application. Although I was working at the weekend, I reached out to Ionic’s Dan Bucholtz via twitter, expecting that maybe on the Monday I might get a reply.  That was to underestimate Super Dan and within minutes he was helping me out (thanks Dan).  Finally I managed to get things working.  And it was all very simple.

In my components/services I now have:

import azureMobileClient from ‘azure-mobile-apps-client’;

and in their constructor:

this.client = new azureMobileClient.MobileServiceClient(azureURL);
this.table = this.client.getTable(tableName);

and it now all works swimmingly well !  🙂

 

 

 

 

 

 

 

 

 

Firstly thanks to this timely article from the Ionic Framework team about using 3rd party libraries and secondly thanks to some help from Dan Bucholtz

 



I’m a mobile applications developer based in the UK, concentrating primarily on hybrid application development with Ionic and Ionic 2 but also with native development skills. Please visit www.crossplatformsolutions.co.uk for more information about me and how I may be able to help you with mobile application development, particularly with Ionic 2 but also with other mobile frameworks and technologies.  Thanks for visiting.