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>
Related
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);
});
}
can any one please tell me why I can not loop through this array?
In ngOnInit, everything works fine. I got an array that I successfully display in the template.
But in ngAfterViewInit, console.log show the array but when looping through with "for of" or "forEach", nothing works.
import { JobsService } from '../jobs.service';
import {Job} from '../models/Job';
#Component({
selector: 'app-job',
templateUrl: 'job.component.html'
})
export class JobComponent implements OnInit, AfterViewInit {
title = 'Job';
jobs: Job[] = [];
InProcess = '';
CurrentPartner = '';
ShowProcess = false;
sended = '';
constructor(private jobsService: JobsService) {
}
ngOnInit() {
this.jobs = this.jobsService.getJobs();
}
ngAfterViewInit() {
console.log(this.jobs); // Show the array
// Nothing happened when looping through the array
this.jobs.forEach((oneJob) => {
console.log(oneJob);
});
}
}
Screenshot of the console in Google Chrome
The content of the service:
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import {Job} from './models/Job';
interface IJob {
message: string;
jobs: any[];
}
#Injectable({
providedIn: 'root'
})
export class JobsService {
constructor(private httpClient: HttpClient) { }
private REST_API_SERVER = 'http://localhost:8080/myband/api/getjobs.php';
private REST_API_SERVER_SEND = 'http://localhost:8080/myband/api/sendjob.php';
jobList: Job[] = [];
errorMessage: any;
message: string;
static handleError(err: HttpErrorResponse) {
let errorMessage = '';
if (err.error instanceof ErrorEvent) {
errorMessage = `An error occurred: ${err.error.message}`;
} else {
errorMessage = `Server returned code: ${err.status}, error message is: ${err.message}`;
}
console.error(errorMessage);
return throwError(errorMessage);
}
public getJobs() {
this.requestJobs().subscribe(
iJob => {
this.message = iJob.message;
for (const job of iJob.jobs) {
const oneJob: Job = new Job(job);
this.jobList.push(oneJob);
}
},
error => this.errorMessage = error as any
);
return this.jobList;
}
public requestJobs(): Observable<IJob> {
return this.httpClient.get<IJob>(this.REST_API_SERVER).pipe(
catchError(JobsService.handleError)
);
}
}
The first thing I want to say to you is about isolation of responsibilities.
Your service must have just one job: provider one way to access your data; It means your logic inside getJobs() method could be done in your component.
export class JobsService {
constructor(
private httpClient: HttpClient,
) {}
private REST_API_SERVER = 'http://localhost:8080/myband/api/getjobs.php';
public requestJobs(): Observable<IJob> {
return this.httpClient.get<IJob>(this.REST_API_SERVER);
}
}
Now, you can handler your data in your component.
import { JobsService } from '../jobs.service';
#Component({
selector: 'app-job',
templateUrl: 'job.component.html'
})
export class JobComponent implements OnInit, AfterViewInit {
title = 'Job';
jobs$;
InProcess = '';
CurrentPartner = '';
ShowProcess = false;
sended = '';
constructor(private jobsService: JobsService) {
}
ngOnInit() {
this.jobs$ = this.jobsService.requestJobs();
}
ngAfterViewInit() {
this.jobs$
.pipe(
map(() => {}), // change your data here
catchError(() => {}) // handler your error here;
)
.subscribe(
() => {} // have access to your final data here.
);
}
}
Things to know:
You can remove the subscribe() execution and use the async pipe in your template;
The use of the operator map in pipe() is optional, you can handler your final data directly from your first callback subscribe().
You can convert your Observable to Promise using toPromise() method in one observable. Don't forgot async / await in your ngAfterViewInit.
Let me know if there is something I can help.
Try:
Object.keys(this.jobs).forEach(job => {
console.log(this.jobs[job]);
});
Try to assign an iterator function with below part replacement by this code:
// Nothing happened when looping through the array
this.jobs.forEach(oneJob, function(value, key) {
console.log(key + ': ' + value);
});
Usage of forEach in AngularJS:
For documentation try to check AngularJS forEach Docs
Syntax:
someIterable.forEach(object, iterator, [context])
Please check below example
class Job {
id: any;
status: any;
constructor(obj: any) {
this.id = obj.id;
this.status = obj.status;
}
}
let arr = [
{
id: 1,
status: "job"
}, {
id: 2,
status: "job2"
}
];
let newArr: any = [];
arr.forEach(a => {
let obj: Job = new Job(a);
newArr.push(obj);
})
console.log(newArr);
newArr.forEach((a: any) => {
console.log(a);
})
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;
}
}
});
}