Data Persistence on Angular with Firestore after page reload - javascript

How could i persist or recall on my firestore server the data once i reload page for any reason?
Basically in one of the components i make request to some data which by default gets initialized on my login Service component,lets say:
USER Component
export class UserComponent implements OnInit {
resumeAccount: UserResumeAccount = {
amountInAccount: 0,
allDetails: null,
};
constructor(
private userLogged: LoginService) {}
ngOnInit(): void {
this.resumeAccount.allDetails = this.userLogged.resumeAccount.allDetails;
this.resumeAccount.amountInAccount = this.userLogged.resumeAccount.amountInAccount;
}
}
then on the service i deploy the logic about showing or not this user logged information once is logged accessing to the firebase /firestoree data .Once the user signs in the data shows up cprrectly , but if for any reason the page is reload all the data get lost despite of user being active according with method getUserState():
LoginService component
#Injectable()
export class LoginService {
//variables
userdata: UserDataModel = {
uid: '',
restData: '',
};
userResumeAccount: UserResumeAccount = {
amountInAccount: 0,
allDetails: null,
};
totalAmount: number = 0;
resumeAccount: UserResumeAccount = {
amountInAccount: 0,
allDetails: null,
};
//constructor
constructor(
private userLogin: AngularFireAuth,
private docCreator: AngularFirestore,
private router: Router
) {}
ngOnInit():void {
this.userLogin.setPersistence(auth.Auth.Persistence.SESSION);
}
//methods
async logInUser(SignUpData: LoginData) {
let { emailUser, passwordUser } = SignUpData;
await this.userLogin.setPersistence(auth.Auth.Persistence.SESSION);
let responseOk = await this.userLogin.signInWithEmailAndPassword(emailUser,passwordUser);
this.userdata.uid = await responseOk.user.uid;
this.userdata.restData = await responseOk.user;
let pathUserName = await this.docCreator==>path to the document inside the firestore
.collection('users')
.doc(this.userdata.uid);
await pathUserName========>getting the firestore data and asigning it to variables
.get()
.toPromise()
.then(async (result) => {
if (result.exists) {
this.resumeAccount.allDetails = await result.get('userName');
this.resumeAccount.amountInAccount = await this.totalAmount;
} else return console.log('not Document Found');
})
.then(() => {})
.catch((err) => {
console.log('Error getting document:', err);
});
}
getUserState() {
return this.userLogin.authState.pipe(map((userLoged) =>{return (console.log(userLoged),
userLoged)}));
}===>ON RELOAD THIS METHOD STILL SHOWS USER ACTIVE
}
Thus basically in this former section inside the method which triggers the sign in process i access the data stored in firestore ; also at the ngOnInit methods i establish a persistence method for the section pretending to keep data reached once the page reloads, but is wrong!,as well as its initialization at the beggining of the loginUser method too.
How could i improve this problem.Really thanks in advance

Related

Pass value to subject service Angular js

i try passa values to child component using subject service, I am based on Documentatio, but somethink is wrong and when a try print data dont show nothing.
i have the navComponent which request api base on select input. When changing select update request and send new data to child page.
Service:
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs';
#Injectable()
export class SearchService {
// Observable string sources
private data = new Subject<any>();
// Observable string streams
value$ = this.data.asObservable();
// Service message commands
setValeu(value: any) {
this.data.next(value);
}
}
NavComponent:
async consultaIndicadores() {
try {
this.loading = true
let con = await this.rq.postRequest(... request ...).toPromise()
this.loading = false
if (con.status == 200) {
this.dados = con.data;
this.search.setValeu(this.dados)
}
}
} catch (error) {
this.loading = false
console.log(error)
}
}
childComponent
constructor(private trans: SearchService) {
this.subscription = trans.value$.subscribe(val => {
console.log(val);
})
}
in each component i seted provider, pass service to constructor etc... I dont know what its wrong. please help me

Firebase Firestore: globally detect when there are pending writes

