Angular 2 callback - javascript

I created a service that gets the some data from the api this is the code
getChallenges(): Observable<IChallenge[]> {
if (this._challengeUrl != null) {
return this.http.get(this._challengeUrl)
.map((res:Response) => <IChallenge[]>res.json())
.do(data => console.log('data: ' + JSON.stringify(data)))
.catch(this.handleError);
} else {
//;
}
}
and i subscribe inside the component where i want to use the service inside ngOnInit and everything is running my fine.
this._challengeService.getChallenges()
.subscribe(challenges => this.challenges = challenges,
error => this.errorMessage = <any>error);
but now i need to use a filter on the data which should run after ngInit finishes getting the data. this is the filter:
filterByLvl(lvl){
this.challenges.filter((obj)=> obj.level == lvl);
}
well my problem is when i try to put the function after the subscribe code i keep getting an empty array because the ngOnInit runs this function first and then gets the data. how can i inverse this? i need to get the data and then run this function.
so any ideas on how to do this? and thanks

I haven't tried ( don't have access to angular2 at work :-( ), but you can have multiple statements in the lambda function in subscribe.
this._challengeService.getChallenges()
.subscribe(challenges =>
{
this.challenges = challenges;
filterByLvl(expert_level);
},
error => this.errorMessage = <any>error
);

One method would be filter directly when it retrieves the data something like:
this._challengeService.getChallenges()
.subscribe(challenges => this.challenges = challenges.filter((obj)=> obj.level == lvl),
error => this.errorMessage = <any>error);
NOTE The lvl will be undefined so you've to define it someway with your logic

Related

Sequential API call using Rxjs in Angular with null return

I have a scenario where I need to make a sequential API call using RxJs in Angular, which I have done but I am facing this null error. For calling the 2nd api I will receive and id from the first API which can be null or undefined. So what I wanted to If if id is not available I will return of(null) otherwise will return the response. But there are some typescript error. Following is what I have done so far.
of(personId).pipe(
take(1),
switchMap((personId: string) => {
return this.personService.getById(personId).pipe(
concatMap((person: Person) => {
const classId = person?.class?.id || null;
let class$ = of(null);
if (classId) {
class$ = this.classService.getById(classId); // Will return Observable<Class>
}
return combineLatest([of(person), class$])
})
)
}),
tap(([person, class]) => {
console.log('Person: ', person);
console.log('Clas: ', class);
})
).subscribe()
class$ = this.classService.getById(classId);
On this line I am facing the 'TS2322: Observable is not assignable to Observable`
Any suggestion on how do I resolve this? Also can this code be improved?
you can just replace the conditional logic with this line
let class$= classId?this.classService.getById(classId):of(null)
The observable that is returned by this.classService.getById is different to the one that is returned by of(null), hence you cannot reassign the class$ variable with it.
However your problem can easily be overcome by simply using a ternary operator to define class$ as follows:
const class$ = person?.class?.id ? this.classService.getById(classId) : of(null);
First of all this of(personId) it seems weird.
Why not:
this.personService.getById(personId).pipe(
concatMap((person: Person) => {
const class$ = person?.class?.id
? this.classService.getById(person?.class?.id)
: of(null);
return combineLatest([of(person), class$]);
})
).subscribe(/*...*/);
The error TS2322: Observable is not assignable to Observable I think that it is self-described.

Web-Bluetooth API, can not update characteristics. time-dependent updating possible?

