I'm looking to set up firebase authentication on a NativeScript app and I'm having a hard time figuring out the best practice to set up the routes/components.
What I like to do is set it up like any typical modern app where if the user is not logged in then they are redirected to the login page. If there are logged in then they skip the login page and are redirected to their dashboard.
I have a user.service like this
import { Injectable } from '#angular/core';
import { RouterExtensions } from 'nativescript-angular/router';
import firebase = require("nativescript-plugin-firebase");
#Injectable()
export class UserService {
private uid;
private route;
constructor(router: RouterExtensions){
this.route = router;
}
public initFirebase(){ //This gets called from AppComponent Constructor
let that = this;
firebase.init({
onAuthStateChanged: function(data) {
if (data.loggedIn) {
that.route.navigate(["/dash"]);
} else {
console.log("NOT logged in.. redirecting to login");
that.route.navigate(["/login"]);
}
}
}).then(
(instance) => {
console.log("Firebase Initialized");
},
(error) => {
console.log("firebase.init error: " + error);
}
);
}
And in the router what I currently have set up is
export const routes = [
{ path: "", component: LoginComponent },
{ path: "dash", component: DashComponent},
{ path: "login", component: LoginComponent}
];
But that flashes the login page before it redirects to the dash which is terrible.
I also tried to do this..
export const routes = [
{ path: "", component: AppComponent },
{ path: "dash", component: DashComponent},
{ path: "login", component: LoginComponent},
];
But this for reason makes Firebase initialize twice.
SO finally I tried this..
export const routes = [
{ path: "", component: DashComponent},
{ path: "dash", component: DashComponent},
{ path: "login", component: LoginComponent},
];
But this runs the DashComponent constructor before I want it to.
I want firebase init in user.service run FIRST and then DashComponent run after the user service firebase init completes and redirects to dash.
What is the best practice to solve this?
You could use a guard service that validates if the user is logged in such as:
import { AuthGuardService } from "../../...."
export const routes = [
{ path: "", component: DashComponent},
{ path: "dash", component: DashComponent, canActivate:[AuthGuardService]},
{ path: "login", component: LoginComponent},
];
That will implement CanActive from "#angular/router".
There you can validate if a user is logged in through a service and redirect him to that.route.navigate(["/login"]); if he is not.
import { Injectable } from "#angular/core";
import { Observable } from 'rxjs/Observable';
import { CanActivate, RouterStateSnapshot, ActivatedRouteSnapshot } from "#angular/router";
import { RouterExtensions, PageRoute } from "nativescript-angular/router";
import { UserService } from "./user.service";
#Injectable()
export class AuthGuardService implements CanActivate {
private userService: UserService;
private router: RouterExtensions;
public constructor(userService: UserService,
router: RouterExtensions) {
this.router = router;
this.userService = userService;
}
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
if(!userService.IsAuthenticated()){
that.route.navigate(["/login"])
}
return this.userService.IsAuthenticated();
}
I know it's kinda late, but it might help someone.
Related
I am using the ionic framework. How do I set storage to login information so if the app restart the user can go to the home page when filling the login information again and again.
import * as firebase from 'firebase/app';
import { Storage } from '#ionic/storage';
#Injectable({
providedIn: 'root'
})
export class AuthenticationService {
constructor(public storage: Storage) {}
loginUser(value){
firebase.auth().signInWithEmailAndPassword(value.email, value.password)
.then(() => {
console.log('Log In Successful, UID: ' + value.uid + 'Email: ' +
value.email);
this.storage.set('Email', value.email);
this.storage.set('Password', value.password);
})
}
}
Ref. My github Url
authentication.service.ts
import { Injectable } from '#angular/core';
import { Router } from '#angular/router';
import { Storage } from '#ionic/storage';
import { ToastController, Platform } from '#ionic/angular';
import { BehaviorSubject } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class AuthenticationService {
authState = new BehaviorSubject(false);
constructor(
private router: Router,
private storage: Storage,
private platform: Platform,
public toastController: ToastController
) {
this.platform.ready().then(() => {
this.ifLoggedIn();
});
}
ifLoggedIn() {
this.storage.get('USER_INFO').then((response) => {
if (response) {
this.authState.next(true);
}
});
}
login() {
var dummy_response = {
user_id: 'manzoor.alam#thinktac.com',
user_name: 'manzoor'
};
this.storage.set('USER_INFO', dummy_response).then((response) => {
this.router.navigate(['dashboard']);
this.authState.next(true);
});
}
logout() {
this.storage.remove('USER_INFO').then(() => {
this.router.navigate(['login']);
this.authState.next(false);
});
}
isAuthenticated() {
return this.authState.value;
}
}
In auth-guard.service.ts
import { Injectable } from '#angular/core';
import { AuthenticationService } from './authentication.service';
import { CanActivate } from '#angular/router';
#Injectable({
providedIn: 'root'
})
export class AuthGuardService implements CanActivate {
constructor( public authenticationService: AuthenticationService) { }
canActivate(): boolean {
return this.authenticationService.isAuthenticated();
}
}
App.component.ts file
import { Component } from '#angular/core';
import { Platform } from '#ionic/angular';
import { SplashScreen } from '#ionic-native/splash-screen/ngx';
import { StatusBar } from '#ionic-native/status-bar/ngx';
import { AuthenticationService } from './services/Authentication.service';
import { Router } from '#angular/router';
#Component({
selector: 'app-root',
templateUrl: 'app.component.html'
})
export class AppComponent {
constructor(
private platform: Platform,
private splashScreen: SplashScreen,
private statusBar: StatusBar,
private router: Router,
private authenticationService: AuthenticationService
) {
this.initializeApp();
}
initializeApp() {
this.platform.ready().then(() => {
this.statusBar.styleDefault();
this.splashScreen.hide();
this.authenticationService.authState.subscribe(state => {
if (state) {
this.router.navigate(['dashboard']);
} else {
this.router.navigate(['login']);
}
});
});
}
}
In app-routing.module.ts
import { NgModule } from '#angular/core';
import { PreloadAllModules, RouterModule, Routes } from '#angular/router';
import { AuthGuardService } from './services/auth-guard.service';
const routes: Routes = [
// { path: '', redirectTo: 'home', pathMatch: 'full' },
// { path: 'home', loadChildren: './home/home.module#HomePageModule' },
// { path: 'login', loadChildren: './login/login.module#LoginPageModule' },
// { path: 'dashboard', loadChildren: './dashboard/dashboard.module#DashboardPageModule' },
{ path: '', redirectTo: 'login', pathMatch: 'full' },
{ path: 'login', loadChildren: './login/login.module#LoginPageModule' },
{
path: 'dashboard',
loadChildren: './dashboard/dashboard.module#DashboardPageModule',
canActivate: [AuthGuardService]
// Here canActivate is a method inside the AuthGuardService which return boolen type values
}
];
#NgModule({
imports: [
RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
],
exports: [RouterModule]
})
export class AppRoutingModule { }
Please Ref. My github url more details github Url
Use Router Guard.
A Guard is just an Angular service - or injectable - that controls the behavior of the router in a maintainable way. Let’s generate it with the CLI:
ionic generate guard guards/login
The guard contains a special canActivate method that we are required to implement that must return or resolve to a boolean value. Because Ionic Storage is Promise-based, we can just make it an async function. Its job is to read the loginComplete value from the device storage. If true it allows the route to active, but if false it will block the route and redirect to the login.
// ...omitted
import { Storage } from '#ionic/storage';
#Injectable({
providedIn: 'root'
})
export class LoginGuard implements CanActivate {
constructor(private storage: Storage, private router: Router) {}
async canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Promise<boolean> {
const isComplete = await this.storage.get('loginComplete');
if (!isComplete) {
this.router.navigateByUrl('/login');
}
return isComplete;
}
}
Applying the Guard
app-routing.module
import { Routes, RouterModule } from '#angular/router';
import { LoginGuard } from './guards/login.guard';
const routes: Routes = [
{
path: '',
loadChildren: './tabs/tabs.module#TabsPageModule',
canActivate: [LoginGuard] // <-- apply here
},
{
path: 'login',
loadChildren: './login/login.module#LoginPageModule'
}
];
#NgModule(...)
export class AppRoutingModule {}
Login page
import * as firebase from 'firebase/app';
import { Storage } from '#ionic/storage';
#Injectable({
providedIn: 'root'
})
export class AuthenticationService {
constructor(public storage: Storage) {}
loginUser(value){
firebase.auth().signInWithEmailAndPassword(value.email, value.password)
.then(() => {
console.log('Log In Successful, UID: ' + value.uid + 'Email: ' +
value.email);
this.storage.set('Email', value.email);
this.storage.set('Password', value.password);
this.storage.set('loginComplete', true);
})
}
}
Hope it helps you :)
Ref url: AngularFirebase
I have a problem calling Custom Web Api in asp.net MVC 5.The following is my code for Web Api Controller and angular js 2.
[Route("api/email/detail/{id:int}"), HttpGet]
public async Task<IHttpActionResult> EmailDetail(int id)
{
return Ok();
}
The code for angular app.routing.ts is as follows:
import { ModuleWithProviders } from '#angular/core';
import { Routes, RouterModule } from '#angular/router';
import { HomeComponent } from './components/home.component';
import { UserComponent } from './components/user.component';
import { EmailComponent } from './components/email.component';
import { EmailDetail } from './components/email.detail';
const appRoutes: Routes = [
{ path: 'App/Template', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'user', component: UserComponent },
{ path: 'detail/:id', component: EmailDetail },
{ path: 'email', component: EmailComponent },
//{ path: 'detail', component: EmailDetail},
];
export const routing: ModuleWithProviders = RouterModule.forRoot(appRoutes);
Code for email.detail.ts is as follows:
import { Component, OnInit, ViewChild } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { UserService } from '../Service/user.service';
import { IEmail } from '../Models/user';
import { Http, Response, Headers, RequestOptions } from '#angular/http';
#Component({
templateUrl: 'About/Template/Detail'
})
export class EmailDetail implements OnInit {
private emailId: number;
private email: IEmail
constructor(private _userService: UserService, private _http: Http,
private route: ActivatedRoute) {
}
ngOnInit(): void {
this._http.get("api/email/detail/1992").subscribe(data => {
// Read the result field from the JSON response.
});
}
}
The error I get in the console is as follows:
The api call is http://localhost:16552/detail/api/email/detail/1992 and detail is getting prepended which is preventing from calling the api controller.
useHash solved the problem and its working fine now.
I am new to Angular2 Native script Programming... i need to navigate one page to another. i am stuck with the typeError:"undefined is not an object (evaluating 'this.router')" plz help me out..
my code is given..
//page1.ts
public constructor(private router: Router) { }
getMyDrawing(args) {
let pad = this.DrawingPad.nativeElement;
let img: Image = this.signImage.nativeElement;
let drawingImage;
pad.getDrawing().then
(
(data) => {
console.log(data);
drawingImage = data;
img.src=data;
this.router.navigate(['page2']);
}
);
}
//routing.ts
import { DrawingPadExample } from "./app.component";
import { Page2Component } from "./app.page2";
export const routes = [
{ path: "drawing-pad-example", component: DrawingPadExample},
{ path: "page2", component: Page2Component }
];
export const navigatableComponents = [
DrawingPadExample,
Page2Component
];
//module.ts
import { NgModule, NO_ERRORS_SCHEMA } from "#angular/core";
import { NativeScriptModule } from "nativescript-
angular/nativescript.module";
import { DrawingPadExample } from "./app.component";
import { routes, navigatableComponents } from "./app.routing";
import { NativeScriptRouterModule } from "nativescript-
angular/router";
#NgModule({
bootstrap: [
DrawingPadExample
],
imports: [
NativeScriptModule,
NativeScriptRouterModule,
NativeScriptRouterModule.forRoot(routes)
],
declarations: [
DrawingPadExample,
...navigatableComponents
],
schemas: [
NO_ERRORS_SCHEMA
]
})
export class AppModule { }
When you say navigation is not working, do you nothing at all happens and no errors are generated?
I would advice you try putting the callback within a zoneCallback.
public constructor(private ngZone: NgZone, private router: Router){
}
public getMyDrawing(args) {
let pad = this.DrawingPad.nativeElement;
let img: Image = this.signImage.nativeElement;
let drawingImage;
pad.getDrawing().then((data) => {
this.ngZone.run(() => {
console.log(data);
drawingImage = data;
img.src=data;
this.router.navigate(['page2']);
});
});
}
Add instance in constructor for router.
constructor(private router : Router){}
because as error stated typeError:"undefined is not an object (evaluating 'this.router')" you are trying to access
this.router which is not initilized yet
#JBR, No, I mean dependency injection using constructor, for example,
constructor(private router: Router) {}
add that line to you page1 component.
Update
replace this
function(data) {
console.log(data);
drawingImage = data;
img.src=data;
this.router.navigate(['page2']);
}
with arrow function
(data) => {
console.log(data);
drawingImage = data;
img.src=data;
this.router.navigate(['page2']);
}
I got this bug when I was passing a function between two components.
In the first component, I imported Router, but in the second I didn't. Make sure that you import all necessary libraries when you are using callback functions.
We're trying to work out why our information is not sending to our DB, except for email. We want to save name, surname, etc. to Firebase, but it is only saving email. What are we doing wrong?
signup.ts:
import { Component } from '#angular/core';
import { SearchPage } from '../search/search';
import { MyBookingsPage } from '../my-bookings/my-bookings';
import { NavController, AlertController, LoadingController, Loading } from 'ionic-angular';
import { Auth, User, UserDetails, IDetailedError } from '#ionic/cloud-angular';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { AuthProvider } from '../../providers/auth/auth';
import { EmailValidator } from '../../validators/email';
#Component({
selector: 'page-signup',
templateUrl: 'signup.html'
})
export class SignupPage {
public signupForm:FormGroup;
public loading:Loading;
constructor(public navCtrl: NavController, public authProvider:AuthProvider, public formBuilder: FormBuilder, public user: User, public alertCtrl: AlertController, public loadingCtrl:LoadingController) {
this.signupForm = formBuilder.group({
name: ['', Validators.compose([Validators.required])],
surname: ['', Validators.compose([Validators.required])],
birthday: ['', Validators.compose([Validators.required])],
licenseNum: [''],
email: ['', Validators.compose([Validators.required, EmailValidator.isValid])],
password: ['', Validators.compose([Validators.minLength(6), Validators.required])]
});
}
signupUser(){
if (!this.signupForm.valid){
console.log(this.signupForm.value);
} else {
this.authProvider.signupUser(this.signupForm.value.name,
this.signupForm.value.surname,
this.signupForm.value.birthday,
this.signupForm.value.licenseNum,
this.signupForm.value.email,
this.signupForm.value.password)
.then(() => {
this.loading.dismiss().then( () => {
this.navCtrl.setRoot(MyBookingsPage);
});
}, (error) => {
this.loading.dismiss().then( () => {
let alert = this.alertCtrl.create({
message: error.message,
buttons: [
{
text: "Ok",
role: 'cancel'
}
]
});
alert.present();
});
});
this.loading = this.loadingCtrl.create();
this.loading.present();
}
}
}
auth.ts:
#Injectable()
export class AuthProvider {
public fireAuth:firebase.auth.Auth;
public userProfileRef:firebase.database.Reference;
constructor(public http: Http) {
this.fireAuth = firebase.auth();
this.userProfileRef = firebase.database().ref('/userProfile'); //linked to firebase node userProfile
console.log('Hello AuthProvider Provider');
}
signupUser(name: string, surname: string, birthday: any, licenseNum: string, email: string, password: string ): firebase.Promise<any> {
return this.fireAuth.createUserWithEmailAndPassword(email, password).then( newUser => {
this.userProfileRef.child(newUser.uid).set({
name: name,
surname: surname,
birthday: birthday,
licenseNum: licenseNum,
email: email,
password: password
});
});
}
}
This is the Firebase result.
We think that the problem is in signupUser in auth.ts, but we really don't know, and we can't find anything online that fits with our structure. If possible, we don't want to have to redo our entire project!
Thanks in advance!
You should use .update() function instead of .set() function to store information at firebase database.
Hi I'm having trouble navigating with a browsers back/forward buttons. It only occurs when navigating between routes that share the same component via route parameters.
I have an application that lists lots of animals. Every time I navigate to example.com/animal/cat or example.com/animal/dog, a Http Get request is made to a restful Api which returns the relevant data. If I navigate to /animal/cat then to /animal/dog everything seems ok. The problem starts when I click on the browsers Back Button to go back to /animal/cat. Instead of loading the data for the cat, the URL changes but the data is still listed for the dog. I'm using router 3.0.0-beta.2 with RC4.
heres my animals details.component page:
import { Component, OnInit, OnDestroy } from '#angular/core';
import { ROUTER_DIRECTIVES, ActivatedRoute, Router } from '#angular/router';
import { Response } from '#angular/http';
import { Subscription } from 'rxjs/Rx';
import { DataService } from '../../data.service';
#Component({
moduleId: module.id,
selector: 'animal-details',
templateUrl: 'animal-details.component.html',
styleUrls: ['animal-details.component.css'],
directives:[ROUTER_DIRECTIVES]
})
export class AnimalDetailsComponent implements OnInit, OnDestroy {
constructor(private activatedRoute: ActivatedRoute, private dataService: DataService, private router: Router) {
}
private subscription: Subscription;
animal = {};
link: string;
table = 'animal/';
private url = '?related=family_by_link';
ngOnInit() {
this.subscription = this.activatedRoute.params.subscribe(
(param: any) => this.link = param['link']
);
this.dataService.getData(this.table, this.link, this.url)
.map((response: Response) => response.json())
.subscribe(
(data: any) => {this.animal = data},
err => { console.log('error404') }
);
};
nextData(){
this.dataService.getData(this.table, this.link, this.url)
.map((response: Response) => response.json())
.subscribe(
(data: any) => {this.animal = data},
err => { console.log('error404') }
);
window.scroll(0,0);
};
ngOnDestroy() {
this.subscription.unsubscribe();
};
}
I use the getData() method when navigating between animals on the same component. My AnimalComponentDetails html:
<div class="row">
<div class="col-sm-6 col-md-4 col-lg-3 text-xs-center" *ngFor="let family of animal.family_by_link" (click)="nextData()">
<a [routerLink]="['/animal', family.ani_link]">
<img src="/images/animal/{{family.ani_link}}.png">
<p>{{family.name}}</p>
</a>
</div>
Heres my DataService:
import { Injectable } from '#angular/core';
import { Http, Response } from '#angular/http';
import { Subscription, Observable } from 'rxjs/Rx';
import 'rxjs/add/operator/map'
#Injectable()
export class DataService {
constructor(private dataService: Http) {
}
urlprefix = 'http://123.123.123.123/api/v2/_table/';
api = '&api_key=7201521drgdc71';
getData(table, link, url): Observable<any> {
return this.dataService.get(this.urlprefix + table + link + url +this.api);
}
}
my app.routes:
import { provideRouter } from '#angular/router';
import { AnimalComponent } from './animal/animal.component';
import { AnimalDetailsComponent } from './animal/animal-details/animal-details.component'
{ path: '', component: HomeComponent },
{ path: 'animal', component: AnimalComponent },
{ path: 'animal/:link', component: AnimalDetailsComponent },
{ path: '**', redirectedTo: 'error404', component: Error404Component }
];
export const APP_ROUTES_PROVIDER = [
provideRouter(APP_ROUTES)
];
And my boot:
import { bootstrap } from '#angular/platform-browser-dynamic';
import { enableProdMode } from '#angular/core';
import { AppComponent, environment } from './app/';
import { HTTP_PROVIDERS } from '#angular/http';
import { APP_ROUTES_PROVIDER } from './app/app.routes'
if (environment.production) {
enableProdMode();
}
bootstrap(AppComponent, [APP_ROUTES_PROVIDER, HTTP_PROVIDERS]);
Chrome's Network indicates when I navigate back or forward on the same component between animal/:cat and animal/:dog via browser buttons, no new Http requests are made unless I change to a different component.
This is the last bug I'm down too. Would appreciate any help, thanks!