I have an ion-list with a ngFor loop. Here is the html:
<ion-list>
<ion-item *ngFor="let kiosk of kiosks" (click)="goToKioskProfilePage(kiosk)">
{{kiosk.name}}
</ion-item>
</ion-list>
And here is the constructor:
kiosks: any;
constructor(public navCtrl: NavController, navParams: NavParams, public locationTracker: LocationTracker, public api: ApiService, public zone: NgZone) {
this.kiosks = [];
this.zone.run(() => {
this.locationTracker.getGeolocation().then(location => {
this.api.getClosestKiosks(location, constants.AMOUNT_OF_CLOSEST_KIOSKS).then(
data => {
console.log("ready");
this.kiosks = data['data'];
},
err => {
console.log(err);
}
);
});
});
}
The console logs "ready" but the list does not update. I already tried it with NgZone but it's still not working. Only when I open the sidemenu the list updates, but not before. Someone know how to fix that?
I've encountered a similar issue before. What fixed it for me was putting only the variable update/assignment in between zone.run(), as it's after the promise resolves that you want to update the values.
Try:
this.locationTracker.getGeolocation().then(location => {
this.api.getClosestKiosks(location, constants.AMOUNT_OF_CLOSEST_KIOSKS).then(
data => {
console.log("ready");
this.zone.run(() => {
this.kiosks = data['data'];
});
},
err => {
console.log(err);
}
);
});
Related
I am very new to ionic and angular.
Anyway, I am trying to following an tutorial to create a notes app using ionic4 https://www.joshmorony.com/building-a-notepad-application-from-scratch-with-ionic/.
So, I follow the instruction. Everything is ok except that the view doesn't updated when I add new note. The code is as follow:
Note services:
import { Injectable } from '#angular/core';
import { Storage } from '#ionic/storage';
import { Note } from '../interfaces/note';
#Injectable({
providedIn: 'root'
})
export class NotesService {
public notes: Note[] = [];
public loaded: boolean = false;
constructor(private storage: Storage) {
}
load(): Promise<boolean> {
// Return a promise so that we know when this operation has completed
return new Promise((resolve) => {
// Get the notes that were saved into storage
this.storage.get('notes').then((notes) => {
// Only set this.notes to the returned value if there were values stored
if (notes != null) {
this.notes = notes;
}
// This allows us to check if the data has been loaded in or not
this.loaded = true;
resolve(true);
});
});
}
save(): void {
// Save the current array of notes to storage
this.storage.set('notes', this.notes);
}
getNote(id): Note {
// Return the note that has an id matching the id passed in
return this.notes.find(note => note.id === id);
}
createNote(title): Promise<boolean> {
return new Promise((resolve) => {
// Create a unique id that is one larger than the current largest id
let id = Math.max(...this.notes.map(note => parseInt(note.id)), 0) + 1;
this.notes.push({
id: id.toString(),
title: title,
content: ''
});
this.save();
console.log('Service Log ' + this.notes);
resolve(true);
});
}
}
The HTML code:
<ion-header>
<ion-toolbar color="primary">
<ion-title>Notes</ion-title>
<ion-buttons slot="end">
<ion-button (click)="addNote()">
<ion-icon slot="icon-only" name="clipboard"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item button detail *ngFor="let note of notesService.notes" [href]="'/notes/' + note.id" routerDirection="forward">
<ion-label>{{ note.title }}</ion-label>
</ion-item>
</ion-list>
</ion-content>
I've followed the same tutorial and got the same issue. The issue is because of something very interesting and powerful called Zones.
The idea is that you'd need to let Angular know that the array with the notes has changed, by doing something like this:
// Angular
import { Component, NgZone } from '#angular/core';
// Ionic
import { NavController, AlertController } from '#ionic/angular';
// Services
import { NotesService } from '../services/notes.service';
import { AlertOptions } from '#ionic/core';
#Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
})
export class HomePage {
constructor(
private ngZone: NgZone, // Add this in the constructor
private navCtrl: NavController,
private alertCtrl: AlertController,
private notesService: NotesService,
) { }
ngOnInit() {
this.notesService.load();
}
addNote() {
const alertOptions: AlertOptions = {
header: 'New Note',
message: 'What should the title of this note be?',
inputs: [
{
type: 'text',
name: 'title'
}
],
buttons: [
{
text: 'Cancel'
},
{
text: 'Save',
handler: (data) => {
// Create the note inside a Zone so that Angular knows
// that something has changed and the view should be updated
this.ngZone.run(() => {
this.notesService.createNote(data.title);
});
}
}
]
};
this.alertCtrl
.create(alertOptions)
.then((alert) => {
alert.present();
});
}
}
my ionic2 app get data from cordova plugin,but input's value had not update automatic.thank you first.
my demo code:
import { Component } from '#angular/core';
import { NavController, Events } from 'ionic-angular';
declare var AMapPlugin;
#Component({
selector: 'page-hello-ionic',
templateUrl: 'hello-ionic.html'
})
export class HelloIonicPage {
gps: string = '';
_distance: number = 0;
constructor(public navCtrl: NavController, private event: Events) {
}
getDistance() {
return new Promise<number>((resolve, reject) => {
AMapPlugin.getDistance(data => {
resolve(data);
});
});
}
location() {
AMapPlugin.startLocation(10, success => {
let lo = { lat: success.latitude, lng: success.longitude };
this.gps = JSON.stringify(lo);
}, error => {
});
}
start() {
this.getDistance().then(data => {
this._distance = data;
});
}
}
location will trigger gps 10 seconds per time,but html value not realtime modify.
<ion-item>
<ion-label>distance</ion-label>
<ion-input type="text" [(ngModel)]="_distance" readonly></ion-input>
</ion-item>
<ion-item>
<ion-label>current</ion-label>
<ion-input type="text" [(ngModel)]="gps | async" readonly></ion-input>
</ion-item>
You have to detect the change manually. Use ngZone or another change detector in angular, check this answer
I have a page which is listening to beacon events. I want to show a popup when the beacon is detected. I have the following code:
home.ts
export class HomePage {
beacon_found: boolean;
constructor(public navCtrl: NavController, public events: Events, public ibeacon: IBeacon) {
this.ibeacon.requestAlwaysAuthorization();
let delegate = this.ibeacon.Delegate();
let region = this.ibeacon.BeaconRegion('ibirapuera','B9407F30-F5F8-466E-AFF9-25556B57FE6D');
this.ibeacon.startMonitoringForRegion(region)
.then(
() => console.log('Native layer recieved the request to monitoring'),
error => console.error('Native layer failed to begin monitoring: ', error)
)
delegate.didStartMonitoringForRegion()
.subscribe(
(data) => console.log("Started monitoring beacons", data)
)
delegate.didEnterRegion()
.subscribe(
(data) => {
this.beacon_found = true;
}
)
delegate.didExitRegion()
.subscribe(
(data) => {
console.log("Exit Region");
}
)
}
}
home.html
<div class="card-beacon" *ngIf="beacon_found">
</div>
The problem is that when I detect the beacon, the div is not being displayed. I read something about async databiding, but I have no idea how to do it.
Does anybody know how to solve it, please?
Thanks in advance.
I got it working using ChangeDetectorRef
import { Component, Input, ChangeDetectorRef } from '#angular/core';
export class HomePage {
beacon_found: boolean;
constructor(public navCtrl: NavController, public events: Events, public cdr: ChangeDetectorRef) {
events.subscribe('beacon:detected',(data) => {
this.beacon_found = true;
cdr.detectChanges();
})
events.subscribe('beacon:undetected',(data) => {
this.beacon_found = false;
cdr.detectChanges();
})
}
}
This is where you should use an rxjs BehaviorSubject:
beaconFoundSubject : BehaviorSubject<boolean> = new BehaviorSubject(false);
In your constructor (where NgZone may need to be injected and used to notify beaconFoundSubject):
constructor(private ngzone: NgZone) {
....
delegate.didEnterRegion()
.subscribe(
(data) => {
this.ngzone.run(() => this.beaconFoundSubject.next(true));
}
)
And then in your template:
<div class="card-beacon" *ngIf="beaconFoundSubject | async">
</div>
.Am working on an app using ionic 2. I have my data in couchdb about 9 differents items. How can i display each data from the database corresponding to the item clicked. The items are in a list?
This s the provider diagnosis.ts
import { Injectable } from '#angular/core';
import PouchDB from 'pouchdb';
#Injectable()
export class Diagnosis{
data: any;
db: any;
remote: any;
constructor() {
this.db = new PouchDB('ezy');
this.remote = 'http://localhost:5984/ezy';
let options = {
live: true,
retry: true,
continuous: true
};
this.db.sync(this.remote, options);
}
getTodos() {
if (this.data) {
return Promise.resolve(this.data);
}
return new Promise(resolve => {
this.db.allDocs({
include_docs: true
}).then((result) => {
this.data = [];
let docs = result.rows.map((row) => {
this.data.push(row.doc);
});
resolve(this.data);
this.db.changes({live: true, since: 'now', include_docs: true}).on('change', (change) => {
this.handleChange(change);
});
}).catch((error) => {
console.log(error);
});
});
}
createTodo(todo){
this.db.post(todo);
}
getTodo(todo){
this.db.get(todo);
}
updateTodo(todo){
this.db.put(todo).catch((err) => {
console.log(err);
});
}
deleteTodo(todo){
this.db.remove(todo).catch((err) => {
console.log(err);
});
}
handleChange(change){
let changedDoc = null;
let changedIndex = null;
this.data.forEach((doc, index) => {
if(doc._id === change.id){
changedDoc = doc;
changedIndex = index;
}
});
//A document was deleted
if(change.deleted){
this.data.splice(changedIndex, 1);
}
else {
//A document was updated
if(changedDoc){
this.data[changedIndex] = change.doc;
}
//A document was added
else {
this.data.push(change.doc);
}
}
}
}
provider usage is as
.ts
constructor(public navCtrl: NavController, public navParams: NavParams,public diagnose:Diagnosis) {
this.pet = "puppies";
console.log(diagnose.getTodos());
diagnose.getTodos().then(result=>{
this.pest = result;
console.log(result);
})
}
.html
<ion-grid>
<ion-row>
<ion-col *ngFor="let val of pest" width-100>
<ion-list>
<ion-item><h3>{{val.name}}</h3></ion-item>
<ion-item><h3>{{val.description}}</h3></ion-item>
<ion-item><h3>{{val.disease}}</h3></ion-item>
<ion-item><h3>{{val.prevention}}</h3></ion-item>
</ion-list>
</ion-col>
</ion-row>
</ion-grid>
I'm not sure what you want.. If you need to display clicked item what you have to do is add (click) event where you call a function sending the item (Again, I don't know if you are asking this)...
<ion-item (click)="viewItem(val.prevention)"><h3>{{val.prevention}}</h3></ion-item>
I have created an Ionic2 App using cordova FileTransferplugin, i am downloading remote server file.
Everything is working perfectly, but when I try to update template while fileTransfer.OnProgress event, the template is not updating the % downloaded.
Pleas see this video for my problem.
Ionic_youtube_link
My Code is, logic is in downloadFile function
import {Component, AfterViewInit, ViewChild} from '#angular/core';
import {NavController, NavParams, ViewController, Nav} from 'ionic-angular';
import {Page, Platform} from 'ionic-angular';
import {File, Device, Cordova, Transfer} from 'ionic-native';
import { SafeResourceUrl, DomSanitizationService } from '#angular/platform-browser';
#Component({
templateUrl: 'build/pages/video-download-modal/video-download-modal.html',
providers: [File, Transfer]
})
export class VideoDownloadModal {
selectedItem: any;
#ViewChild(Nav) nav: Nav;
videoPathUrl: string;
isPlatformReady: boolean;
platformName: string;
directoryName: string = "socialAppDir";
totalVideoSize:number;
totalDownloaded:number;
totalPercent:string = "0%";
constructor(public navCtrl: NavController, navParams: NavParams, private _viewController: ViewController, platform: Platform, private transfer:Transfer) {
// If we navigated to this page, we will have an item available as a nav param
if (platform.is('core')) {//if on dekstop
console.log('dektop');
} else {
this.videoPathUrl = navParams.get('videoPath');
console.log(this.videoPathUrl);
platform.ready().then((readySource) => {
this.isPlatformReady = true;
console.log('ready 1234');
const fs: string = cordova.file.externalRootDirectory;
console.log(cordova.file.dataDirectory);
this.platformName = Device.device.platform;
File.checkDir(cordova.file.externalDataDirectory, this.directoryName).then(() => {
console.log('directory exists');
this.downloadFile();
}, (error) => {
console.log('directory not exists');
this.createDirectory();
})
})
}
}
dismiss() {
let data = { 'foo': 'bar' };
this._viewController.dismiss(data);
}
createDirectory():void{
File.createDir(cordova.file.externalDataDirectory, this.directoryName, true).then(() => {
console.log("created externalDataDirectory");
this.downloadFile();
},(error) => {
console.log('some error happen')
})
}
downloadFile = () => {
console.log(this);
let fileName: string = this.videoPathUrl.split("/").pop();
let targetPath = cordova.file.externalDataDirectory + this.directoryName + "/" + fileName;
console.log(targetPath);
this.transfer.download(this.videoPathUrl, targetPath, true, {}).then(() => {
console.log('video downloaded')
}, (error) => {
console.log(error)
})
this.transfer.onProgress((progress) => {
console.log(this);
this.totalVideoSize = progress.total;
this.totalDownloaded = progress.loaded;
this.totalPercent = ((progress.loaded / progress.total) * 100).toString();
console.log(this.totalPercent);
})
}
ionViewDidEnter() {
console.log("enter login1");
}
}
And HTML is
<ion-content>
<div id="modalContainer" class="abd">
<ion-spinner></ion-spinner>
<br />
{{**totalPercent**}}
<br />
<button dnager block (click)="dismiss()">Exit</button>
</div>
</ion-content>
The totalPercent value either has 0 or 100.But not updating.
Please help.
This is because the totalPercent of this inside the handler was set to the global Window object instead of the object itself.
I have finally got it to work
import { NgZone } from '#angular/core';
fileTransfer.onProgress((progressEvent: ProgressEvent) => {
this.ngZone.run(() => {
if (progressEvent.lengthComputable) {
let lp = progressEvent.loaded / progressEvent.total * 100;
this.loadingPercent = Math.round(lp * 100) / 100;
}
});
})