Create callback in Ionic 2 for Observable.timer - angularjs

I have a Observable.timer function that creates a countdown and I want to call a specific function called endTimer() when the timer has ended inside my #Component without using setTimeout(). I can check the value of counter == 0 in the view but how do I check in the #Component
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams, ModalController } from 'ionic-angular';
import { Storage } from '#ionic/storage';
import { Observable } from 'rxjs/Observable'
import 'rxjs/add/observable/timer'
import 'rxjs/add/operator/map'
import 'rxjs/add/operator/take'
//imported pipes
import {FormatTimer} from '../../pipes/formattimer';
#Component({
selector: 'page-livegame',
templateUrl: 'livegame.html',
pipes: [FormatTimer]
})
export class LivegamePage {
gamesData: any;
countDown: any;
counter = 1*60;
tick = 1000;
constructor(public modalCtrl: ModalController, public navCtrl: NavController, public navParams: NavParams, public storage: Storage) {
//setTimeout(function(){ endTimer(); }, 300000);
}
ionViewDidLoad() {
this.getCurrentGame();
}
ngOnInit() {
this.countDown = Observable.timer(0, this.tick)
.take(this.counter)
.map(() => --this.counter);
}
endTimer() {
console.log('ended');
}
}
<div *ngIf="counter == 0">the timer has ended </div>

Are you subscribing to it somewhere? on the template with 'async' pipe or so? If not, you have to subscribe to it. For the same reason, the variable 'this.counter' will never reach 0.
If you are already subscribing to it on the template with async Pipe, you can use the operator finally:
ngOnInit() {
this.countDown = Observable.timer(0, this.tick)
.take(this.counter)
.map(() => --this.counter)
.finally(() => this.endTimer())
}
if you are not subscribing to it on the template, you can subscribe like this:
ngOnInit() {
this.countDown = Observable.timer(0, this.tick)
.take(this.counter)
.map(() => --this.counter)
.finally(() => this.endTimer())
.share() // Avoid multiple side effect if multiple subscribers
this.countDown.subscribe(() => { /* do nothing */})
}
Hope this helps.

Related

Trying to get a unique list of objects from an array in angular

Have an observable being returned from my service.ts as shown here:
import { Injectable, ErrorHandler } from '#angular/core';
import { HttpClient, HttpErrorResponse, HttpHeaders } from
'#angular/common/http'
import { Observable } from 'rxjs';
import { catchError, tap } from 'rxjs/operators';
import { PokeResponse } from '../PokeResponse';
#Injectable({
providedIn: 'root'
})
export class PokeApiService {
private url = "https://pokemon-go1.p.rapidapi.com/pokemon_stats.json";
constructor(private http:HttpClient) { }
getPokeData(): Observable<PokeResponse> {
return this.http.get<PokeResponse>(this.url,
{
headers : new HttpHeaders({
'x-rapidapi-key': 'a6cef4cbcamsh05b29346394d4a4p1bafacjsn2a92406ac103'
})
})
.pipe(
tap(data => console.log('Pokedata/Error' + JSON.stringify(data))
),
catchError(this.handleError)
);
}
private handleError(err:HttpErrorResponse) {
console.log('PokemonService: ' + err.message);
return Observable.throw(err.message);
}
}
This is my response:
export interface PokeResponse{
list:results[];
results:{
pokemon_id:number;
pokemon_name:string;
base_attack:number;
base_defense:number;
base_stamina:number;
}[];
}
export interface results{
pokemon_id:number;
pokemon_name:string;
base_attack:number;
base_defense:number;
base_stamina:number;
}
export class Pokemon implements results{
pokemon_id:number;
pokemon_name:string;
base_attack:number;
base_defense:number;
base_stamina:number;
constructor(_id:number, _name:string, _atk:number, _def:number, _stam:number) {
_id = this.pokemon_id;
_name = this.pokemon_name;
_atk = this.base_attack;
_def = this.base_defense;
_stam = this.base_stamina;
}
}
And this is my component.ts:
import { Component, OnInit } from '#angular/core';
import { PokeApiService } from 'src/app/services/poke-api.service';
import { PokeResponse, results, Pokemon } from 'src/app/PokeResponse';
import { Observable } from 'rxjs';
#Component({
selector: 'app-list-pokemon',
templateUrl: './list-pokemon.component.html',
styleUrls: ['./list-pokemon.component.css']
})
export class ListPokemonComponent implements OnInit {
constructor(private pokeService: PokeApiService) { }
ngOnInit(): void {
this.getPokeDetails();
}
pokeData:PokeResponse;
errorMessage:any;
pokeArray:results;
getPokeDetails() : boolean {
this.pokeService.getPokeData().subscribe(
pokeData => {
this.pokeData=pokeData;
console.table(pokeData);
},
error => this.errorMessage = <any>error
);
return false;
}
}
In my console I'm getting back a console.table of my observable like this
I'm trying to filter out the names of Pokemon which are the same as others, which could also be achieved by just filtering out any of the pokemon_ids as all the stats match regardless of the type.
So far I've tried:
console.log(this.pokeArray);,
using [...Set()], forEach(), and Array.from()
Any help or suggestions on how I can make this question any clearer would be greatly appreciated.
Try this, using filter:
// list-pokemon.component.ts
export class ListPokemonComponent implements OnInit {
uniqueListPoke = [];
flags = {};
constructor(private pokeService: PokeApiService) { }
ngOnInit(): void {
this.getPokeDetails();
}
pokeData:PokeResponse;
errorMessage:any;
pokeArray:results;
getPokeDetails() : boolean {
this.pokeService.getPokeData().subscribe(
pokeData => {
this.uniqueListPoke = pokeData.filter((entry) => {
if (this.flags[entry.pokemon_name]) {
// console.log('flags', false);
return false;
}
this.flags[entry.pokemon_name] = true;
return true;
});
console.log(JSON.stringify(this.uniqueListPoke));
},
error => this.errorMessage = <any>error
);
return false;
}
}
The working example:
https://stackblitz.com/edit/angular-distinct-poke?file=src/app/hello.component.ts

