Angular-Redux Epics - redux

Getting the following error message in console when using the angular-redux library. Also, Redux won't catch or listen for actions after the error occurs. I've searched, including the documentation but nothing points out to fix the error.
Am I missing something?
Error
core.js:1427 ERROR Error: Actions must be plain objects. Use custom middleware for async actions.
at Object.performAction (<anonymous>:3:2312)
at liftAction (<anonymous>:2:27846)
at dispatch (<anonymous>:2:31884)
at eval (createEpicMiddleware.js:67)
at SafeSubscriber.dispatch [as _next] (applyMiddleware.js:35)
at SafeSubscriber.__tryOrUnsub (Subscriber.js:240)
at SafeSubscriber.next (Subscriber.js:187)
at Subscriber._next (Subscriber.js:128)
at Subscriber.next (Subscriber.js:92)
at SwitchMapSubscriber.notifyNext (switchMap.js:127)
Here's code
Component
import { Component, OnInit } from '#angular/core';
import { Observable } from 'rxjs/Observable';
import { select } from '#angular-redux/store';
import { ScheduleActions } from '../store/actions'
#Component({
selector: 'app-page2',
templateUrl: './page2.component.html',
styleUrls: ['./page2.component.css']
})
export class Page2Component implements OnInit {
#select(['schedule', 'scheduleList']) values: any;
constructor(public actions: ScheduleActions) { }
ngOnInit() {
this.actions.loadSchedule();
}
}
Actions
//schedule-actions.ts
import { Injectable } from '#angular/core';
import { NgRedux } from '#angular-redux/store';
import { Schedule } from '../../model/schedule.model';
#Injectable()
export class ScheduleActions {
static readonly LOAD_SCHEDULE = 'LOAD_SCHEDULE';
static readonly LOAD_SCHEDULE_SUCCESS = 'LOAD_SCHEDULE_SUCCESS';
constructor(private ngRedux: NgRedux<any>){}
loadSchedule(){
this.ngRedux.dispatch({
type: ScheduleActions.LOAD_SCHEDULE
});
}
}
Reducer
//schedule-reducer.ts
import { ScheduleActions } from '../actions';
export interface SCHEDULE_STATE {
scheduleList: any,
scheduleDetail: any
}
const initialState: SCHEDULE_STATE = {
scheduleList: [],
scheduleDetail: {}
}
export const ScheduleReducer = (state: SCHEDULE_STATE = initialState, action): SCHEDULE_STATE => {
switch(action.type){
case ScheduleActions.LOAD_SCHEDULE_SUCCESS:
return {...state, scheduleList: action.payload };
case ScheduleActions.LOAD_SCHEDULE_DETAIL_SUCCESS:
return {...state, scheduleList: action.payload };
case ScheduleActions.CREATE_SCHEDULE_SUCCESS:
return {...state, scheduleDetail: action.payload };
default:
return state;
}
}
Epics
//schedule-epic.ts
import { Injectable } from '#angular/core';
import { ActionsObservable, ofType } from 'redux-observable';
import { ScheduleService } from '../services';
import { ScheduleActions } from '../actions';
import { Observable } from 'rxjs/Observable';
#Injectable()
export class ScheduleEpic {
constructor(private service: ScheduleService,
private actions: ScheduleActions
){}
loadScheduleEpic = (action$: ActionsObservable<any>) => {
return action$.ofType(ScheduleActions.LOAD_SCHEDULE)
.mergeMap(action => {
return this.service.loadSchedule().map(result => {
this.actions.loadScheduleSuccess(result)
})
})
}
}
Service
//schedule-service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
import { Schedule } from '../../model/schedule.model';
#Injectable()
export class ScheduleService {
private API_URL: String = "http://mockserver.io/v2";
constructor(private http: HttpClient){}
loadSchedule(){
return this.http.get(this.API_URL + '/5a6225153100004f2bde7f27').map(res => res)
}
}

That error means you dispatched something that was not an action--in this case, your epic emitted something that wasn't an action.
Thankfully, it's an easy fix! You're just missing a return statement in your map
return this.service.loadSchedule().map(result => {
this.actions.loadScheduleSuccess(result)
})
// change it to this:
return this.service.loadSchedule().map(result => {
return this.actions.loadScheduleSuccess(result)
})

Related