I try to get the characteristics everytime they change, the problem is, they change but the eventListener doesn't recognize it. So i only get the first value, after connecting to my BLE, but nothing happen after that. Is there something wrong in my code ?
Another Question, is there a way to update the characteristic every, i dont know 5 Seconds for example? And how would you do that, are there any code examples?(Maybe with setInterval() ? )
Thank you !
function test() {
console.log('Requesting Bluetooth Device...');
navigator.bluetooth.requestDevice({
acceptAllDevices: true,
optionalServices: ['af07ecb8-e525-f189-9043-0f9c532a02c7']
}) //c7022a53-9c0f-4390-89f1-25e5b8ec07af
.then(device => {
console.log('Gatt Server Verbindung');
return device.gatt.connect();
})
.then(server => {
console.log('Dose Service...');
return server.getPrimaryService('af07ecb8-e525-f189-9043-0f9c532a02c7');
})
.then(service => {
console.log('mgyh Characteristic...');
return service.getCharacteristic('a99e0be6-f705-f59c-f248-230f7d55c3c1');
})
.then(characteristic => {
// Set up event listener for when characteristic value changes.
characteristic.addEventListener('characteristicvaluechanged',dosechanged);
return characteristic.readValue();
})
.catch(error => {
console.log('Das geht nicht: ' + error);
});
}
function dosechanged(event) {
let dose = event.target.value.getUint8(0)+event.target.value.getUint8(1)*10+event.target.value.getUint8(2)*100 + event.target.value.getUint8(3)*1000+ event.target.value.getUint8(4)*10000;
console.log('Received ' + dose);
}
You missed a characteristic.startNotifications() call to start receive notification. example
setInterval would be fine to call readValue() every 5 seconds.

Nested subscribe and need all values to pass as body to API - Angular 6 RxJS

I am developing a table component and the data for the table component is to be populated on basis of three dropdown values. I need to pass in all three values to the API to get the desired response. I can achieve it using nested subscribes, which is a very bad way. but any change calls the API multiple times. How can I fix it? Most examples I found are for getting only the final subscribe value but in my case, I need all three. Any advice to achieve using tap and flatMap?
Please advice.
this._data.currentGroup.subscribe(bg => {
this.bg = bg;
this._data.currentUnit.subscribe(bu => {
this.bu = bu;
this._data.currentFunction.subscribe(jf => {
this.jf = jf;
this.apiService.getFunctionalData(this.bg, this.bu, this.jf)
.subscribe(
(data) => {
console.log(data)
}
);
});
});
});
This is what I did.
this._data.currentGroup.pipe(
tap(bg => this.bg = bg),
flatMap(bu => this._data.currentUnit.pipe(
tap(bu => this.bu = bu),
flatMap(jf => this._data.currentFunction.pipe(
tap(jf => this.jf = jf)
))
))
).subscribe();
This is a sample example of my dataService. I initialize my dataservice in the table component's constructor as _data.
changeGroup(bg: string) {
this.changeGroupData.next(bg);
}
private changeGroupData = new BehaviorSubject<string>('');
currentChangeGroup = this.changeGroupData.asObservable();
You can use combineLatest to combine the three Observables and then subscribe to all of them at once. It will emit a new value as soon as one of the three Observables changes.
combineLatest(this._data.currentGroup,
this._data.currentUnit,
this._data.currentFunction).subscribe(([bg, bu, jf]) => {
// do stuff
});
For an example, have a look at this stackblitz demo I created.

Why my effect is running serveral times after action is called?

I have this effect that request serveral values to retrive a product from service. Afer dispatch REQUEST_PRODUCTS is called one time as expected, but when I tried go to other location in the routing the this.apiMarketServices is called serveral times, this trigger the router navigate and this will redirect to previous page. The action REQUEST_PRODUCTS is dispatched one time. Why this effect is called serveral times?
Do I need add some kind of stop to the effect in order to avoid the called after the return GetSuccess and GetFailed?
#Effect()
requestProductsFromMarket = this.actions$
.ofType(REQUEST_PRODUCTS)
.withLatestFrom(this.store)
.switchMap(([action, store]) => {
const id = store.product.id;
return this.savedProducts.getProduct(id, 'store');
})
.switchMap(_ => this.stateService.getMarketId())
.switchMap(({ marketId }) =>
this.apiMarketServices.get(MARKETS_PROFILES + marketId)
)
.withLatestFrom(this.store)
.map(([r, store]) => {
const ser = r.data.map(s => s.legId);
const storSer =
store.product.serIds;
if (storSer.every(s =>ser.includes(s))) {
this.router.navigate([
`/products/edit/${store.products.id}`
]);
return GetSuccess;
} else {
return GetFailed;
}
})
.catch(() => of(GetQueryFailed));
The solution for defect is related to an Observable. In the debugging the "this.apiMarketServices.get(MARKETS_PROFILES + marketId)" was called several times, I realted this services like cause of the defect:
.switchMap(({ marketId }) =>
this.apiMarketServices.get(MARKETS_PROFILES + marketId)
)
But the real cause was the stateSevice, this behavior subject was updated with next, in anothers parts of the app.
.switchMap(_ => this.stateService.getMarketId())
In order to avoid those calls, I created a function in order to retrive the current value from the BehaviorSubject.
getCurrentMarketId(): ClientData {
return this.currentMarket.value; // BehaviorSubject
}
I added this function to the effect the call is once per dispatched effect.
...
.switchMap(([action, store]) => {
const id = store.product.id;
return this.savedProducts.getProduct(id, 'store');
})
.map(_ => this.stateService.getCurrentMarketId())
.switchMap(({ marketId }) =>
this.apiMarketServices.get(MARKETS_PROFILES + marketId)
)