IONIC 2 - Read OTP from sms and login to dashbord

I am working on auto-verification otp ionic2. In that, SMS permission is applied and if device mobile number exists or read otp from sms then it switch directly to dashboard page for that i use a function called watchSMS() as shown in angular code but it gives an error (SMS is not defined).
Any help will be highly appreciated.
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams, AlertController, Platform, LoadingController, ToastController, ViewController } from 'ionic-angular';
import { FormGroup, FormBuilder, FormControl, Validators } from '#angular/forms';
import { Network } from '#ionic-native/network';
import { CrudHttpProvider } from '../../providers/crud-http/crud-http';
import { SQLite, SQLiteObject } from '#ionic-native/sqlite';
import { ConstantVariable } from '../../app/constant-variable';
import { HomePage } from '../home/home';
// import { SMS } from '#ionic-native/sms';
import { Sim } from "#ionic-native/sim";
import { AndroidPermissions } from '#ionic-native/android-permissions';
declare var window: any;
declare var SMS:any;
#IonicPage()
#Component({
selector: 'page-login',
templateUrl: 'login.html',
})
export class LoginPage {
public myForm: FormGroup;
public mobile: any;
public db_name: any;
public url_play_store: any;
public buttonDisabled: any;
public studname: any;
public tuition_name: any;
public prn: any;
public otp: any;
public api_key: any;
public userinfo:any;
public stored_mb:any;
public simInfo: any;
public cards: any;
public phoneNumber: any;
constructor(public navCtrl: NavController, public navParams: NavParams, public formBuilder: FormBuilder, public alertCtrl: AlertController, public platform: Platform, public network: Network, public loadingCtrl: LoadingController, public toastCtrl: ToastController, public crudHttpProvider: CrudHttpProvider, public sqlite: SQLite, public viewCtrl: ViewController, public sim: Sim, public androidPermissions: AndroidPermissions) {
this.db_name = ConstantVariable.db_name;
this.myForm = this.formBuilder.group({
'mobile': ['', [Validators.required]],
});
}
ionViewDidLoad() {
console.log('ionViewDidLoad LoginPage');
}
login() {
let post_data = { 'api_url': 'checkMobileNumber', "post": {'mobile': this.mobile} };
this.crudHttpProvider.callToCrudPost(post_data)
.then(data => {
let res = data;
if (res['status'] == 100) {
this.studname = res['data'].studname;
this.tuition_name = res['data'].tuition_name;
this.prn = res['data'].prn;
this.otp = res['data'].otp;
this.api_key = res['data'].api_key;
this.navCtrl.push('RegisterPage',{
"studname": this.studname,
"tuition_name": this.tuition_name,
"prn": this.prn,
"otp": this.otp,
"api_key": this.api_key
});
this.viewCtrl.dismiss();
this.checkPermission();
}
});
}
checkPermission() {
this.androidPermissions.checkPermission
(this.androidPermissions.PERMISSION.READ_SMS).then(
success => {
//if permission granted
this.watchSMS();
},
err =>{
this.androidPermissions.requestPermission
(this.androidPermissions.PERMISSION.READ_SMS).
then(success=>{
this.watchSMS();
},
err=>{
alert("cancelled")
});
});
this.androidPermissions.requestPermissions
([this.androidPermissions.PERMISSION.READ_SMS]);
}
watchSMS() {
if(window.SMS) window.SMS.startWatch(function(){
console.log('Succeed to start watching SMS');
this.navCtrl.push('DashboardPage');
let toast = this.toastCtrl.create({
message: "Succeed to start watching SMS.",
duration: 4000
});
toast.present();
document.addEventListener('onSMSArrive', this.smsArived);
}, function(){
console.log('failed to start watching SMS');
let toast = this.toastCtrl.create({
message: "failed to start watching SMS.",
duration: 4000
});
toast.present();
});
}
stopWatchSMS() {
if(window.SMS) window.SMS.stopWatch(function(){
console.log('Succeed to stop watching SMS');
}, function(){
console.log('failed to stop watching SMS');
});
}
smsArived = (result: any) => {
console.log("SMS DATA 2" + result);
let toast = this.toastCtrl.create({
message: "RESULT " + result,
duration: 4000
});
toast.present();
this.stopWatchSMS();
}
}
I implemented auto verify OTP via this tutorial. I had no trouble.
SMS is called inside of platform.ready() and it says : "Make sure always use method in Ionic Framework inside the platform.ready() else it will not work.". Finally, I think, you dont need to use window.
ionViewDidEnter()
{
this.platform.ready().then((readySource) => {
if(SMS) SMS.startWatch(()=>{
console.log('watching started');
}, Error=>{
console.log('failed to start watching');
});
document.addEventListener('onSMSArrive', (e:any)=>{
var sms = e.data;
console.log(sms);
});
});
}

