I have a simple app set up in Ionic 3 with a login page and a home page. I check to see if the user is logged in, then redirect to either the login page or the home page. That part seems to work.
After the login, on the home page I am trying to get the current logged in user. I get the user twice, and then it returns null twice. Why is the user not persistent? According the the AngularFire docs, the login should be persistent by default.
app.component.ts:
export class MyApp {
#ViewChild(Nav) nav: Nav;
rootPage:any;
constructor(private afAuth: AngularFireAuth, platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) {
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
statusBar.styleDefault();
splashScreen.hide();
const authObserver = this.afAuth.authState.subscribe(user =>{
if (user){
this.rootPage = 'HomePage';
authObserver.unsubscribe();
} else {
this.rootPage = 'LoginPage'
authObserver.unsubscribe();
}
});
});
}
}
login.ts:
export class LoginPage {
user = {} as User;
constructor(public authData: AuthProvider, public navCtrl: NavController, public navParams: NavParams) {
}
ionViewDidLoad() {
console.log('ionViewDidLoad LoginPage');
}
login(user: User){
this.authData.loginUser(user.email, user.password).then( authData => {
this.navCtrl.setRoot('HomePage');
}, error => {
console.log('Something went wrong')
});
}
}
home.ts:
export class HomePage {
constructor(private afAuth: AngularFireAuth, public authData: AuthProvider, public navCtrl: NavController, public navParams: NavParams) {
this.afAuth.auth.onAuthStateChanged(user => {
console.log(user);
});
}
}
logs:
login.ts:27 ionViewDidLoad LoginPage
home.ts:22 Hk {D: Array(0), G: "XIzaSyDn9pRx23NeEaCNVMQ0sBveDxYEsGrGvRA", s: "[DEFAULT]", A: "pigs-n-bulls-xxxxxxxx.firebaseapp.com", b: xh, …}
home.ts:22 Hk {D: Array(0), G: "XIzaSyDn9pRx23NeEaCNVMQ0sBveDxYEsGrGvRA", s: "[DEFAULT]", A: "pigs-n-bulls-xxxxxxxx.firebaseapp.com", b: xh, …}
home.ts:27 ionViewDidLoad HomePage
home.ts:22 null
home.ts:22 null
this.afAuth.auth.auth.setPersistence(firebase.auth.Auth.Persistence.LOCAL);
or
this.afAuth.auth.auth.setPersistence(firebase.auth.Auth.Persistence.SESSION);
Then in your auth check code:
this.afAuth.auth.auth.onAuthStateChanged(user => {
if (!user) {
}
}
Take not I am using Angularfire, but accessing the Firebase auth functions directly.
Related
I am integrating Nestjs with firebase admin, the controller is not updating the view. With the service there is not problem, update in real time.
Someone will have some suggestion, what is my wrong in the code?
The Service that inject the controller is:
import { Injectable } from '#nestjs/common';
import * as admin from 'firebase-admin';
export interface Customer {
direction: string,
codLegal: string,
phone: string,
name: string
}
export interface CustomerId extends Customer{
id: string;
}
#Injectable()
export class CustomerService {
constructor() {}
findCustomers(): Promise<any>{
return new Promise((resolve, reject) => {
admin.firestore().collection('/data/LYvBew5FDpjLqcQjA2Ra/info')
.onSnapshot((querySnapshot) => {
const promises: any = [];
querySnapshot.forEach((doc: any) => {
promises.push({
id: doc.id,
data: doc.data() as Customer,
});
});
console.log(promises);
resolve(promises);
})
});
}
}
**The basic controller is: **
import { Controller, Get } from '#nestjs/common';
import { CustomerService } from './services/customer.service';
#Controller('customers') export class CustomerController {
constructor(private readonly customerService: CustomerService) {
}
#Get()
async findAll() {
try {
return await this.customerService.findCustomers();
}catch(err) {
console.log(err);
}
}
}
Talking in terms of HTTP, the controller will not update the view. The view is rendered once you call the findAll route and send to the client.
If you want to show updates to the view in realtime, you should include firebase into your frontend.
I am unable to read data from Firebase using angularfireobject.
This is my code:
export class UserService {
user: Observable<any>;
constructor(private db: AngularFireDatabase) {
}
public getUser($userId: string){
this.db.object('users/'+$userId).valueChanges();
}
}
and in user.component.html
<h1>Welcome {{ (userService.user | async)?.name }}!</h1>
userService.user | json gives null.
Any help is appreciated.
You're not setting your .user Observable anywhere that I see and .getUser() is never called.
If you intend to show information on the current user you'll have to pull in authentication too:
export class UserService {
user: Observable<any>;
constructor(private db: AngularFireDatabase, private auth: AngularFireAuth) {
this.user = auth.user.pipe(switchMap(user =>
user ? db.object(`users/${user.uid}`).valueChanges() : of(null)
));
}
}
Firebase Phone Auth is working on web, but not working on android device at returns "SMS NOT SENT" error and "auth/operation-not-supported-in-this-environment" message
Recaptcha working on Web and not working on Android Phone
and firebase android document suggests
compile 'com.google.firebase:firebase-auth:11.6.0' --- be added in build.gradle
when added & create a build
compile dependency line disappears from build.gradle
SMS not sent
N {code: "auth/operation-not-supported-in-this-environment", message: "RecaptchaVerifier is only supported in a browser HTTP/HTTPS environment."}
code
:"auth/operation-not-supported-in-this-environment"
message
:"RecaptchaVerifier is only supported in a browser HTTP/HTTPS environment."
__proto__
:Error
Here is my code
import { Component } from '#angular/core';
import { IonicPage, NavController, NavParams, AlertController} from 'ionic-angular';
import firebase from 'firebase';
/**
* Generated class for the LoginPage page.
*
* See https://ionicframework.com/docs/components/#navigation for more info on
* Ionic pages and navigation.
*/
#IonicPage()
#Component({
selector: 'page-login',
templateUrl: 'login.html',
})
export class LoginPage {
public recaptchaVerifier:firebase.auth.RecaptchaVerifier;
constructor(public navCtrl: NavController, public alertCtrl: AlertController) {}
ionViewDidLoad() {
this.recaptchaVerifier = new firebase.auth.RecaptchaVerifier('recaptcha-container',{
'size':'invisible',
'callback':function(responce){
}
});
}
signIn(phoneNumber: number){
const appVerifier = this.recaptchaVerifier;
const phoneNumberString = "+91" + phoneNumber;
firebase.auth().signInWithPhoneNumber(phoneNumberString, appVerifier)
.then( confirmationResult => {
// SMS sent. Prompt user to type the code from the message, then sign the
// user in with confirmationResult.confirm(code).
let prompt = this.alertCtrl.create({
title: 'Enter the Confirmation code',
inputs: [{ name: 'confirmationCode', placeholder: 'Confirmation Code' }],
buttons: [
{ text: 'Cancel',
handler: data => { console.log('Cancel clicked'); }
},
{ text: 'Send',
handler: data => {
confirmationResult.confirm(data.confirmationCode)
.then(function (result) {
var user = result.user;
// User signed in successfully.
console.log(result.user);
// ...
}).catch(function (error) {
// User couldn't sign in (bad verification code?)
// ...
});
}
}
]
});
prompt.present();
})
.catch(function (error) {
console.error("SMS not sent", error);
});
}
}
I am trying to implement login with firebase on Ionic 2 with the following code.
export class MyApp {
rootPage:any = Login;
isAuthenticated = false;
constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen) {
firebase.initializeApp({
apiKey: "AIzaSyC94rD8wXG0aRLTcG29qVGw8CFfvCK7XVQ",
authDomain: "myfirstfirebaseproject-6da6c.firebaseapp.com",
});
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
this.rootPage = Home;
} else {
this.rootPage = Login;
}
});
platform.ready().then(() => {
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
statusBar.styleDefault();
splashScreen.hide();
});
}
}
I realize that even when I am authenticated, I am always brought to the Login screen because it does not wait for onAuthStateChanged promise to be fulfilled and carries on with initializing the app, therefore, the Login screen instead of the Home screen is always shown.
But how should I change my code so that I can show Home when authenticated?
Remove the login from the rootPage declaration
export class MyApp {
rootPage:any;
...
}
You're setting the page to your LoginPage as the app initializes and before he can check if the user is loged.
For it to run the onAuthStateChange, when the app initializes you need to use Zone to create an observable and the run it.
import { NgZone } from '#angular/core';
zone: NgZone; // declare the zone
this.zone = new NgZone({});
const unsubscribe = firebase.auth().onAuthStateChanged((user) => {
this.zone.run(() => {
if (user) {
this.rootPage = Home;
unsubscribe();
} else {
this.rootPage = Login;
unsubscribe();
}
});
});
Hope it helps
I'm trying to apply reflux/ngrx on my current front-end project.
I want to take advantage of this in order to change a slight functionality: Change current user related tasks in order to use a single user state.
Current user related tasks: Currently, I'm using an traditional model in order to achieve user login process... UserService is able to check user credentials. Once it's been checked I store user information on an AppService:
export class LoginComponent implements OnInit {
private fb: FormBuilder;
private form:FormGroup;
private commty: UsersService;
private router: Router;
private appState: AppState;
private alerts: Array<Object>;
constructor()
{
this.alerts = [];
}
ngOnInit():void {
this.form = this.fb.group({
user: ['', Validators.required],
passwd: ['', Validators.minLength(6)]
});
}
public checkPasswd():void {
this.clearAlerts();
this.commty.checkPasswd(this.form.value.mail, this.form.value.passwd)
.subscribe(
(result: any) => {
this.appState.user = result;
this.router.navigate(['/app']);
},
(error: any) => {
this.addAlert(error.message);
}
);
}
private addAlert(message: string): void {
this.alerts.push({type: 'danger', msg: message});
}
public closeAlert(index): void {
this.alerts.splice(index, 1);
};
private clearAlerts(): void {
this.alerts.splice(0, this.alerts.length);
}
}
I'm a bit confused about how to move this code in order to use reflux/ngrx. I'ce read a bit about this topic, nevertheless I'm not quite able to figure out how to move my code. Up to now, I've created an single Store and User interfaces:
store.interface.ts:
export interface IStore {
user: IUser
sources: ISourceRedux;
}
user.interfcae.ts:
export interface IUser {
id: string;
name: string;
username: string;
customer: string;
}
The next step I think I need to do is to create reducers. This step is which I don't quite understand how build this code. Up to now
user.initialstate.ts:
export function initialUserState(): IUser {
return {
id: '',
name: '',
username: '',
customer: '',
sources: []
};
};
user.reducer.ts
export class User {
private static reducerName = 'USER_REDUCER';
public static reducer(user = initialUserState(), {type, payload}: Action) {
if (typeof User.mapActionsToMethod[type] === 'undefined') {
return user;
}
return User.mapActionsToMethod[type](user, type, payload);
}
// ---------------------------------------------------------------
// tslint:disable-next-line:member-ordering
private static mapActionsToMethod = {};
}
Which reducers I should create in order to:
Check credentials.
If credentials are right get this user and update User state store.
If credentials are wrong inform the process has failed.
Perhaps I'm merging concepts... I need some lights...
EDIT
public connect(user: string, currentPasswd: string, extraHttpRequestParams?: any): Observable<UserDTO> {
return this.checkPasswdWithHttpInfo(id, currentPasswd, extraHttpRequestParams)
.map((response: Response) => {
if (response.status === 204) {
return undefined;
} else {
return response.json();
}
}).catch((error: any) => {
if (error.status >= 500) {
return Observable.throw(new Error(error.status));
}
else { //if (error.status >= 400) {
const body = error.json() || '';
const code = body.error || JSON.stringify(body);
const message = body.message || JSON.stringify(body);
return Observable.throw(ApiError.create(code, message));
}
});
}
Ok so this is the next question of your "Integrate ngrx into my code" =).
What you're looking for is : https://github.com/ngrx/effects
The idea behind effects is that an effect let you catch an Action, do side effect (API call or whatever) and you can then dispatch another Action (often success or error).
Flow example to connect a user :
--| [from component] Dispatch action USER_CONNECT
--| [from user.effect.ts]
----| Catch action ofType('USER_CONNECT')
----| Do what you need to do (API call for ex)
----| When the response comes back :
------| If success : Dispatch USER_CONNECT_SUCCESS
------| If error : Dispatch USER_CONNECT_ERROR
Of course when you dispatch either USER_CONNECT_SUCCESS or USER_CONNECT_ERROR you can pass additional data in the payload (for example user information or the error).
Here's a full example :
#Injectable()
export class UsersEffects {
constructor(
private _actions$: Actions,
private _store$: Store<IStore>,
private _userService: UserService,
) { }
#Effect({ dispatch: true }) userConnect$: Observable<Action> = this._actions$
.ofType('USER_CONNECT')
.switchMap((action: Action) =>
this._userService.connect(action.payload.username, action.payload.password)
.map((res: Response) => {
if (!res.ok) {
throw new Error('Error while connecting user !');
}
const rslt = res.json();
return { type: 'USER_CONNECT_SUCCESS', payload: rslt };
})
.catch((err) => {
if (environment.debug) {
console.group();
console.warn('Error catched in users.effects.ts : ofType(USER_CONNECT)');
console.error(err);
console.groupEnd();
}
return Observable.of({
type: 'USER_CONNECT_ERROR',
payload: { error: err }
});
})
);
}
You can take a look into my project Pizza-Sync were I did something similar (except that I don't catch in case of error and do not dispatch if there's an error).