The dom is not reflective of the actual value wen using onPush strategy with ngrx store subscription

component file:
// Angular and 3rd party libs imports
import { ChangeDetectionStrategy, Component, OnInit } from '#angular/core';
import { Store } from '#ngrx/store';
import { UntilDestroy, untilDestroyed } from '#ngneat/until-destroy';
// Utils
import { ApiLoadInfo, ApiStateEnum } from 'src/app/shared/utils/states';
// Services
import { TestPortalService } from '../../../testportal.service';
import { SharedClient } from 'src/app/shared/services/shared.service';
// Redux
import {
CandidateInstructionsState,
Quiz,
Instruction,
PageEnum,
LandingPageData
} from '../redux/candidate-instructions.state';
import * as instructionActions from '../redux/candidate-instructions.action';
import * as instructionSelects from '../redux/candidate-instructions.selector';
import { ActivatedRoute } from '#angular/router';
#UntilDestroy()
#Component({
selector: 'candidate-instructions-landing',
templateUrl: './instructions-landing.component.html',
styleUrls: ['./instructions-landing.component.scss', '../common.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class CandidateInstructionsLandingComponent implements OnInit {
// Exposing constants to html template
ApiStateEnum = ApiStateEnum;
PageEnum = PageEnum;
// Variables
initDataLoadState: ApiLoadInfo;
data: LandingPageData;
constructor(private _store: Store<CandidateInstructionsState>,
private _activatedRoute: ActivatedRoute,
private _testPortalService: TestPortalService,
) {
_store
.select(instructionSelects.selectInitDataLoadState)
.pipe(untilDestroyed(this))
.subscribe((initDataLoadState) => {
console.log('is same ref?:', this.initDataLoadState === initDataLoadState)
this.initDataLoadState = initDataLoadState;
console.log(initDataLoadState)
console.log('----------')
});
_store
.select(instructionSelects.selectLandingData)
.pipe(untilDestroyed(this))
.subscribe((data) => {
this.data = data;
});
}
ngOnInit() {
this.loadInstructions();
}
loadInstructions() {
this._store.dispatch(instructionActions.setInitData()); // sets state to 'loading'
this._testPortalService.getTestInstructions(
this._activatedRoute.snapshot.params.quizOrInviteId,
(error, response) => {
if (error) {
// sets state to 'error'
this._store.dispatch(instructionActions.setInitDataFail({ errmsg: error.toString() }));
} else {
// sets state to 'loaded'
this._store.dispatch(instructionActions.setInitDataSuccess({ instructions: response }));
console.log(response);
}
}
);
}
}
html:
{{ initDataLoadState.state }}
console output:
ui:
I thought when onPush is set, the template will re-render if the variable ref is changed. And since redux store is immutable that is always supposed to happen (confirmed by logging in the console). But still the actual component data is not in sync with the UI ie. component value = "loaded" but value in ui = "loading". Why is it so?
If you don't want to or can't use the pushPipe you could do something like this to subscribe to the store data:
import { Component, OnDestroy, OnInit } from '#angular/core';
import { Subscription } from 'rxjs';
import { Store } from '#ngrx/store';
import { getData } from 'path/to/store';
import { YourType } from 'path/to/type';
#Component({
selector: 'subscribing-component',
templateUrl: './subscribing.component.html'
})
export class SubscribingComponent implements OnInit, OnDestroy {
data: YourType;
dataSubscription: Subscription;
constructor(store: Store) {}
ngOnInit(): void {
this.dataSubscription = this.store.select(getData).subscribe((data) => {
this.data = data;
});
}
// don't forget to unsubscribe
ngOnDestroy(): void {
if (this.dataSubscription) {
this.dataSubscription.unsubscribe();
}
}
}

working with FirestoreDocument