Angular2 Array objects

I would like to add in an array of objects, but it does not work: Can not set property '0' of undefined
I try to put in this.positions [0] = the PositionMap object
I have decreased the size of the code for better readability, but the rest works
Here is my code:
import { Component, ViewChild, ElementRef } from '#angular/core';
import { NavController } from 'ionic-angular';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { Geolocation } from 'ionic-native';
declare var google;
#Component({
selector: 'page-home',
templateUrl: 'home.html'
})
export class HomePage {
positions: PositionMap[] = [];
#ViewChild('map') mapElement: ElementRef;
constructor(public navCtrl: NavController) {
this.positions = [];
}
ionViewDidLoad(){
this.loadMap();
this.autocomplete();
}
autocomplete() {
autocompleteDepart.addListener('place_changed', function() {
var place = autocompleteDepart.getPlace();
let tmpPosition = new PositionMap(place.geometry.location.lat(), place.geometry.location.lng());
this.positions[0] = (tmpPosition);
return;
});
}
}
export class PositionMap {
latitude: number;
longitude: number;
constructor(_latitude: number, _longitude: number) {
this.latitude = _latitude;
this.longitude = _longitude;
}
}
My table is well declared in the class and in the constructor, but yet is not known in the function.
You're using this from a callback function. This callback function must first be bound to this. The easiest way is to use an arrow function:
autocompleteDepart.addListener('place_changed', () => {
...
});

why subscribe function is not called in angular 2?

