Receive data from multiple observables in Angular - javascript

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.

Related

Javascript / Angular - html displays before rendering code

I have a function to get rates from products, so lets say I have one product with two rates. So my product has two rates. Then, when I get those rates I must get the prices attached to my product. So for each rate I have to look for its prices.
The next code below explains this:
this.loadProductInfo = true; // bool to load data in my form
// First of all, I get rates from API
// const rates = this._http....
// Now, for each rate I must search If my product/products have a price:
this.rates.forEach((rate, index, arr) => {
this._glbGetPricesForProduct.getPrice(params).subscribe(response => {
if (!arr[index + 1]) {
this.initForm();
this.loadProductInfo = false;
}
})
});
The variable loadProductInfo it loads content in my form, so in my html I have:
<form *ngIf="!loadProductInfo"></form>
But form it still give me error: could not find control name.
But if I do this instead, it works correctlly:
setTimeout(() => {
this.initForm();
this.loadProductInfo = false;
}, 2000);
So what I want its to say my form to wait until I have all code loaded and then after it, load its contents. But instead it cant find the control because it loads before code. Any help I really appreciate it.
The main mistake I see there is that you are looping over async data which may not be there when your code execute the for each loop (your rates).
I would build an observable with your rates as a source:
...
$rates: Observable<any> = this._http.get(...);
rates.pipe(
mergeMap((rates) => {
const priceByRates: Observable<any>[] = rates.map((rate, index, arr) => this._glbGetPricesForProduct.getPrice(params));
return combineLatest(pricesByRates); // if getPrice complete right away, use forkJoin() instead
})
).subscribe(res => {
// No need to check for the last item, all rates have been checked for possible price
this.initForm();
this.loadProductInfo = false;
});
...
This implementation should wait for your api calls to resolve before printing your form.
Since you are hiding the entire form, it may be better to just move the API call into a resolver so that the page does not render until the data is ready.
Here is a minimal StackBlitz showcasing this behavior: https://stackblitz.com/edit/angular-ivy-4beuww
Component
In your component, include an ActivatedRoute parameter via DI.
#Component(/*omitted for brevity*/)
export class MyComponent {
constructor(private route: ActivatedRoute) {
// note: 'data' is whatever you label your resolver prop in your routing setup
route.data.subscribe(resolved => {
if ("data" in resolved) this.resolveData = resolved["data"];
});
}
}
Route Setup
And in your router setup you would have the following:
const routes: Routes = [
{
path: 'my-route-path',
component: MyComponent,
resolve: {
data: MyResolver
}
}
];
Resolver
Finally, your resolver would make your API call utilizing your service:
#Injectable({providedIn: 'root'})
export class MyResolver() implements Resolve<T> {
constructor(private service: MyService) {}
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<T> | Promise<T> | any {
return this.service.myRequest();
}
}
The final result will be that your view will not be rendered until your data is ready.

Reusing the same reducer & epic for multiple Ajax calls like an api factory?

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.

Angular 2 subject is not a function

Trying to communicate with 2 components
I thought I'd be able to make a http call, then maybe mergeMap or switchMap to a subject?
Something like
import {Subject} from 'rxjs/Subject';
constructor(private _http: HttpClient) {
this.populateList = new Subject<Blog[]>();
}
getBlogs(){
return this._http.get(this.blogsURL+'blogs')
.map((result: Response ) => {
this.blogs = result['blogs'];
return this.blogs;
}).switchMap((blogs)=>this.populateList.next(blogs))
}
But I get:
You provided 'undefined' where a stream was expected. You can provide
an Observable, Promise, Array
I'm getting errors just trying to subscribe to the subject:
this.blogsService.populateList()
.subscribe((res)=>{
console.log(res)
})
this.blogsService.populateList is not a function
What I'm looking for is a way to update views after http calls
You need to subscribe like this without (). Cause its not a function. surprise
this.blogsService.populateList.subscribe()
and rewrite first function like this cause you dont need switch map you just need to do is side effect to populate list.
getBlogs(){
return this._http.get(this.blogsURL+'blogs')
.map((result: Response ) => {
this.blogs = result['blogs'];
return this.blogs;
}).do((blogs)=>this.populateList.next(blogs))
}

Returning a record with a string in ember