I am working with FirestoreDocument and I am trying to retrieving document data.I am getting the post undefined.Below is my code for service and Component
PostService.ts
import { Injectable } from '#angular/core';
import { AngularFirestore,AngularFirestoreCollection,AngularFirestoreDocument} from 'angularfire2/firestore'
import {Post} from './post';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/map';
#Injectable()
export class PostService {
postsCollection: AngularFirestoreCollection<Post>
postDoc: AngularFirestoreDocument<Post>
constructor(private afs:AngularFirestore) {
this.postsCollection = this.afs.collection('posts',ref => ref.orderBy('published','desc'));
}
getPosts(){
return this.postsCollection.snapshotChanges().map(actions=>{
return actions.map(a=>{
const data=a.payload.doc.data() as Post
const id=a.payload.doc.id
return {id, ...data}
})
})
}
getPostData(id: string){
this.postDoc=this.afs.doc<Post>(`posts/${id}`)
return this.postDoc.valueChanges()
}
}
PostDetailComponent.ts
import { PostService } from './../post.service';
import { Component, OnInit } from '#angular/core';
import {ActivatedRoute} from '#angular/router';
import {Post} from '../post';
#Component({
selector: 'app-post-detail',
templateUrl: './post-detail.component.html',
styleUrls: ['./post-detail.component.css']
})
export class PostDetailComponent implements OnInit {
post: Post
constructor(
private route:ActivatedRoute,
private postService:PostService
) { }
ngOnInit() {
this.getPost();
console.log(this);
}
getPost(){
const id= this.route.snapshot.paramMap.get('id')
return this.postService.getPostData(id).subscribe(data=>this.post=data)
}
}
PostDetailComponent from console
PostDetailComponent {route: ActivatedRoute, postService: PostService}
post:undefined
postService:PostService {afs: AngularFirestore, postsCollection: AngularFirestoreCollection, postDoc: AngularFirestoreDocument}
route:ActivatedRoute {url: BehaviorSubject, params: BehaviorSubject, queryParams: BehaviorSubject, fragment: BehaviorSubject, data: BehaviorSubject, …}
__proto__:Object
My post from PostDetailComponent is undefined.
The rest of your code looks right, I think it's just that your console.log(this) is in the wrong spot.
ngOnInit() {
this.getPost(); <----- your getPost() function is asynchronous
console.log(this); <----- meaning this console.log runs before you get data from Firebase
so when this runs, posts is still undefined.
If you would console.log(this) *AFTER* you get data
from Firebase, then you should see the values OK.
}
getPost(){
const id= this.route.snapshot.paramMap.get('id')
return this.postService.getPostData(id).subscribe(data=>this.post=data)
^^^^^^^^^^^
ASYNC portion is right here
}
To fix it, move your console.log inside your subscription:
ngOnInit() {
this.getPost();
<--- From here
}
getPost(){
const id= this.route.snapshot.paramMap.get('id')
return this.postService.getPostData(id).subscribe(data=> {
this.post=data;
console.log(this); <--- To here
});
}
EDIT - Additional troubleshooting ref comments below.
getPost(){
const id= this.route.snapshot.paramMap.get('id')
console.log('id from route params is: ' + id); <---- log the *id* too
return this.postService.getPostData(id).subscribe(data=> {
this.post=data;
console.log(data); <--- log *data* instead
});
}
Since angular 6 there were changes on rxjs
// replace import 'rxjs/add/operator/map';
with import { map } from 'rxjs/operators';
Then include .pipe() inside PostService.ts file in getPosts() function
like this:
getPosts() {
return this.postCollection.snapshotChanges().pipe(map(actions => {
return actions.map( a => {
const data = a.payload.doc.data() as Post
const id = a.payload.doc.id
return { id, ...data }
})
})
)
}
Home someone helps, cheers !!!

How to test effects in ngrx?