I am using observable in angular .Actually my issue when I click button my subscribe function not called why ?
as per documentation subscribe function will call when we call next function
https://plnkr.co/edit/83NaHoVaxiXAeUFoaEmb?p=preview
constructor() {
this.data = new Observable(observer => this.dataObserver = observer);
this.data.subscribe(value => {
console.log('+++')
console.log(value)
})
}
hndle(){
this.name.push({name:"navee"});
this.dataObserver.next(this.name);
}
here is documentation
http://reactivex.io/rxjs/manual/tutorial.html
On basis of Volodymyr Bilyachat suggestion i have modified your code. its working now plz check. Problem was in your way of using dataObserver
//our root app component
import {Component, NgModule} from '#angular/core'
import {BrowserModule} from '#angular/platform-browser'
import 'rxjs/Rx';
import {Observable} from 'rxjs/Observable';
#Component({
selector: 'my-app',
template: `
<div>
<ul>
<li *ngFor ="let n of name">{{n.name}}</li>
</ul>
<button (click)="hndle()">heelo</button>
</div>
`,
})
export class App {
private data:Observable;
private dataObserver:Observer;
name:string;
name[];
constructor() {
this.dataObserver = new Observable(observer => this.dataObserver = observer);
this.dataObserver.subscribe(value => {
console.log('+++')
console.log(value)
});
}
hndle(){
this.name.push({name:"navee"});
this.dataObserver.next(this.name);
}
}
#NgModule({
imports: [ BrowserModule ],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}
link https://plnkr.co/edit/PO80y2udrOhsVq4QQXc5?p=preview
I believe you are subscribing to the observable 2 times. You should be able to fix it by adding .share()
constructor() {
this.data = new Observable(observer => this.dataObserver = observer).share();
this.data.subscribe(value => {
console.log('+++')
console.log(value)
})
}
hndle(){
this.name.push({name:"navee"});
this.dataObserver.next(this.name);
}
In your case, it's better to use this solution:
constructor() {
this.data = new Subject();
this.data.subscribe(value => {
console.log('+++');
console.log(value);
});
}
hndle() { // TYPO: Probably it was meant to be handle
this.name.push({
name: 'navee'
});
this.data.next(this.name);
}
Don't forget to add:
import { Subject } from 'rxjs/Subject'
Working example:
https://plnkr.co/edit/zB8FHTVEm2QUHiEAYuQB?p=preview

Detect if Observable not found using combineLatest

I need to modify my code where loading detail category will first look whether it is not already loaded in the statement, and if not then detail loads. Thanks for help!
Constructor of CategoryProvider:
private _obServers = {
'categoryList': undefined,
'category': undefined,
'idCategory': new Subject<Number>()
};
constructor(){
this.categories = new Observable(observer => this._obServers.categoryList = observer).share();
this._categoryObservable = this.categories
.combineLatest(this._obServers.idCategory, (categories, idCategory) => {
return categories.filter(category => category.id === idCategory)[0];
})
.distinctUntilChanged((oldCategory, newCategory) => {
return oldCategory.id === newCategory.id;
});
}
CategoryList:
loadCategories(search?:string):void{
this._http
.get('/services/category/list?search=' + search)
.map(res => res.json())
.subscribe(data => {
this._obServers.categoryList.next(this.createCategoryEntities(data));
});
}
CategoryDetail:
loadCategory(categoryId:number){
this._obServers.idCategory.next(categoryId);
//If category not loaded I need to load it
}
I have followed this way https://github.com/JonatanSCS/Angular-2-Tutorial/blob/master/node_modules/rxjs/src/add/observable/combineLatest.ts
import { Component, Injectable, Inject, provide } from '#angular/core';
import { HTTP_PROVIDERS } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import { combineLatestStatic } from 'rxjs/operator/combineLatest.js';
import { MessageApi } from '../providers/lb-service/lb-services.provider'
import { EstimatesService } from './estimates.service';
#Component({
pipes: [TranslatePipe],
})
#Injectable()
export class InvoiceService {
constructor(private _message:MessageApi,
#Inject(EstimatesService) _estimates:EstimatesService) {
this._message = _message;
this._estimates = _estimates;
Observable.combineLatest = combineLatestStatic;
declare module 'rxjs/Observable' {
namespace Observable {
export let combineLatest: typeof combineLatestStatic;
}
}
Observable.combineLatest(
this._estimates.getEstimates(),
this._message.findOne({
where: {
moduleTag: 'monthlyStat',
'dynamic.date': "2016-07-01" //new Date
},
fields: {
dynamic: true
}
}),this._message.findOne({
where: {
moduleTag: 'areaPriceSE1',
'dynamic.date': ''
},
fields: {
dynamic: true
}
})
).subscribe(res => console.log("observable", res));

Resources