I am trying to implement a search function where a user can return other users by passing a username through a component. I followed the ember guides and have the following code to do so in my routes file:
import Ember from 'ember';
export default Ember.Route.extend({
flashMessages: Ember.inject.service(),
actions: {
searchAccount (params) {
// let accounts = this.get('store').peekAll('account');
// let account = accounts.filterBy('user_name', params.userName);
// console.log(account);
this.get('store').peekAll('account')
.then((accounts) => {
return accounts.filterBy('user_name', params.userName);
})
.then((account) => {
console.log(account);
this.get('flashMessages')
.success('account retrieved');
})
.catch(() => {
this.get('flashMessages')
.danger('There was a problem. Please try again.');
});
}
}
});
This code, however, throws me the following error:
"You cannot pass '[object Object]' as id to the store's find method"
I think that this implementation of the .find method is no longer valid, and I need to go about returning the object in a different manner. How would I go about doing this?
You can't do .then for filterBy.
You can't do .then for peekAll. because both will not return the Promise.
Calling asynchronous code and inside the searchAccount and returning the result doesn't make much sense here. since searchAccount will return quickly before completion of async code.
this.get('store').findAll('account',{reload:true}).then((accounts) =>{
if(accounts.findBy('user_name', params.userName)){
// show exists message
} else {
//show does not exist message
}
});
the above code will contact the server, and get all the result and then do findBy for the filtering. so filtering is done in client side. instead of this you can do query,
this.store.query('account', { filter: { user_name: params.userName } }).then(accounts =>{
//you can check with length accounts.length>0
//or you accounts.get('firstObject').get('user_name') === params.userName
//show success message appropriately.
});
DS.Store#find is not a valid method in modern versions of Ember Data. If the users are already in the store, you can peek and filter them:
this.store.peekAll('account').filterBy('user_name', params.userName);
Otherwise, you'll need to use the same approach you used in your earlier question, and query them (assuming your backend supports filtering):
this.store.query('account', { filter: { user_name: params.userName } });

Angular 2 w/ TypeScript Firebase API returns array of objects in service but undefined in component

I am trying to use the Firebase API in my Angular 2 application.
Here is what I have in my Service:
fbGetData() {
firebase.database().ref('/path/to/data').on('value',(snapshot) => {
this.data = snapshot.val();
console.log(this.data); //This logs my data
return this.data;
})
}
This is what I have in my Component:
ngOnInit() {
this.data = this.dataService.fbGetData();
console.log(this.data); //This logs undefined
}
The component logs undefined and then the service logs the array of objects. Thanks in advance for any help.
As #jonrsharpe commented: Firebase retrieves data from its servers asynchronous. Your browser won't wait for this result, as that would block the user from doing other actions. So you cannot return the data from fbGetData as it hasn't been loaded yet.
There are two ways to work with asynchronous loading:
use a callback
return a promise
use a callback
The trick for asynchronous loading is to reframe your solution from "first get the data, then print it" to "whenever the data is loaded, print it".
In code this translates to:
fbGetData() {
firebase.database().ref('/path/to/data').on('value',(snapshot) => {
this.data = snapshot.val();
console.log(this.data);
})
}
fbGetData();
So the logging is now purely in on() callback.
Of course you might want to do something different with the data at different times, so you can pass in a callback to fbGetData():
fbGetData(callback) {
firebase.database().ref('/path/to/data').on('value',(snapshot) => {
this.data = snapshot.val();
callback(this.data);
})
}
fbGetData(data => console.log(data));
Of course at this stage, you should consider if you shouldn't simply be invoking your own callback with a snapshot:
fbGetData(callback) {
firebase.database().ref('/path/to/data').on('value',callback);
}
fbGetData(data => console.log(data.val()));
return a promise
In the above example the data will be printed when it is initially loaded from the server and whenever the data on the server is changed. This is part of the magic of the Firebase Database: it synchronizes both the current value and any changes to that value.
If you only care about the current value, you can also use once(). This method can take a callback, but it can alternatively also return a promise. That means that you have something that you can return from fbGetData:
fbGetData() {
var promise = firebase.database().ref('/path/to/data').once('value');
return promise;
}
fbGetData().then(snapshot => console.log(snapshot.cal());
With this snippet the value will only be printed (at most) once, since you're using once().
You can try the subscribe method to get data. The Observable can be subscribed ,mapped and can be converted into JSON.
Have a look at the code and see if you can get something.
public sessions(): any {
return this.http.get('api/ResultsAPI') //.get('api/vehicles.json')
.map(
(response: Response) => <any>response.json())
.do
(
response => response
);
}
To call the service use this
this.ApiService.sessions().subscribe(
data => {
this.api = data;
this.getdata(this.api);
}
);

Categories