I just want to get my getUser effect below.
I'm using angular5, typescript and ngrx.
I'm open to alternative examples to what I have below.
This is my effect:
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/catch';
import { Effect, Actions } from '#ngrx/effects';
import { HttpErrorResponse } from '#angular/common/http';
import { Injectable } from '#angular/core';
import { map, switchMap } from 'rxjs/operators';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import * as fromUser from '../actions/user-state.actions';
import { UserService } from '../services/user-state.service';
import { UserStateState } from '../models/user-state.interfaces';
#Injectable()
export class UserStateEffects {
#Effect({ dispatch: true })
getUser$: Observable<any> = this.actions$
.ofType(fromUser.GET_USER)
.pipe(
switchMap(() => this.userService.getUser()),
map((result: any) => new fromUser.UserSuccess(result)))
.catch((error: HttpErrorResponse) => of(new fromUser.UserFailure(error)));
constructor(
private actions$: Actions,
private userService: UserService
) {}
}
This is my spec for the effect test:
import 'rxjs/add/observable/throw';
import { Actions } from '#ngrx/effects';
import { cold, hot } from 'jasmine-marbles';
import { empty } from 'rxjs/observable/empty';
import { HttpClientTestingModule } from '#angular/common/http/testing';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { TestBed } from '#angular/core/testing';
import * as actions from '../actions/user-state.actions';
import { UserService } from '#tradingapplication/user-state/src/state/services/user-state.service';
import { UserStateEffects } from '#tradingapplication/user-state/src/state/effects/user-state.effects';
import { UserStateState } from '../models/user-state.interfaces';
import { UserStateConstants } from '../../user-state.constants';
export class TestActions extends Actions {
constructor(){
super(empty());
}
set stream(source: Observable<any>){
this.source = source
}
}
export function getActions() {
return new TestActions();
}
fdescribe('UserStateEffects', function () {
let actions$: TestActions;
let service: UserService;
let effects: UserStateEffects;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ HttpClientTestingModule ],
providers: [
UserService,
UserStateConstants,
UserStateEffects,
{ provide: Actions, useFactory: getActions },
],
});
actions$ = TestBed.get(Actions);
service = TestBed.get(UserService);
effects = TestBed.get(UserStateEffects);
spyOn(service, 'getUser').and.returnValue(of(null));
});
it('should return user from GetUser', () => {
const action = actions.GET_USER;
const completion = new actions.UserSuccess({});
actions$.stream = hot('-a', { a: action });
const expected = cold('-b', { b: completion });
expect(effects.getUser$).toBeObservable(expected);
});
});
You might want to check out the NGRX example app from the NGRX/Google team. It utilizes Jasmine-Marbles for testing streams, as used in NGRX Effects. This way you can test the streams for being closed or not after execution and how many elements passed through them before the expected value arrived. And all of that with just a few dashes (ticks).

Angular 2 cannot read property 'get' of undefined

I have seen this error on SO quite a few times, all I can find on it is that I need to make sure that I have my service Provided in my app.modules, and then call it in my constructor of my component. I have done this and am still getting the error. Also I have both http and HTTPMODULES in my application. The error only occurs when I use the delete functionality in my application. Here is the error error_handler.js:45 EXCEPTION: Cannot read property 'get' of undefined, here is some relevant code....
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { HttpModule, JsonpModule } from '#angular/http'; <------------HTTP
import { AppComponent } from './app.component';
import { PostRantComponent } from './postRant.component';
import { PostDataService } from './PostData.Service'; <------------service
import { Constants } from './app.const.service';
import { Routing } from './app.routes';
import { FormsModule } from '#angular/forms';
#NgModule({
imports: [NgbModule.forRoot(), BrowserModule, Routing, FormsModule, HttpModule, JsonpModule],
declarations: [AppComponent,,PostRantComponent],
providers: [PostDataService, Constants],
bootstrap: [AppComponent]
})
export class AppModule { }
Service (tried cutting it down to just show relevant code)
import { Injectable } from '#angular/core';
import { Http, Response, Headers } from '#angular/http';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import { Observable } from 'rxjs/Observable';
import { PostViewModel } from './models/Post';
import { Constants } from './app.const.service';
#Injectable()
export class PostDataService{
private actionUrl: string;
private headers: Headers;
constructor( private _http: Http, private _constants: Constants ){
this.actionUrl = _constants.ServerWithApi;
this.headers = new Headers();
this.headers.append('Content-Type', 'application/json');
this.headers.append('Accept','application/json');
}
public GetAll = (): Observable<PostViewModel[]> => {
return this._http.get(this.actionUrl)
.map((response: Response) => <PostViewModel[]>response.json())
.catch(this.handleError);
}
public Delete = (id: string) =>{
return this._http.delete(this.actionUrl + id)
.map(res => res.json())
.catch(this.handleError);
}
}
Component
import { Component, Attribute, OnInit,ViewChild } from '#angular/core';
import { PostViewModel } from './models/Post';
import { PostDataService } from './PostData.Service';
import { Constants } from './app.const.service';
#Component({
selector: 'postRant',
templateUrl: 'html/postRant.html',
})
export class PostRantComponent implements OnInit {
txtTitle: string;
txtDescription: string;
public myPosts : Array<PostViewModel>;
public newPost : PostViewModel = new PostViewModel();
constructor(private auth:Auth, private _dataservice: PostDataService){
}
ngOnInit(){
this.getAllItems();
}
private getAllItems():void {
this._dataservice
.GetAll()
.subscribe((Post: Array<PostViewModel>) => this.myPosts = Post,
error => console.log(error),
() => console.log('get all items complete'))
}
delete(id){
console.log(id);
this._dataservice.Delete(id)
.subscribe((res) => {
this.myPosts = res;
});
var index = this.myPosts.findIndex(x => x.id == id);
this.myPosts.splice(index, 1);
}
}
If you are interested in all the code I have it posted on my git located here, however it is rather large.
EDIT
picture of error....
it appears that the error is produced by line 52 of PostData.Service.ts
i.e. var applicationError = error.headers.get('Application-Error');
this makes me guess that your GetAll Http call is erroring out, but the server you are asking for data is not returning data in the format of error.headers
Add a debugger; to the handleError and check the object that it is receiving.

