it might be a silly question but i am a beginner so please help me on this
i want to access a public property and change its value inside setTimeout function, Below is my code .
export class AppComponent implements OnInit {
public selectedIndex = 0;
public appPages = [
{
title: 'Trash',
url: '/folder/Trash',
icon: 'trash'
},
{
title: 'Spam',
url: '/folder/Spam',
icon: 'warning'
}
];
public showSplash = true;
constructor(
private platform: Platform,
private splashScreen: SplashScreen,
private statusBar: StatusBar,
) {
this.initializeApp();
}
initializeApp() {
this.platform.ready().then(() => {
this.statusBar.styleDefault();
this.splashScreen.hide();
setTimeout(function() {
//showSplash = false ;
console.log('set timeout worked');
}, 300);
});
}
console.log works inside setTimeout i need to change showSplash value in that block, Please help how can i do that
I think is angular and typescript code. You can use arrow function.
https://developer.mozilla.org/tr/docs/Web/JavaScript/Reference/Functions/Arrow_functions
initializeApp() {
this.platform.ready().then(() => {
this.statusBar.styleDefault();
this.splashScreen.hide();
setTimeout(() => {
this.showSplash = false;
console.log('set timeout worked');
}, 300);
});
}
Related
I have an app made in ionic 3 in which I created a provider to centralize access to LoadingController.
I have implemented the provider as the code shown below, and I thought it'd be enough to control loading indicators for everywhere in the app.
I don't know how, but sometimes there are multiple instances of the indicator being instantiated, even with the if (!this.isShowing()) verification before instantiating a new one.
Can someone help me figure out what is happening? Thanks in advance.
import { Injectable } from '#angular/core';
import { LoadingController, Loading, Platform } from 'ionic-angular';
import { BehaviorSubject } from 'rxjs';
export enum LoadingStatus {
SHOWING,
DISMISSED,
}
#Injectable()
export class LoadingProvider {
private loading: Loading = null;
private status: BehaviorSubject<LoadingStatus> = new BehaviorSubject(LoadingStatus.DISMISSED);
constructor(private loadingCtrl: LoadingController, private platform: Platform) {
this.platform.ready().then(() => {
this.status.next(LoadingStatus.DISMISSED);
});
}
async show(content?: string) {
if (!this.isShowing()) {
this.create(content);
await this.loading.present();
}
}
async dismiss() {
if (this.isShowing()) {
await this.loading.dismiss();
this.loading = null;
}
}
private create(content?: string) {
this.loading = this.loadingCtrl.create({
content: content ? content : 'Carregando...',
showBackdrop: true,
enableBackdropDismiss: true,
});
this.loading.didEnter.subscribe(() => {
if (this.status.getValue() === LoadingStatus.DISMISSED) {
this.updateLoadingStatus(LoadingStatus.SHOWING);
}
});
this.loading.didLeave.subscribe(() => {
if (this.status.getValue() === LoadingStatus.SHOWING) {
this.updateLoadingStatus(LoadingStatus.DISMISSED);
}
});
}
private async updateLoadingStatus(status: LoadingStatus) {
this.status.next(status);
}
private isShowing(): boolean {
return this.status.getValue() === LoadingStatus.SHOWING;
}
}
You're not updating your loading status until after the loader enters. If the entering is asynchronous, you've got a possibility for a race condition:
show() is called
A loader is created
show() is called again by something else
A second loader is created
The first loader enters, updating the status
I am having a bit of trouble getting a firebase key into a function for some reason. I can log the correct key from the other functions on the page but not one of them, I'm sure I'm missing something simple, but right now I am super confused as to why. Could somebody explain this to me like I'm 5?
Code:
export class HomePage {
infos = [];
ref = firebase.database().ref('infos/').orderByChild('vote');
constructor(private route: ActivatedRoute, public router: Router, public alertController: AlertController) {
this.ref.on('value', resp => {
this.infos = [];
this.infos = snapshotToArray(resp);
});
}
edit(key) {
this.router.navigate(['/edit/'+key]);
console.log(key); <----- correct key here--
}
async delete(key) {
console.log(key); <-------correct key here ---
const alert = await this.alertController.create({
header: 'Confirm!',
message: 'Are you sure want to delete this info?',
buttons: [
{
text: 'Cancel',
role: 'cancel',
cssClass: 'secondary',
handler: (blah) => {
console.log('cancel');
}
}, {
text: 'Okay',
handler: () => {
firebase.database().ref('infos/'+key).remove();
}
}
]
});
await alert.present();
}
upvote(key) {
console.log(key); <------Undefinded Key????---
};
When I try to return the key in the "Upvote" function it returns undefined, but everything else returns the correct key. What am I missing here?
Edit:
<ion-button (click)="upvote(key)">Upvote</ion-button>
<ion-button (click)="downvote(key)">Downvote</ion-button>
I'm using stripe-js.
When I call this.initializePaymentRequest() at following code,
If I call initializePaymentRequest() from first observable, canMakePayment() returns Object, and I'm able to see that google pay is supported in browser.
If I call initializePaymentRequest() from this.datas.subscribe, I'm getting null from canMakePayment() which is not true. I'm still same tab, and google pay is supported.
export class DatasComponent implements OnInit {
datas: any;
data: any;
data2s: any;
data2: any;
paymentRequest: any;
private isStripeAvailable: boolean;
constructor(
private db: AngularFirestore,
private paymentService: PaymentService
) {
// stripe js load status listener (true/false)
paymentService.stripeStatus.asObservable().subscribe(data2 => {
this.isStripeAvailable = !!data2;
if ((this.data || {}).val) {
// /******************** works here ****************
this.initializePaymentRequest();
}
});
this.slug1 = 'hello', this.slug2 = 'hi';
this.data2s = db
.collection('data2s', ref => ref
.where('slug', '==', this.slug1)
).valueChanges();
this.data2s.subscribe(data3 => {
if (data3.length) {
this.data2 = data[0];
this.datas = db
.collection('datas', ref => ref
.where('slug', '==', this.slug2)
)
.valueChanges();
this.datas.subscribe(data4 => {
if (data4.length) {
this.data = data4[0];
if (this.isStripeAvailable) {
// /*************** doesn't work here ********
this.initializePaymentRequest();
}
}
});
}
});
}
initializePaymentRequest = () => {
this.paymentRequest = this.paymentService.stripe.paymentRequest({
country: 'US',
currency: 'usd',
total: {
label: 'Sample Payment',
amount: 500,
},
requestPayerName: true,
requestPayerEmail: true,
requestPayerPhone: true,
});
this.paymentRequest.canMakePayment().then(data => {
// data is object if called from first, null if called from second
debugger;
});
}
}
Why would this happen?
Update
I can see that if I call initializePaymentRequest() within setTimeout, It is returning null too. Is there any way set timeout is breaking payment apis?
How do I use a method starting a listener on an observable which it returns in an if statement?
I'm in an Angular 5 project, I have this sort of setup in one of my components with an timeline where double click opens up a modal and you can type in the name for the item you're creating into that modal.
for the modals I used a reworked version of this answer. (I needed more up to date syntax and imports).
I've got it all nearly working now, here's my setup,
(timeline component which opens the modals) :
#Component({
selector: 'app-planning',
templateUrl: './planning.component.html',
styleUrls: ['./planning.component.css']
})
export class PlanningComponent implements AfterViewInit {
options = {
onAdd: (item, callback) => {
if(this.timeline.getCurrentTime() > item.start){
this.errorTimelineItemModal();
callback(null);
} else {
if (this.createNewTimelineItemModal()) { // <-- currently I have no return but
// having one would be meaningless
// anyways because the if wouldn't wait
// for the observable response and as a
// result, it would always assess false.
callback(item);
} else callback(null);
}
}
}
constructor(
private _element: ElementRef,
private modalService: BsModalService
) {}
ngAfterViewInit(){
this.container = this._element.nativeElement.querySelector('#timeline');
if (!this.items) {
this.items = new vis.DataSet(this.mydataset);
this.timeline = new vis.Timeline(this.container, this.items, this.groups, this.options);
}
}
createNewTimelineItemModal() {
const initialState = {
title: 'Ajouter',
multipleChoice: 'Bid',
choices: ['Bid', 'D.C.', 'Kickoff'],
accceptBtnName: 'Ajouter',
closeBtnName: 'Annuler',
};
this.bsModalRef = this.modalService.show(Modal, {initialState});
this.bsModalRef.content.onClose.subscribe(result => {
this.createItemResult = result;
console.log(JSON.stringify(result));
})
}
updateTimelineItemModal(name) {
const initialState = {
title: 'Nouveau Nom ?',
itemCurrentName: name,
accceptBtnName: 'Rennomer',
closeBtnName: 'Annuler',
};
this.bsModalRef = this.modalService.show(Modal, {initialState});
this.bsModalRef.content.onClose.subscribe(result => {
this.createItemResult = result;
console.log(JSON.stringify(result));
})
}
deleteTimelineItemModal() {
const initialState = {
title: 'Êtes-vous sûr de vouloir supprimer cet element?',
accceptBtnName: 'Supprimer',
closeBtnName: 'Annuler',
};
this.bsModalRef = this.modalService.show(Modal, {initialState});
this.bsModalRef.content.onClose.subscribe(result => {
this.createItemResult = result;
console.log(JSON.stringify(result));
})
}
errorTimelineItemModal() {
const initialState = {
title: 'Erreur',
list: ['Désolé, créer des éléments avant la date d\'aujourd\'hui est désactivé']
};
this.bsModalRef = this.modalService.show(Modal, {initialState});
this.bsModalRef.content.onClose.subscribe(result => {
this.createItemResult = result;
console.log(JSON.stringify(result));
})
}
}
(modal component) :
export class Modal implements OnInit {
onClose: Subject<Object>;
constructor(
private formBuilder: FormBuilder,
public _bsModalRef: BsModalRef) {}
ngOnInit(): void {
this.onClose = new Subject();
}
public onConfirm(): void {
this.onClose.next(true);
this._bsModalRef.hide();
}
public onCancel(): void {
this.onClose.next(false);
this._bsModalRef.hide();
}
}
As you can see I am getting an answer from validating or not the modal. I can console log it.
Now is where I'm stuck. How can I get the code execution to just halt until an observable has been received by that method so as to assess correctly within the if?
this is actually very important for the correct execution of my code because the callback(null); and callback(item); that you might have noticed are the syntaxe one must have to either finalize the item creation or prevent it.
see : http://visjs.org/docs/timeline/#Methods
I had this working with alerts but I'm trying to switch to something with more functionalities and cleaner.
If I can understand you correctly, you need to synchronize two separate events. It is usually a bad practice to do so.
Try to re-organise your code. It is an async process, so you should divide the process into sub-"transactions", that can happen separately.
Separate the logic for opening up your modal.
Wait for the user to enter the data
Process the answer from the modal.
Something like this:
createNewTimelineItemModal() {
const initialState = {
...
this.bsModalRef.content.onClose.subscribe(result => {
this.createItemResult = result;
this.continueToCreateItem(result);
});
}
private continueToCreateItem(result: any){
<insert your code here>
}
Or other solution can be to return observable objects and hande it within the onAdd
options = {
onAdd: (item, callback) => {
...
this.createNewTimelineItemModal().subscribe(result => {
if(result is something){
callback(item);
} else callback(null);
}
}
}
}
To "halt" the process is a pretty bad practice, but can be achived with Promise object.
this.myPromiseReturningMethod(params).then(() => {
but this will block all your application for the time being (with the user being unable to do anything) so I recommend to alter the structure instead.
I'm trying to do the following:
I have an object like this:
let demo_obj = {
park_name: "Park #01",
assets:
[
{
id: 1,
name: "Test #01"
},
{
id: 2,
name: "Test #02"
},
{
id: 3,
name: "Test #03"
}
],
}
A page loops through the assets objects with an *ngFor listing each asset as a list item.
<ion-list>
<ion-item *ngFor="let asset of demo_obj.assets" (click)="gotoAsset(asset)">
<ion-avatar item-left>
<img [src]="asset.cover_image_public_path">
</ion-avatar>
<h2>{{asset.nome}}</h2>
</ion-item>
</ion-list>
When the user clicks the button the following code gets executed:
gotoAsset(asset) {
this.navCtrl.push(AssetPage, { asset: asset });
}
This pushes a page displaying the asset details.
When the new page gets on the stack it fetches the asset from the nav params and it makes a backup copy of it stringifying it.
ngOnInit() {
this.asset = this.navParams.get("asset");
this.original_asset = JSON.stringify(this.asset);
}
The user here, can change several properties on the asset. But when he tries to go back without saving changes the app asks the user with a confirmation alert if he is sure to exit loosing changes:
ionViewCanLeave(): boolean {
if (JSON.stringify(this.asset) === this.original_asset) {
return true;
}
if (this.can_go_back) {
return true;
}
this.askForLeaveConfirmation();
return false;
}
private askForLeaveConfirmation() {
let confirm = this.alertCtrl.create({
title: 'Do you confirm?',
message: 'Leaving the page without saving, will reset the resource at its original state. Are you sure?',
buttons: [
{
text: 'Disagree',
handler: () => {
console.log('Disagree clicked');
}
},
{
text: 'Agree',
handler: () => {
this.asset = JSON.parse(this.original_asset);
this.can_go_back = true;
this.navCtrl.pop();
console.log('Agree clicked');
}
}
]
});
confirm.present();
}
At this point if he chooses Agree, the resource gets reseted. All works fine, in fact you can see in a small fraction of time the resource getting reseted before the animation start.
The weird thing happens when the this.navCtrl.pop() gets called. The previous page keeps the changes even if the resource were reseted.
I thought that it's in some way linked with the previous page since I'm continuously passing the same object between the pages. Where am I doing wrong?
I've managed to solve it with a workaround.
Creating a service called SyncService, which will take care to backup the given object and provides a copy of it.
Then that copy can be fetched back with the proper method.
Keeping a backed up copy of the object allows me to perform a check on the ionViewCanLeave() method and on the constructor of the parent page.
The core key was using Events. When the user confirms the reset of the object in the alert, an event is published, and the constructor of the parent page subscribes at that event.
Inside the subscription, we'll loop all the current assets searching for the same ID, then if found the entire object gets overwritten.
SyncService class:
import { Injectable } from "#angular/core";
#Injectable()
export class SyncService {
public resource;
public workOnCopy(resource: any, res_type: string): boolean {
sessionStorage.setItem("astrid_mobile_tmp_" + res_type + "_bak", JSON.stringify(resource));
this.resource = resource;
return this.resource;
}
public getBackedUpResource(res_type: string, parse: boolean = true): any {
let res = sessionStorage.getItem("astrid_mobile_tmp_" + res_type + "_bak");
if (!res) {
return false;
}
if (parse) {
JSON.parse(res);
}
return res;
}
}
The child page (AssetPage):
#Component({
selector: 'page-asset',
templateUrl: 'asset.html'
})
export class AssetPage implements OnInit {
public asset;
private can_go_back: boolean = false;
constructor(
private navCtrl: NavController,
private navParams: NavParams,
private alertCtrl: AlertController,
private Events: Events,
private SyncService: SyncService,
) {
this.asset = this.SyncService.workOnCopy(this.navParams.get("asset"), "asset");
}
ionViewCanLeave(): boolean {
if (JSON.stringify(this.asset) === this.SyncService.getBackedUpResource("asset")) {
return true;
}
if (this.can_go_back) {
return true;
}
this.askForLeaveConfirmation();
return false;
}
private askForLeaveConfirmation() {
let confirm = this.alertCtrl.create({
title: 'Do you confirm?',
message: 'Leaving the page without saving, will reset the resource at its original state. Are you sure?',
buttons: [
{
text: 'Disagree',
handler: () => {
console.log('Disagree clicked');
}
},
{
text: 'Agree',
handler: () => {
console.log("Event fired");
this.Events.publish("asset:reseted", this.SyncService.getBackedUpResource("asset"));
this.can_go_back = true;
this.navCtrl.pop();
console.log('Agree clicked');
}
}
]
});
confirm.present();
}
}
The parent page (ParkPage) constructor:
constructor(
private navCtrl: NavController,
private navParams: NavParams,
private Events: Events,
) {
this.park = this.navParams.get("park");
this.Events.subscribe("asset:reseted", (bak_asset) => {
let asset = JSON.parse(bak_asset);
for (let i = 0; i < this.park.assets.length; i++) {
if (this.park.assets[i].id == asset.id) {
this.park.assets[i] = asset;
}
}
});
}