I have this method (Angular 9, so Typescript) which is used to retrieve a brend new Json Web Token for authenticate the current user
getNewAccessToken(){
return this.httpClient.post<Token>(`${this.baseService.baseUrl}auth-token-refresh/`, { refresh: this.getRefreshToken() }, this.baseService.httpOptions).pipe(
tap((response:Token) => {
this.cookieService.set(environment.tokenAccessName, response.access, null, '/', null, null, 'Strict');
this.isLoggedIn.next(true);
}
}
When I subscribe to this method, I check for errors like so
this.authService.getNewAccessToken().subscribe(
res => { //do something with res... },
error => throw error //catch error
);
Could I move the error detection directly inside my observable code using pipe and catchError? The code would turn to this
getNewAccessToken(){
return this.httpClient.post<Token>(`${this.baseService.baseUrl}auth-token-refresh/`, { refresh: this.getRefreshToken() }, this.baseService.httpOptions).pipe(
tap((response:Token) => {
this.cookieService.set(environment.tokenAccessName, response.access, null, '/', null, null, 'Strict');
this.isLoggedIn.next(true);
},
catchError(error => {
throw error;
})
));
}
I think this is a sort of centralized way of managing errors in observable.
Generally, is error handling better on observables or on their observers?
What are the pros and cons of these two approaches? Is there any difference in terms of performance?
I think the same question can be raised for promises
Yeah, and it is the good practice to move error handling into pipe as it is separation of concern. It separates data retrieving from the presentation of the data.
An example of code of Angular 2 documentation:
return this.http.get<Hero[]>(this.heroesUrl)
.pipe(
catchError(this.handleError('getHeroes', []))
);
Related
I have 2 services. Both services have individual subjects and I am exposing both of them to other components by returning asObservable. Both have an addDataN function and both are emitting data to respecting subjects in services by on subject,so the getdataN method which is also emitting data on subject.
Now at the consumer side I am receiving two services completely independent. In component I am subscribing to the Listener, which are returning asObservable, and getdataN funnction, which are emitting data
Serv1
getdata1() {
this.http.get<{message:string,Data1:any}>('http://localhost:3000/api/1')
.pipe(map((data1)=>{
return Data1.Data1.map(data=>{
return {
id: data._id,
data1Title:data1.data1Title,
}
})
})).subscribe((data1) => {
this.data1=data1
this.serv1Subject.next([...this.data1])
})
}
addData1(){
this.http.post<{message:string,Data1:any}>('http://localhost:3000/api/1',dataObject)
.subscribe((data)=>{
this.data1=data1
this.serv1Subject.next([...this.data1])
})
}
getData1Listener() {
return this.serv1Subject.asObservable()
}
Serv2
getdata1() {
this.http.get<{message:string,Data2:any}>('http://localhost:3000/api/2')
.pipe(map((data1)=>{
return Data2.Data2.map(data=>{
return {
id: data._id,
data1Title:data1.data1Title,
}
})
})).subscribe((data1) => {
this.data1=data1
this.serv2Subject.next([...this.data1])
})
}
addData2(){
this.http.post<{message:string,Data2:any}>('http://localhost:3000/api/2',dataObject)
.subscribe((data)=>{
this.data1=data1
this.serv2Subject.next([...this.data1])
})
}
getData2Listener() {
return this.serv2Subject.asObservable()
}
Now at consumer component at ngOnInit at beginning I want to make sure that both subjects emit data and perform function x on data received from both end. And after that both work independently.
ngOnInit(){
this.serv1.getdata1()
this.serv2.getdata2()
combineLatest(this.serv1.getData1Listener(), this.serv2.getData2Listener())
.subscribe(([data1,data2])=>{function x(){
something with data1,data2
}})
}
My Problem is with zip at beginning. Both work fine at beginning as both services emit, but after emitting data to serv1 the other is not emitted anything so it stuck there.
In withLatestFrom only recent data is received and I want to avoid spread operator
Is there any way I can cleanly implement this?
Any help is appreciated.
If I use the tap rxjs operator on an observable to call another observable, can I guarantee that it completes before the rest of the pipe?
The idea here is to have a service make an http call to the backend, if it's a good login, create a cookie, then return a mapped response to the consuming component. I want to make sure the cookie is added before continuing to make sure there are no race conditions.
import { of, Observable } from 'rxjs';
import { map, tap } from 'rxjs/operators';
const httpObservable = loginAccount('fsdfds', 'fdsfsd');
httpObservable.subscribe(x => {
console.log(x);
});
function loginAccount(username, password): Observable<any> {
const httpResponse = of({ loggedIn: false, data: 'faketokenfrombackend' });
return httpResponse.pipe(
// Will this AWLAYS complete before map?
tap(resp => fakeLocalStorage('Do something with the result')),
// Will this AWLAYS complete before map?
tap(resp => fakeLocalStorage('Do something else with the result')),
map(resp => {
if (!resp.loggedIn)
return { success: false, message: 'really bad thing happened' };
else
return {success: true, message: 'WEEEEEE, it worked!'}
}));
}
function fakeLocalStorage(data: string): Observable<boolean> {
console.log('adding token to cookie');
return of(true);
}
The above script outputs this to the console window as expected, but can I rely on it?
adding token to cookie
adding token to cookie
{success: false, message: "really bad thing happened"}
Yes, RxJS will run the piped operators in order. As long as the tap operators are synchronous they will complete before the map operator is run. If they do anything asynchronous they will not.
Currently i'm working with this module for angular: Angular web bluetooth
I can find an example how to read characteristics and got it working. But there are no samples to write (or notify). I am pretty sure that this module is capable of this tasks, see here, but my knowledge of Angular (or even JS?) isnt enough.
Did someone have any idea where to find examples or/and can provide them?
Snippet to read battery level:
getBatteryLevel() {
console.log('Getting Battery Service...');
try {
return this.ble
.discover$({
acceptAllDevices: true,
optionalServices: [BatteryLevelService.GATT_PRIMARY_SERVICE]
})
.mergeMap((gatt: BluetoothRemoteGATTServer) => {
return this.ble.getPrimaryService$(
gatt,
BatteryLevelService.GATT_PRIMARY_SERVICE
);
})
.mergeMap((primaryService: BluetoothRemoteGATTService) => {
return this.ble.getCharacteristic$(
primaryService,
BatteryLevelService.GATT_CHARACTERISTIC_BATTERY_LEVEL
);
})
.mergeMap((characteristic: BluetoothRemoteGATTCharacteristic) => {
return this.ble.readValue$(characteristic);
})
.map((value: DataView) => value.getUint8(0));
} catch (e) {
console.error('Oops! can not read value from %s');
}
}
Thank you.
Edit: thanks to the assistance of Aluan Haddad i was able to perform a write request, with the right Service and Characteristic UUID
.mergeMap((characteristic: BluetoothRemoteGATTCharacteristic) => {
let value = new Uint8Array(1);
value[0] = 2;
return this.ble.writeValue$(characteristic, value);
}).subscribe();
is using the same reducer to update different parts of state an anti pattern?
Like my data reducer has a GET_DATA_DONE action, updates state.data and then in another instance you use fetch something else and call GET_DATA_DONE to update state.somethingElse?
Or would you do something like GET_SOMETHING_DATA_DONE & so on.. multiple diff actions doing the same thing? (Hardly DRY)
reducers.js
export const reducer = (state, action) => {
switch (action.type) {
case actions.GET_DATA_REQUESTED:
return { ...state, isLoading: true };
case actions.GET_DATA_DONE:
return { ...state, isLoading: false, data: action.payload };
case actions.GET_DATA_FAILED:
return { ...state, isLoading: false, isError: true }
default:
return state;
}
};
actions.js
export function getDataRequested() {
return {
type: 'GET_DATA_REQUESTED'
};
}
export function getDataDone(data) {
return {
type: 'GET_DATA_DONE',
payload: data
};
}
export function getDataFailed(error) {
return {
type: 'GET_DATA_FAILED',
payload: error
};
};
export function getDataEpic(action$) {
return action$.ofType(GET_DATA_REQUESTED)
.mergeMap(action =>
ajax.getJSON(action.url)
.map(response => getDataDone(response))
.catch(error => getDataFailed(error))
);
}
What't the best way to structure the app such that , getDataEpic acts like a api factory and the data returned from getDataDone(response) can be passed to another reducer to update a part of state based on the action, for example a cities action using getDataDone reducer dispatches another action which updates state.cities with the response?
Edit: I've made an app with rx-observable & redux calling 3 different api's but I ended up with a lot of duplicate code and just not happy with the solution, so i want to build a properly architectured app
Hope I was clear enough.
Thanks a lot!
I don't think it's an anti-pattern if deals with state of it's own children, but if used too much, it could certainly get you in trouble. Having it modify totally unrelated state is a definite anti-pattern. According to the docs the first line hits the nail on the head.
For any meaningful application, putting all your update logic into a single reducer function is quickly going to become unmaintainable.
We're not talking about 'all our data' but we are talking about all the data from a single API call.
It's not too complicated in regards to setting a loading flag but in a real world app would get significantly more complicated for a reducer that is both setting a loading flag, error flag and 'data'. And that is assuming we even know what the data being requested is.
In your example, if the intention is to create an API factory through reducers, we have to assume an API could return any number of different data structures, right now it could be a string or an int but what if it's a deeply nested Object? How would you access this data and differentiate it between another piece of data?
Let's say we have an app with this data structure for just the errors:
{
"errors": {
"byId": {
"1": {
"code": 500,
"message": "There was an internal server error"
},
"2": {
"code": 400,
"message": "There was another error."
},
"3": {
"code": 999,
"message": "Wow! weird error."
},
},
"all": ["1", "2", "3"]
}
}
I might have a byId reducer that returns a computed key with another reducer as the value.
byId = (state={}, action) => {
if (action.type === 'ADD_ERROR') {
...state,
[action.id]:error_reducer(state[action.id], action)
} else {
return state
}
}
error_reducer might look like
errorReducer = (state={}, action) => {
if (action.type === 'ADD_ERROR') {
code: action.code,
message: action.message
} else {
return state
}
}
I think it makes more sense to have errorReducer handle both code and message because we know that they are both mutually inclusive pieces of data where as each error is mutually exclusive (different ids) and so require their own reducer.
Another major advantage of this when dealing with real-world applications is that when the data is separated one action can update state across MANY different areas of our app. When reducers handle multiple pieces of state these pieces of tied state become harder to update.
There are many different patterns you can employ with your reducers and none of them are wrong however I have found this pattern to work very well for me and i've used it successfully in a quite complex production app.
Having said all that, one possible approach for your AJAX function is to write a generic action that accepts an object that contains your dispatches.
By using a library like redux-thunk you can execute multiple dispatches to update different parts of your state with different pieces of data. I won't explain redux-thunk here as I think it's beyond the scope of the question.
an example object might look like this:
{
getDataRequested: function () {
return {
type: 'GET_DATA_REQUESTED'
};
},
getDataFailed: function (error) {
return {
type: 'GET_DATA_FAILED',
payload: error
};
},
getDataDone: function (data) {
return {
type: 'GET_DATA_DONE',
payload: data
};
}
}
then you can pass this object of callbacks to your main AJAX function along with your API endpoint, REST request type etc.
I hope this helps.
I am struggling to convert Node streams to Rxjs Observables.
The streaming by itself works great when I try 1 URL.But, when I try to map the same function over an array of URLS, I get errors.
I am using Rx.Node to convert the stream into an Observable.
This is what I'm currently trying
// data_array is an array of 10 urls that I'm scraping data from.
let parentStream = Rx.Observable.from(data_array);
parentStream.map(createStream).subscribe(x => console.log(x), (e)=> console.log('Error', e), console.log('Complete'));
function createStream(url){
return RxNode.fromStream(x(url, '#centercol ul li', [{name: 'a', link: 'a#href'}]).write().pipe(JSONStream.parse('*')))
}
But this is the output X 10(the number of URLS in data_array)
RefCountObservable {
source:
ConnectableObservable {
source: AnonymousObservable { source: undefined, __subscribe: [Function] },
_connection: null,
_source: AnonymousObservable { source: [Object], __subscribe: [Function: subscribe] },
_subject:
Subject {
isDisposed: false,
isStopped: false,
observers: [],
hasError: false } },
_count: 0,
_connectableSubscription: null }
I first thought flatMap would work because it's flattening observables in an observable....but when I try flatMap, I get this:
Complete
Error TypeError: unknown type returned
However, if I do this:
This works for 1 URL, but I can't capture all of the urls in the data_array in one stream.
let stream = RxNode.fromStream(x(url, '#centercol ul li', [{name: 'a', link: 'a#href'}]).write().pipe(JSONStream.parse('*')))
stream.subscribe(x => console.log(x), (e)=> console.log('Error', e), console.log('Complete'))
I feel like I'm misunderstanding something not only because it clearing isn't working for multiple URLS, but even when it does work in the second example....I get 'Complete' first before all data comes in.
Clearly, I'm misunderstanding something. Any help would be wonderful. Thanks.
*UPDATE*
I tried a different path, which works, but does not use Node Stream. Node streams would be ideal, so still would like to make the above example work.
The approach I used next was to wrap a promise around my web scraping function, which is scrape below. This works, but the result is ten huge arrays with all the data from each URL in each array. What I really want is a stream of objects that I can compose a series of transformations as the data objects pass through.
Here is different, but working approach:
let parentStream = Rx.Observable.from(data_array);
parentStream.map(url => {
return Rx.Observable.defer(() => {
return scrape(url, '#centercol ul li', [{name: 'a', link: 'a#href'}]);
})
})
.concatAll()
.subscribe(x => console.log(x), (e)=> console.log('Error', e), console.log('Complete'));
function scrape(url, selector, scope) {
return new Promise(
(resolve, reject) => x(
url,
selector,
scope
)((error, result) => error != null ? reject(error) : resolve(result))
);
}
*Solution*
I figured it out. I have attached the solution below:
Instead on using RxNode, I opted to use Rx.Observable.fromEvent().
Node streams emit events, whether it be an new data, error or on complete.
So the fromEvent static operator is listening for the 'data' event and creates a new Observable for each event.
I then mergeAll those, and subscribe. Here's the code:
let parentStream = Rx.Observable.from(data_array);
parentStream.map((url)=> { return createEventStream(url); } ).mergeAll().subscribe(x => console.log(x), (e)=> console.log('Error', e), console.log('Complete'));
function createEventStream(url){
return Rx.Observable.fromEvent(x(url, '#centercol ul li', [{name: 'a', link: 'a#href'}]).write().pipe(JSONStream.parse('*')), 'data');
}