How to make an observer to return as observable?

I'm new to rxjs and would like some help on how to solve this.
I want to pass an Observer to onAuthStateChanged(), which takes an observer object. The observer would do some work and emit a boolean value such that the boolean value can be returned as an Observable. How do I go about implement this bridge of from observable to observer?
export class AuthGuard implements CanActivate {
constructor(private firebase: FirebaseService, private router: Router) {
}
canActivate(): Observable<boolean> {
this.firebase.auth.onAuthStateChanged(/* an observer */)
return /* an Observable<boolean> */
}
}
Since onAuthStateChanged takes an observer as input, and returns the teardown function, we can simply wrap it with:
Rx.Observable.create(obs => firebase.auth().onAuthStateChanged(obs))
Actually for strange reasons this might not work, and we can do:
var onAuthStateChanged$ = Rx.Observable.create(obs => {
return firebase.auth().onAuthStateChanged(
user => obs.next(user),
err => obs.error(err),
() => obs.complete());
})
Now if you are unfamiliar with the Observable.create function, let me explain: create takes a onSubscribe function that hands in an observer and returns the teardown function. Doesnt that sounds very familiar with now onAuthStateChanged is build up? You hand in nextOrObserver and it returns the teardown!
(Now for strange reasons nextOrObserver did not accept an observer for me, so i switched to giving it a next function instead. Hench the code above.)
With the onAuthStateChanged$ set up, we can transform the stream using operators. All operators do is transform one observable into another, and RxJs has several dozen of these. In your case, it might look like this:
canActivate(): Observable<boolean> {
onAuthStateChanged$
.do(user => {if (!user) { this.router.navigate(['/login']); } })
.map(user => !!user)
.do(user => console.log('Authenticated?', user))
}
To benefit others, here's what I ended up writing and it seems to work well.
import 'rxjs/add/operator/do';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/take';
import { Observable } from 'rxjs/Observable';
import { ReplaySubject } from 'rxjs/ReplaySubject';
import { Injectable } from '#angular/core';
import { CanActivate, Router } from '#angular/router';
import { FirebaseService } from '../shared/firebase.service';
#Injectable()
export class AuthGuard implements CanActivate {
loggedInSubject: ReplaySubject<any>;
constructor(private firebase: FirebaseService, private router: Router) {
this.loggedInSubject = new ReplaySubject(1);
this.firebase.auth.onAuthStateChanged(this.loggedInSubject);
}
canActivate(): Observable<boolean> {
return this.loggedInSubject.map(user => {
if (!user) {
this.router.navigate(['/login']);
}
console.log('Authenticated?', !!user);
return !!user;
}).take(1);
}
}
Not sure if this is necessarily 'better' than the answers above, but it's certainly cleaner. I decided to create two properties on the AuthService, one as just a boolean to reflect whether the user is authenticated, and a userLoggedIn subject which basically emits the value of the boolean property. Both properties are bound with onAuthStateChanged(). So once the state changes, the authenticated property becomes true, if authenticated, otherwise false, and userLoggedIn emits this value using next() (next(this.authenticated)). On the AuthGuard I set CanActivate() to return a boolean or Observable<boolean>. First, if the authenticated property on the AuthService is checked, and if it is returns true, otherwise it maps the userLoggedIn subject to find out whether or not the user has been authenticated. This means that after the page refreshes the guard will return the value of the emitted subject because authenticated is not yet defined, so instead just waits for userLoggedIn to return. The reason to have a check for the authenticated property first is that if you tried to change page using a nav link nothing would happen because the guard only returns the emitted value of the subject, which is only called when the state of authorisation changes - i.e. login, logout, or page-refresh (re-bootstrapping application). Code below:
AuthService
import * as firebase from 'firebase';
import { Router } from '#angular/router';
import { Injectable, OnInit } from '#angular/core';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class AuthService implements OnInit {
authenticated: boolean;
userLoggedIn = new Subject<boolean>();
constructor(private router: Router) {}
ngOnInit() {
}
checkAuthStatus() {
firebase.auth().onAuthStateChanged((user) => {
this.authenticated = !!user;
this.userLoggedIn.next(this.authenticated);
});
}
login(email: string, password: string) {
firebase.auth().signInWithEmailAndPassword(email, password).then(() => {
this.authenticated = true;
this.router.navigate(['/']);
}).catch((error) => {
console.log(error);
});
}
logout() {
firebase.auth().signOut().then(function() {
this.router.navigate(['login']);
}.bind(this)).catch((error) => {
console.log(error);
});
}
}
AuthGuard
import { CanActivate, Router } from '#angular/router';
import { Injectable } from '#angular/core';
import { AuthService } from './auth.service';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
#Injectable()
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {
}
canActivate(): Observable<boolean> | boolean {
if(this.authService.authenticated) {
return true;
}
return this.authService.userLoggedIn.map((authenticated) => {
if(!authenticated) {
this.router.navigate(['login']);
}
return authenticated;
});
}
}
Here's the short version, a helper function you can place anywhere...
export function MakeAuthstateObservable(
auth: firebase.auth.Auth
): Observable<firebase.User> {
const authState = Observable.create((observer: Observer<firebase.User>) => {
auth.onAuthStateChanged(
(user?: firebase.User) => observer.next(user),
(error: firebase.auth.Error) => observer.error(error),
() => observer.complete()
);
});
return authState;
}
Similar approach:
./auth-guard.ts
import { Injectable } from '#angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '#angular/router';
import { Observable } from 'rxjs/Observable';
import { AuthService } from '../shared/auth.service';
#Injectable()
export class AuthGuard implements CanActivate {
constructor(
private router: Router,
private authService: AuthService) { }
canActivate(
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> | Promise<boolean> | boolean {
return this.authService.authState.map((auth) => {
if (auth == null) {
this.router.navigate(['auth']);
return false;
} else {
return true;
}
}).first();
}
}
./shared/auth.service.ts
import { Observable } from 'rxjs/Observable';
import { Observer } from 'rxjs/Observer';
import { FirebaseApp } from '../shared/firebase';
#Injectable()
export class AuthService {
public auth: firebase.auth.Auth;
public authState: Observable<firebase.User>;
constructor(public app: FirebaseApp) {
this.auth = app.auth();
this.authState = this.authStateObservable(app);
}
/**
* #function
* #desc Create an Observable of Firebase authentication state
*/
public authStateObservable(app: FirebaseApp): Observable<firebase.User> {
const authState = Observable.create((observer: Observer<firebase.User>) => {
this.auth.onAuthStateChanged(
(user?: firebase.User) => observer.next(user),
(error: firebase.auth.Error) => observer.error(error),
() => observer.complete()
);
});
return authState;
}
}
./shared/firebase.ts
import * as firebase from 'firebase';
export class FirebaseApp implements firebase.app.App {
name: string;
options: {};
auth: () => firebase.auth.Auth;
database: () => firebase.database.Database;
messaging: () => firebase.messaging.Messaging;
storage: () => firebase.storage.Storage;
delete: () => firebase.Promise<any>;
constructor() {
return firebase.initializeApp({
apiKey: 'AIzaSyC6pDjAGuqXtVsU15erxVT99IdB0t4nln4',
authDomain: 'inobrax-ebs-16552.firebaseapp.com',
databaseURL: 'https://inobrax-ebs-16552.firebaseio.com',
storageBucket: 'inobrax-ebs-16552.appspot.com',
messagingSenderId: '383622803653'
});
}
}

Resources