I have a component that needs to display data that is fetched via an asynchronous http call to another server.
I have a service that fetches the data. It gets instantiated immediately, but the http call takes a few seconds to return. When it does, the page is already drawn and found errors for missing data.
In the ngOnInit() method of the component, I am attempting to fetch the data that is obviously not there yet.
What is the best/proper approach to fill in the data? is it to add a listener to the http response and then populate the component or is there another standard approach?
Thanks
I know I can add listeners in the component to subscribe to change events, but I'm wondering what the more correct approach is here.
The proper approach for this would be to use the async pipe.
Your .ts component:
export class MyComponent implements OnInit {
constructor(private service:Service){}
apiData$:any;
ngOnInit() {
apiData$ = thhis.service.getData();
}
}
And then in your .html component, you use the async pipe.
<div>{{apiData$|async}}</div>
You need to refresh the component when you receive the data.
I do that usually in the end of the call method.
Related
For example, the API has the getList() and deleteItem() functions. providesTags is configured to requery: after a deleteItem() request, a getList() request is automatically fired.
It is necessary to block the interface while the getList() request is in progress. How to do it?
I usually do this with isLoading, but it is part of a hook called on the component. And in the case of automatic re-request, the call occurs not in the component, but in the store
first, I miss understood your question
I think somehow you managed to call getList inside of the store.
you don't have access to the loading inside of the store because you have access to isLoading inside of Component, right?
first why you should do such a thing and if you must do it please provide some codes or explain more.
second I am assuming you do something like this
dispatch(api.endpoints.getPost.initiate()) then if so you should know RTK query have something called Matchers.
and throw this you have access to actions like matchPending, matchFulfilled and matchRejected.
Angular front-end calls a service that must return observable.
I need to set the calling component's variable to a value from the service's observable, but I can't find a way to access the component variable.
Hoping I'm not missing something trivial, here is what I tried.
#Component
metadata
export class AppComponent {
hexResult: string = ''; <-- this needs to be set with data from the service
The service uses a filereader to read a local file. Service has reader.onLoadend that is triggered when the read is complete. After massaging the data a bit, I use a BehaviorSubject passed from the component to the service to send back the result.
But the component has to Subscribe to the Behavior subject. It then can process the result more, but cannot get the result back to the component, which then needs it to affect the html.
This is the code in the component that is receiving the data via BehaviorSubject ("binaryRetrieved"):
this.binaryRetrieved.subscribe( (binString) => {
console.log('received raw binary');
if (binString && binString.length > 0) {
this.hexResult = this.binService.binaryToHexString(binString);
}
});
"this.hexResult" is undefined because it can't see hexResult in the component declarations.
I'm lost... any hints?
Thanks in advance.
Yogi
I am soooo sorry to have wasted people's time with this question. If I had been a little smarter, I would have found the solution faster.
All component variables are accessible from within the subscribe/error/complete sections of an observable response. MY PROBLEM WAS: I had DECLARED a variable in the component, but not INITIALIZED (instantiated) it; thus it appeared to be "undefined".
What a waste of my time to find it, but even more I apologize for wasting yours.
Yogi.
I'm currently trying to get a Register/Subscribe system to work with RxJs.
The situation is that I have component A with several sub components A1, A2, A3, ... The amount has to be dynamic. What I want to do now is that whenever an event I will call "somethingChanged" occurs (which is already distributed through an Observable) all sub components A1, ... will do some processing and then return some information (a state) as an event I'll call newStates to the parent action A probably using another observable. For this to work the sub components first have to register themselves to the "event manager" as children of A so that these events can be processed accordingly.
First idea
My first idea for this was to use a bufferCount on the newStates observable with the count being the amount of registered sub components. The problem is that the sub component registering and the parent component subscribing to the newStates observable is happening at almost the same time, the parent even being slightly faster which means the amountSub is usually 0 which breaks this attempt.
registerSubComponent() {
amountSub++;
}
getParentObservable() {
return newStates.bufferCount(amountSub).mergeMap();
}
Second idea
The second attempt was to use the somethingChanged Event and use that to initialize a takeLast to get the last items when they should be thrown. The problem is again as i will run into race condition as sub components take longer to throw their newStates events meaning I'll get old values.
registerSubComponent() {
amountSub++;
}
getParentObservable() {
return somethingChanged.map(() => newStates.takeLast(amountSub);
}
Third idea
So currently my only idea would be to catch the newStates event in the event manager, store the states in an array and check everytime if all registered components send them by looking at the array length. When all states are in i could then send the saved states and reset the array.
registerSubComponent() {
amountSub++;
}
getParentObservable() {
return newParentObservable;
}
newStates.subscribe(state => {
savedStates.push(state);
if(savedStates.length == amountSub) {
newParentObservable.next(savedStates);
savedStates = [];
}
});
Is this the only way or am I missing something so it could be done easier/with observables?
Btw: This is all pseudo code as my actual code also has to support multiple parent components in one manager making it cumbersome to read through.
It sounds like you want change detection up the tree. Using the following method with an angular service sounds like it might be what you need:
I found a solution on this guy Jason Watmore's blog that describes using rxjs Observables and Subjects. Using this method allows data changes to easily propagate up and down the angular inheritance tree to any component you want
Jason's Post
Accompanying Plunkr
Briefly:
You declare a service as a provider at the module.ts level with 3 methods:
sendMessage
clearMessage
getMessage
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
import { Subject } from 'rxjs/Subject';
#Injectable()
export class MessageService {
private subject = new Subject();
sendMessage(message: string) {
this.subject.next({ text: message });
}
clearMessage() {
this.subject.next();
}
getMessage(): Observable<any> {
return this.subject.asObservable();
}
}
This service needs imports of Observable and Subject from rxjs
In each component you want to share data with:
create a subscription object in the constructor which calls the service.getMessage() function
call rxjs subscription.unsubscribe() in ngOnDestroy for each component so you aren't leaking memory
you can hook in a function to handle the incoming subscription updates
When you have data you want to share with your other components:
Create a public method which calls the service.sendMessage() method
This will send your updated data to each component and fire those functions you've hooked in to handle the changed data
I believe the blog post I linked to and the plunkr from the post say it best and have really helped me move data around efficiently in my own app but if you have any questions I'll do my best to answer them
I am an Angular2 beginner, creating a test note-taking app. The app is simple: I have a NotesContianerComponent which connects to a NoteService which has the getNotes() and addNote() method.
In the NotesContainerComponent, I am storing the data returned from service in a member myNotes and on ngOnInit event, refreshing the array. I am not touching that array anywhere in that code as of now.
Now, when I add a new note using the form and call the service's addNote() method from the NotesContainerComponent, the note does get added to the backend mock notes array, but at the same time the UI (i.e. NotesContainerComponent) gets updated with the new note automatically! I am not doing anything to refresh it manually.
To investigate, I added a console.log() to the getNotes method of the service, however it is being only called the first time, possibly by ngOnInit hook.
What I cannot figure out is, how does Angular2 know about the new note without even querying the service automatically? And how to stop this? Any clues will be appreciated.
NotesContainerComponent code for reference:
export class NotesContainerComponent implements OnInit {
constructor(
private noteService: NoteService
) { }
addNote(newNote):void {
this.noteService.add(newNote);
}
myNotes: Notes[];
ngOnInit() {
this.noteService.getNotes()
.then(notes => this.myNotes = notes);
}
}
If you are storing your mock data in an Object or an Array and this.myNotes's reference is that Objects reference. When your mock datas content changes, all the references will change too. This is because objects are mutable in javascript.
I just started playing with angular 2 and i've ran into a small problem, that i ve searched for in various forms and also angulars documentation.
I've managed to make a service that makes a call and then i want in a component when i press a button to load another component with dynamicload component and have access to the ajax result.
The problem is that I can t figure out how to do that..
The question is how can I make the result accesible in other components using Observables or Promises method.
If I understood correctly your question, you are looking a way to insert a data from request to another nested component.
I hope this image will clarify for you the data flow for this case.
Your Root component is calling a service method which returns for you promise object.
Then you map needed data from response to the component model inside Root Component constructor.
And your Child component should be subscribed for the model which you was preparing in previous step.
ngOnInit() {
this.dataService.getSomeData()
.subscribe((data: IData) => {
this.data = data;
});
}
Just a short example above how to set model in the root component from the promise object to the local model.
New research:
There is another way to fill your components by data from api's. You can use EventEmitter to emit event from service, and then, you can subscribe for this event inside you created components, so they will get a data, each time there will be called the service. Here is nice example of this strategy in the first answer. Service Events
Hope it will help you, let me know if you will need additional info!
Just create a service, then inject the service where you want.
Here it's an example how to share a service ajax data across many components without making the request twice :
https://stackoverflow.com/a/36413003/2681823
the Service:
#Injectable()
export class DataService {
constructor(private http: Http) { }
private _dataObs = new ReplaySubject<request>(1);
getData(forceRefresh?: boolean) {
// On Error the Subject will be Stoped and Unsubscribed, if so, create another one
this._dataObs = this._dataObs.isUnsubscribed ? new ReplaySubject(1) : this._dataObs;
// If the Subject was NOT subscribed before OR if forceRefresh is requested
if (!this._dataObs.observers.length || forceRefresh) {
this.http.get('http://jsonplaceholder.typicode.com/posts/2')
.subscribe(
requestData => {
this._dataObs.next(requestData);
},
error => this._dataObs.error(error));
}
return this._dataObs;
}
}
the Component:
#Component({
selector: 'child',
template : `<button (click)="makeRequest()" class="btn">Click me!</button>`
})
export class Child {
constructor(private _dataService: DataService) { }
makeRequest() {
this._dataService.getData().subscribe(
requestData => {
console.log('ChildComponent', requestData);
}
}
}
A full working example/plunker can be found here : http://plnkr.co/edit/TR7cAqNATuygDAfj4wno?p=preview