I've got a simple requirement to show a warning to the user if they leave the browser window while a pending write is happening in Firestore using a beforeunload listener:
window.addEventListener('beforeunload', e => {
if (NO_PENDING_SAVES) return;
e.preventDefault();
e.returnValue =
'Your changes are still being saved. Are you sure you want to leave?';
}, {
capture: true,
});
In Firestore using the Web SDK, how do I detect whether there are pending saves or not globally? There is a waitForPendingWrites() method on the Firestore object, but it would require polling and it's also asynchronous, so it won't work inside of beforeunload.
Solved doing this (async way) using Angular Framework and dependency "#angular/fire": "^7.4.1" :
export class FirebaseService {
constructor(private db: AngularFirestore) {
}
pendingWrites(): Observable<any> {
return defer(() => this.db.firestore.waitForPendingWrites())
}
And then:
export class PendingWritesService {
hasPendingWrites$: Observable<boolean>;
constructor(
private firebaseService: FirebaseService
){
this.hasPendingWrites$ = this.somethingPending$();
}
somethingPending$(): Observable<boolean> {
const timeEmmiter = timer(1000);
return race(timeEmmiter, this.firebaseService.pendingWrites()).pipe(
switchMap((pendingWrites, noPendingWrites) => {
const arePendingsWrites = 0;
return of(pendingWrites === arePendingsWrites);
})
)
}
}

Firebase Realtime database count active users by status value

I have a service that detects the presence when the user is online, away and offline in firebase.
presence.service.ts
import { Injectable } from "#angular/core";
import { AngularFireAuth } from "#angular/fire/auth";
import { AngularFireDatabase } from "#angular/fire/database";
import * as firebase from "firebase/app";
import { tap, map, switchMap, first } from "rxjs/operators";
import { of } from "rxjs";
import { SuperUserService } from "./../services/superuser.service";
#Injectable({
providedIn: "root",
})
export class PresenceService {
constructor(
private afAuth: AngularFireAuth,
private db: AngularFireDatabase,
private superuser: SuperUserService
) {
console.log("Verificação de status em execução");
this.setName();
this.updateOnUser().subscribe();
this.updateOnDisconnect().subscribe();
this.updateOnAway();
}
getPresence(uid: string) {
return this.db.object(`status/${uid}`).valueChanges();
}
getUser() {
return this.afAuth.authState.pipe(first()).toPromise();
}
async setPresence(status: string) {
const user = await this.getUser();
if (user) {
return this.db.object(`status/${user.uid}`).update({
status,
timestamp: this.timestamp,
});
}
}
async setName() {
const user = await this.getUser();
if (user) {
return this.db.object(`status/${user.uid}`).update({
nome: this.superuser.user.displayName,
});
}
}
get timestamp() {
return firebase.database.ServerValue.TIMESTAMP;
}
updateOnUser() {
const connection = this.db
.object(".info/connected")
.valueChanges()
.pipe(map((connected) => (connected ? "online" : "offline")));
return this.afAuth.authState.pipe(
switchMap((user) => (user ? connection : of("offline"))),
tap((status) => this.setPresence(status))
);
}
updateOnDisconnect() {
return this.afAuth.authState.pipe(
tap((user) => {
if (user) {
return this.db
.object(`status/${user.uid}`)
.query.ref.onDisconnect()
.update({
status: "offline",
timestamp: this.timestamp,
});
}
})
);
}
async signOut() {
await this.setPresence("offline");
await this.afAuth.signOut();
}
updateOnAway() {
document.onvisibilitychange = (e) => {
if (document.visibilityState === "hidden") {
this.setPresence("away");
} else {
this.setPresence("online");
}
};
}
}
Firebase path
With this in mind I am wanting to implement a way to bring up how many users are active (online and away) Ex: 15 active users
I tried this, but it only brings me if exist or not
ref.child("status").orderByChild("status").equalTo("online")
.once("value",snapshot => {
if (snapshot.exists()){
// if exist
}
});
If you want to show the number of online users with your current data structure, you can keep the same query as you have but then use snapshot.numChildren() to determine the number of online users it returned.
If you want to show counts of both online and offline users with your current data structure, you can either:
Use a second query for the offline users, and use the same approach as above to get the count.
Use a single read without a condition on the status field, and then count the individual nodes in your application code with:
ref.child("status")
.once("value",snapshot => {
let onlineCount=0, offlineCount=0;
snapshot.forEach((user) => {
const status = user.child("status").val();
if (status === "online) onlineCount++
else if (status === "offline) offlineCount++
else console.error(`Unexpected status '${status}' for user ${user.key}`);
})
console.log(`Online: ${onlineCount}, Offline: ${offlineCount}`);
});
If you're reading all these user profiles to simply show two counters in your app, you're wasting quite a bit of bandwidth (and thus money) for both yourself and your users. Consider storing the actual counts that you want to display in the database itself, and updating them as each user goes online/offline in something like Cloud Functions.

Why the GET request from Angular in the NodeJS sometimes come as null although there are data

I am having sometimes a little problem with the GET request in Angular.
I am using ReplaySubject and return the Observable but sometimes in the reload of the app the get request it is working but it is getting null data or the message No content although there are the data.
In 3-4 tries then it shows the data.
The request get works but sometimes is null and sometimes give me the data.
Can someone help me on this ?
And if it possible to give any idea to use the ReplaySubject or something like, because i need to reload page everytime to fetch new data.
This is my Frontend part.
export class ModelDataService {
public baseUrl = environment.backend;
private data = new ReplaySubject<any>(1);
public userID = this.authService.userID;
constructor(private http: HttpClient, private authService: AuthService) {
}
public getJSON() {
return this.http.get(`${this.baseUrl}/data/${this.userID}`).subscribe(res => this.data.next(res));
}
public dataModel(): Observable<any> {
return this.data.asObservable();
}
public setData(data: Model) {
const api = `${this.baseUrl}/data`;
const user_id = this.authService.userID;
this.http.post(api, data, {
headers: {user_id}
}).subscribe(res => this.data.next(res));
}
public updateDate(id: string, dataModel: Model) {
const api = `${this.baseUrl}/data/${id}`;
return this.http.put(api, dataModel).subscribe(res => res);
}
}
This is the component which I get data
ngOnInit() {
this.authService.getUserProfile(this.userID).subscribe((res) => {
this.currentUser = res.msg;
});
this.modelDataService.getJSON();
this.model$ = this.modelDataService.dataModel();
this.model$.subscribe((test) => {
this.model = test;
});
this.model$.subscribe((test) => {
this.model = test;
});
}
This is the backend part.
const ModelData = require("../models/data");
async show(req, res) {
let modelData;
await ModelData.findOne({user: req.params.id}, (error, user) => {
modelData = user;
});
if (!modelData) {
res.status(204).json({error: "No Data"});
return;
}
return res.status(200).send(modelData);
},
routes.get("/data/:id", ModelDataController.show);
routes.post("/data", ModelDataController.store);
routes.put("/data/:id", ModelDataController.update);
If you pass in a callback function, Mongoose will execute the query asynchronously and pass the results to the callback. Sometimes you don't get the data because the query is not finished excuting. To fix this, you can change your code to:
let modelData = await ModelData.findOne({user: req.params.id});
if (!modelData)...

Reference to Object is lost somehow

I'm building a fairly advanced app using the Aurelia framework that uses models, services and custom elements.
I have a user model containing username, email etc as well as a bool isLoggedIn.
I have a user service which stores the user in localStorage, allows you to update the user data etc etc.
I finally have some custom elements that use the user service to fetch the current user and display a UI that depends on whether the user is logged in or not.
The issue I'm facing is that after I've fetched the user using the user service, and then update the user model from one of my custom elements (stored in the user service) another custom element's reference to said user model won't update.
I've tried to replicate the issue in a JSFiddle (in plain JS - I'm writing this in ES6) but failed to do so, in the fiddle all works fine.
There's quite a lot of code, but I'd like to hear from you what I might've done wrong for this to happen at all?
Here's the gist of what I have:
user.model.js
export class User {
username = '';
isLoggedIn = false;
constructor (data) {
Object.assign(this, data);
}
}
user.service.js
import { User } from 'user.model';
export class UserService {
constructor () {
this.user = null;
}
getUser () {
// User is cached
if (this.user) {
return Promise.resolve(this.user);
}
// Check if we have localStorage
var user = window.localStorage.getItem('user');
if (user) {
this.user = new User(JSON.parse(user));
return Promise.resolve(this.user);
}
// No user - create new
this.user = new User();
return Promise.resolve(this.user);
}
saveUser () {
this.getUser().then(() => {
window.localStorage.setItem('user', JSON.stringify(this.user));
});
}
updateUser (user) {
this.getUser().then(() => {
Object.assign(this.user, user);
this.saveUser();
});
}
login () {
this.updateUser({
isLoggedIn: true
});
return Promise.resolve(true);
}
}
custom-element.js
import { inject } from 'aurelia-framework';
import { UserService } from 'user.service';
#inject (UserService)
export class CustomElement {
constructor (userService) {
this.userService = userService;
}
attached () {
this.userService.getUser().then(user => {
this.user = user;
console.log('Fetched user:');
console.dir(this.user);
console.log('Same?');
console.dir(this.user === user); // this is true
});
// At some point another custom element fires the UserService.login() method
// Here we check that our reference to the user also updates
setInterval(() => {
console.log('Is user logged in?');
console.dir(this.user.isLoggedIn); // This is always false - even after UserService.login() has been called and UserService.user is updated (if I console.dir this.user inside UserService it is indeed updated)
// Grab a new version of UserService.user
this.userService.getUser().then(user => {
console.log('Fetched new user, is new user and our user same?');
console.dir(this.user === user); // false :/ the new user fetched here actually has isLoggedIn === true but our this.user does not...
});
}, 2000);
}
}
As noted in the comments, at one point another custom element runs UserService.login() which changes UserService.user.isLoggedIn to true (this is reflected in the UserService if I console.dir its this.user) but the other CustomElement's this.user does not update.
Btw: The reason for the Promise.resolve() in UserService.getUser() is that in the future there will be server calls in there.
Tbh I'm quite new to this type of programming in JS, coming more from a jQuery world, and even though I'm basically in love with Aurelia stuff like this still confuse me greatly so hoping for some insight here :)
Looking at the code you provided, I don't see what would cause the behavior you've described.
One suggestion- to make your code a little easier to manage, less async... what if you structured it like this:
user.js
export class User {
loggedIn = false;
name = 'Anonymous';
}
user-store.js
#inject(User)
export class UserStore {
constructor(user) {
this.user = user;
}
load() {
const serializedUser = localStorage.getItem('user');
if (!info) {
return;
}
const storageUser = JSON.parse(serializedUser);
this.user.loggedIn = storageUser.loggedIn;
this.user.name = storageUser.name;
}
save() {
const serializedUser = JSON.stringify(this.user);
localStorage.setItem('user', serializedUser);
}
}
auth-service.js
#inject(User, UserStore)
export class AuthService {
constructor(user, store) {
this.user = user;
this.store = store;
}
login(username, password) {
return fetch('https://api.megacorp.com/login', { method: 'POST' ... })
.then(result => {
this.user.loggedIn = true;
this.user.name = result.name;
this.store.save();
});
}
}
custom-element.js
#inject(User)
export class CustomElement {
constructor(user) {
this.user = user;
}
...
}
The benefit being your custom elements never need to take a dep on something async. They just get the application's User instance, whose properties might change but will always remain the same instance.

Categories

Resources