Check Store before API call NgRx Angular

I am creating NgRx application but I am quite confused about its implementation as it is my first app with NgRx.
I have a store with "Companies" state. I gotta search the companies and return if found.
If the required company is not found it should call the API and fetch the results likewise but the process is circular and runs infinite time.
Here is my code:
this.companySearchCtrl.valueChanges
.pipe(
debounceTime(300),
distinctUntilChanged()
)
.subscribe(val => {
if (val !== ' ' || val !== '') {
this.store.select(getCompanys).subscribe(data => {
console.log(data);
//filter companies on the basis of search text
const filteredData = data.filter(x =>
x['name']
.toLowerCase()
.startsWith(this.companySearchCtrl.value.toLowerCase())
);
console.log(filteredData);
if (filteredData.length === 0) { //if data is not found in store
console.log('got a call');
this.store.dispatch(
new CompanyActions.find({
where: { name: { regexp: `${val}/i` } } // call to API to search with regExp
})
);
} else {
// if required data found in store
console.log('got no call');
this.filteredCompanies$ = of(filteredData);
}
});
}
});
This process runs fine if data is found in store. If data is not found in store or I dont get any results from API call it runs infinitely.
How can I make this correct?
Make a few conventions:
state.Companies = null is initial state if no request to server has been sent yet
state.Companies = [] is state after first request was sent but no companies returned from server
use createSelector that filters your companies based on criteria you need
use withLatestFrom in your effects which will enable you to check store state within effects
Now, turn the logic the other way around:
when you look for companies, first fire an action that will trigger effects
in that effect, check if state.Companies is null or not
if its null > fire api request
if its not null > fire an action that will trigger selector for filtering
if data was not found even if state.Companies was not null that means either you need to refresh your Companies collection or the value doesn't exist on server
Create another action named dataNotFound. If you found data then set its state isFound to true and if data does not find, set its state isFound to false and always before sending call with regex check isFound that either data was found in previous call or not. If data was not found then don't send call again.
I've made a little bit change in your code to manage this. You just have to create an action dataNotFound now.
this.companySearchCtrl.valueChanges
.pipe(
debounceTime(300),
distinctUntilChanged()
)
.subscribe(val => {
if (val !== ' ' || val !== '') {
this.store.select(getCompanys).subscribe(data => {
console.log(data);
//filter companies on the basis of search text
const filteredData = data.filter(x =>
x['name']
.toLowerCase()
.startsWith(this.companySearchCtrl.value.toLowerCase())
);
console.log(filteredData);
if (filteredData.length === 0) { //if data is not found in store
console.log('got a call');
this.store.select(isDataFound).subscribe(isFound => {
if(isFound) {
this.store.dispatch(
new CompanyActions.find({
where: { name: { regexp: `${val}/i` } } // call to API to
search with regExp
})
);
} else {
this.store.dispatch(new CompanyActions.dataNotFound({isFound: false}));
}
});
} else {
// if required data found in store
console.log('got no call');
this.store.dispatch(new CompanyActions.dataNotFound({isFound: true}));
this.filteredCompanies$ = of(filteredData);
}
});
}
});

Categories