How to catch error with ngrx - redux

I am trying to catch an error with ngrx and angularfire2 firetore.
Here is the effect
#Effect()
delete$: Observable<Action> = this.actions$
.ofType(actions.DELETE)
.map((action: actions.Delete) => action.id)
.mergeMap(id =>
of(this.afs.doc<Collection(`collections/${id}`).delete()))
.map(() => new actions.Success())
.catch(err => of (new actions.Fail(err.message )));
and actions:
export class Success implements Action {
readonly type = SUCCESS;
constructor() {
console.log('success')
}
}
export class Fail implements Action {
readonly type = ERROR;
constructor(public payload: any) {
console.log('failed');
}
}
I keep getting success even if the action is not completed. What is the good way to do this?

Currently, this code is trying to catch errors on the entire stream, rather than the inner observable that matters.
Try this:
#Effect delete$: Observable<Action> = this.action$
.ofType(actions.DELETE)
.map((action: actions.Delete) => action.id)
.mergeMap(id => {
return of(this.afs.doc<Collection(`collections/${id}`).delete())
.map(() => new actions.Success())
.catch(err => of(new actions.Fail(err.message))
});
See the docs on mergeMap and this helpful answer on SO about it. The stream that may throw an error is the internal of(this.afs.doc...) observable, so the .catch should be on that observable. In the current implementation (from your example above), it will always map the result of of(this.afs.doc...) to a new actions.Success(), whether it fails or not.

Related

Effect "RouterEffects.navigate$" dispatched an invalid action: {"path":["..."]}

I'm using #ngrx/router-store and setting up a CustomerSerializer to manage routes in my state tree. I'm using the example in the #ngrx/router-store configuration docs. Also setup a RouterEffects class with a navigate$ effect to a specified path:
navigate$ = createEffect(():any => {
return this.actions$.pipe(
ofType(RouterActions.GO),
map((action: RouterActions.Go) => action.payload),
tap(({ path, query: queryParams, extras }) => {
this.router.navigate(path, { queryParams, ...extras });
})
)
}
);
When I dispatch a "GO" action with a payload, the Angular router navigates to the specified path, but I get this error in the console:
ERROR Error: Effect "RouterEffects.navigate$" dispatched an invalid action: {"path":["/admin"]}
I don't understand where the secondary action is being dispatched.
The problem is map((action: RouterActions.Go) => action.payload),
An effect always dispatches the next value back to the store.
This should be an action, but in this case it's the action's payload, resulting in the error.
To fix this you need to create non-dispatching effect.
navigate$ = createEffect(():any => {
return this.actions$.pipe(
ofType(RouterActions.GO),
map((action: RouterActions.Go) => action.payload),
tap(({ path, query: queryParams, extras }) => {
this.router.navigate(path, { queryParams, ...extras });
})
)
}
// add this 👇
, {dispatch: false}
);

Need to show newly added records/rows in Angular11 using web api service without page refresh

Component.ts
ngOnInit() {
this.employeeservice.getEmp_ServiceFun().subscribe(
(data: Employee[]) => this.employees = data,
error => this.error = error
);
}
service.ts
constructor(private http: HttpClient) { }
public getEmp_ServiceFun(): Observable<Employee[]> {
return this.http.get<Employee[]>(this.serverUrl + 'employees')
.pipe(
catchError(this.handleError)
);
}
it seems a bit strange to me to run the ngoninit method again since it is meant to run only once. I would wrap the employeeservice method in an observable interval. dont forget to unsubscribe though. otherwise it would keep calling the getEmp_ServiceFun until the whole app closes
ngOnInit() {
interval(1000).pipe(
map(() => {this.employeeservice.getEmp_ServiceFun().subscribe(
(data: Employee[]) => this.employees = data,
error => this.error = error
);})}
this would replace the whole array instead of just adding to it though. I would take a second look at the getEmp_ServiceFun so that you can ask only for new data and not all data and then push it to the array.
edit: even better would be to not subscribe in the the map but move it to after the pipe. you might need to use a switchMap
I used setTimeout() to refresh a component, it is working fine, but now I just need to check, is it good practice or not?
ngOnInit() {
this.employeeservice.getEmp_ServiceFun().subscribe(
(data: Employee[]) => this.employees = data,
error => this.error = error
);
//refresh this component
this.timeout = setTimeout(() => { this.ngOnInit() }, 1000 * 1)
}

Angular 2 chain route params and http subscriptions

I am trying to get route params and then get data from the service
this.route.params
.switchMap(params => this.service.getData(params['id']))
.subscribe(value => this.value = value,
error => console.log(error));
This works fine, until first error. After the error this line doesn't calls no more params => this.noteService.GetParams(params['id']).
I can write something like this, but i think there is a better way
this.route.params.subscribe(
params => {
this.service.getData(params['id']).subscribe(
result => console.log(result),
error => console.log(error))
});
My service
public getData(id): Observable<any> {
return this.http.get('api/data/' + id)
.map(data => data.json())
.catch(error => Observable.throw(error));
}
Update
This answer helped me a lot to understand what is going on.
When I call Observable.throw(error) subscription to route params stops with an error. So instead of throwing error I just need to return empty observable.
my.component.ts
this.route.params
.switchMap(params => this.service.GetData(params['id']))
.subscribe(result => {
if (result) this.data = result;
else console.log('error');
});
my.service.ts
public GetData(id): Observable<any> {
let url = 'api/data' + id;
return this.http.get(url)
.map(data => data.json())
.catch(error => Observable.of(null));
}
I'm building a github users application right now and had the same problem.
Here is a solution that works for me:
users.service.ts
public getByUsername(username: string): Observable<any[]> {
return this.http
.get(`${this.url}/${username}`)
.map((res: Response) => res.json());
}
user.component.ts
ngOnInit() {
this.sub = this.route.params
.flatMap((v: any, index: number) => {
return this.usersService.getByUsername(v.name);
})
.subscribe(data => this.user = data);
}
So, basically the flatMap operator does the trick.
Here is link to another question,
helping me to figure out how things work with chaining RxJS Observables

Angular2 Observable HTTP Data not filled

I´m fairly new to Angular2 and want to load data from a JSON-file.
I read the AngularIO Article and did the tutorial, but i still need some help.
I splitted the "getting Data" into an apiService which loads data and a component, whicht gets the data from the apiService.
Because it´s simpler, for this question I want to load the data directly to the component without the apiService.
This is the component:
export class StatusComponent implements OnInit {
private status = {};
private error: string;
constructor(private router: Router, private variables: Variables, private apiService: ApiService, private http: Http) {
if (!this.variables.getIsLoggedIn()) {
this.router.navigate(['login']);
}
}
ngOnInit() {
this.getStatus();
}
getStatus() {
this.http.get('../app/apiFiles/status.json')
.map((res:Response) => res.json())
.subscribe(
data => { this.status = data},
err => console.error(err),
() => console.log('done')
);
console.log(JSON.stringify(this.status));
}
}
This is JSON:
{
"status": {
"timercount": "0",
"reccount": "0"
}
}
In the component, the getStatus() is correctly load, and goes through without an error, but the variable i fill status isn´t filled.
After the getStatus() the variable is still {}.
Moreover, the output of console.log(JSON.stringify(this.status)); is {}.
I hope someone can help me and solve it !
Thank so much!
That's because console.log(JSON.stringify(this.status)); is executed after http call is done. Have in mind that all http calls are asynchronous methods. Try this:
getStatus() {
this.http.get('../app/apiFiles/status.json')
.map((res:Response) => res.json())
.subscribe(
data => { this.status = data},
err => console.error(err),
() => console.log(this.status);
);
}
You'll see this will print your result because () => console.log(this.status); is executed after http call is successfuly completed.
The correct solution is this:
getStatus() {
this.http.get('../app/apiFiles/status.json')
.map((res:Response) => res.json())
.subscribe(
data => { this.status = data.status},
err => console.error(err),
() => console.log('done')
);
console.log(JSON.stringify(this.status));
}
So, you have to use data.status, because the JSON file begins with "status" ;)

Assign AJAX data to class variable in AngularJS2

I am doing the following code and unable to figure out that why the data I am obtaining through AJAX is not being assigned to the class variable which is this.users
Code Snippet
getUsers() {
this.http.get('/app/actions.php?method=users')
.map((res:Response) => res.json())
.subscribe(
res => { this.users = res}, // If I console 'res' here it prints as expected
err => console.error(err),
() => console.log('done')
);
console.log(this.users) // Printing 'undefined'
return this.users;
}
Any help will be much appreciated. This (http://prntscr.com/cal2l1) is link to my console output.
It is an asynchronous call, so you don't fetch data right away. However, if you setTimeout() on console.log(), it will be printed correctly because printing will occur after the data is fetched:
getUsers() {
this.http.get('/app/actions.php?method=users')
.map((res:Response) => res.json())
.subscribe(
res => { this.users = res}, // If I console 'res' here it prints as expected
err => console.error(err),
() => console.log('done')
);
setTimeout(() => {
console.log(this.users) // Printing 'undefined'
}, 1000);
return this.users;
}
Reason for Problem
Well, it was really a silly mistake which I was making here. Since, getUsers() was being called after the DOM was loaded so it was assigning the value to class variable which is this.users after loading of DOM which restricted my page to load the required values at page loading stage (not after page loading).
Solution
Angular2 comes with a hook called OnInit or ngOnInit(). I was supposed to call the function in this event as follows.
getUsers() {
this.http.get('/app/actions.php?method=users')
.map((res:Response) => res.json())
.subscribe(
res => { this.users = res},
err => console.error(err),
() => console.log('done')
);
console.log(this.users)
return this.users;
}
ngOnInit() {
getUsers();
}
Documentaion of OnInit: https://angular.io/docs/ts/latest/api/core/index/OnInit-class.html
Also the following documentation came up as a helping tool:
https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html

Resources