angular how to set a component variable from result in observable subscribe - javascript

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.

Related

How to get isLoading / isFetch on auto-refresh in RTK Query?

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.

In React components, where to store unchanging data returned from api so that component methods can have access to it?

My understanding of state variables in React is that only data that changes as a result of user interaction or asynchronous data flow should be stored in state. What if I need to request list data from several APIs and then combine these to make a new master list to serve as a constant source of truth that may need to be used by various component methods during phases of user interaction? What is the correct place to store unchanging data like this? It's tempting to put this data in state for easy access but that doesn't seem right. Thanks.
You don't need to use redux, in this case. It's OK to save your API data call to state, even though the data won't change. If you don't want to save it to state you can save it to a static or global variable.
class App extends React.Component {
API_DATA = []; // save data here
state = {
api_data: [] // it's ok too
}
componentDidMount = () => {
// call to api
// save data to API_DATA
this.API_DATA = data;
}
}
This is where something like React hooks or Redux would come in handy. If you're unfamiliar, both can be used to globally store state. Redux would be better for high frequency changes but it sounds like hooks would be best for your situation.
There are plenty of tutorials out there for this on Youtube, the React subreddit, etc.

Parsing JSON objects array and displaying it in ReactJS

I've been facing a weird issue lately with my React App. I'm trying to parse a JSON object that contains arrays with data. The data is something like this:
{"Place":"San Francisco","Country":"USA", "Author":{"Name":"xyz", "Title":"View from the stars"}, "Year":"2018", "Places":[{"Price":"Free", "Address":"sfo"},{"Price":"$10","Address":"museum"}] }
The data contains multiple arrays like the Author example I've just shown. I have a function that fetches this data from a URL. I'm calling that function in componentDidMount. The function takes the data i.e responseJson and then stores it in an empty array that I've set called result using setState. In my state I have result as result:[]. My code for this would look something like this:
this.setState({result:responseJson})
Now, when I've been trying to access say Author Name from result I get an error. So something like this:
{this.state.result.Author.Name}
I'm doing this in a function that I'm using to display stuff. I'm calling this function in my return of my render function. I get an error stating :
TypeError:Cannot read property 'Name' of undefined. I get the same error if I try for anything that goes a level below inside. If I display {this.state.result.Place} or {this.state.result.Country} it's all good. But if I try,say {this.state.result.Author.Title} or {this.state.result.Places[0].Price} it gives me the same error.
Surprising thing is I've parsed this same object in a different component of mine and got no errors there. Could anyone please explain me why this is happening?
If I store the individual element while I setState in my fetch call function, I can display it. For example:
{result:responseJson,
AuthorName:responseJson.Author.Name
}
Then I'm able to go ahead and use it as {this.state.AuthorName}.
Please help me find a solution to this problem. Thanks in advance!
It could be that your state object is empty on the first render, and only updated with the data from the API after the request has completed (i.e. after the first render). The Name and Place properties don't throw an error, as they probably resolve to undefined.
Try putting an if block in your render method to check if the results have been loaded, and display a loading indicator if they haven't.
I'm guessing your initial state is something like this:
{ results: {} }
It's difficult to say without seeing more code.
[EDIT]: adding notes from chat
Data isn't available on first render. The sequence of events rendering this component looks something like this:
Instantiate component, the initial state is set to { results: [] }
Component is mounted, API call is triggered (note, this asynchronous, and doesn't return data yet)
Render method is called for the 1st time. This happens BEFORE the data is returned from the API request, so the state object is still {results: [] }. Any attempts to get authors at this point will throw an error as results.Authors is undefined
API request returns data, setState call updates state to { results: { name: 'test', author: [...] } }. This will trigger a re-render of the component
Render method is called for the 2nd time. Only at this point do you have data in the state object.
If this state evolves, means it is changed at componentDidMount, or after a fetch or whatever, chances are that your state is first empty, then it fills with your data.
So the reason you are getting this error, is simply that react tries to get this.state.result.Author.Name before this.state.result.Author even exists.
To get it, first test this.state.result.Author, and if indeed there's something there, then get Author.Name like this.
render(){
return(
<div>
{this.state.result.Author ? this.state.result.Author.Name : 'not ready yet'}
</div>
);
}
[EDIT] I'll answer the comment here:
It's just because they are at a higher level in the object.
this.state.result will always return something, even false if there is no result key in your state (no result key in your constructor for instance when the component mounts).
this.state.result.Country will show the same error if result is not a key of your state in your constructor. However, if result is defined in your constructor, then it will be false at first, then become the data when the new state populates.
this.state.result.Author.Name is again one level deeper...
So to avoid it, you would have to define your whole "schema" in the constructor (bad practice in my opinion). This below would throw no error when getting this.state.result.Author.Name if I'm not mistaken. It would first return false, then the value when available.
constructor(props){
super(props);
this.state={
result: {
Author: {}
}
}
}

Angular - Cannot see how Angular2 auto updates data from service

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.

Pass data from dynamicallyloadedcomponent back to parent page Angular2

I'm wondering if there is any way to pass data back from a dynamically loaded component in Angular2. I pass the component data using
this._dcl.loadIntoLocation(...).then(component => {
component.instance.variable = this.variable;
}
On completing the tasks inside the component, in my case a modal, can I pass data or variables back out in a similar manner? Thanks in advance.
You can add an Observable to the dynamically added component
(an EventEmitter might work as well but might break eventually. Then Angular2 team doesn't guarantee that an EventEmitter will keep behaving like an observable)
Then just subscribe to this observable
this._dcl.loadIntoLocation(...).then(component => {
component.instance.variable = this.variable;
component.instance.someObservable.subscribe(val => this.someVal = val);
}
DynamicComponentLoader is deprecated and AFAIK was already removed in master.
See also Angular 2 dynamic tabs with user-click chosen components

Categories

Resources