I am having trouble to have a map within a map.
As you can see below I have commented several tries, ideally I wanted to use workItem.bullets.map((bulletItem, i)=><li key={i}>{bulletItem}</li>)
directly.
If I use it directly I will have "Cannot read properties of undefined (reading 'map')".
On this version I will get a undefined is not iterable (cannot read property Symbol(Symbol.iterator)) even though console.log seems to work fine and shows the type as Array as expected. The Array.from is useless but I since I am not understanding what's happening I gave it a try.
const work = this.props.data.work.map( workItem => {
console.log(workItem.bullets);
//let bulletPts = workItem.bullets.map((bulletItem, i)=><li key={i}>{bulletItem}</li>);
//let bps = workItem.bullets.map((bulletItem, i)=>"toto");
let array = Array.from(workItem.bullets);
return (
<div key={workItem.company}>
<h3>{workItem.company}</h3>
<p className="info">
{workItem.title}
<span>•</span> <em className="date">{workItem.years}</em>
</p>
<p>{workItem.description}</p>
<ul>
{
array.map(bulletItem => "test")
}
</ul>
</div>
);
});
I also took a look at How to map inside a map function in reactjs as it looked like a similar problem but I was not able to apply it to my issue.
I don't think it is needed but If you want to see the full project I am trying to add bullet points for resume, resumeData.json needs to be modified to contain some bulletPoints.
https://github.com/nordicgiant2/react-nice-resume
There is somethign wrong with your JSON :D
Related
i'm making an angular application and i have an empty array
addresses : any [] = [];
then after a click event i start an http observable to charge the data from a server
and then i make a pipe map to take what i want and then i make a subscribe
const URL ='some url';
onSearch() {
this.httpClient
.get(`URL${{some data to generate the http observable}}`)
.pipe(
map(response => response.address),
tap( => console.log("address array", address)) // address array [Object, Object, Object]
)
.subscribe(address => this.addresses = address);
}
in the html template i have this
<button (click)="onSearch()"> search address </button>
<ul *ngFor="let address of addresses">
<li>{{address.one}} - {{address.two}} - {{address.three}}</li>
</ul>
the firs time i clcik the button is ok i see the result , but the second time i click the button
the first result is deleted from the second.
My problem is how can i store multiple result in an array and how can i see thei in the html temlate?
Leveraging angular's change detection
You can try this.
.subscribe(addressArray =>
addressArray.foreach(
address => this.addresses.push(address)
)
);
or this:
.subscribe(addressArray => {
for (const address of addressArray){
address => this.addresses.push(address);
}
});
These two approaches do not work well with angular change detection since they're are mutating an existing array rather than creating a new array.
These approaches both create new arrays and angular change detection will have an easy time spotting.
this:
.subscribe(addressArray =>
this.addresses = this.addresses.concat(addressArray)
);
or this:
.subscribe(addressArray =>
this.addresses = [...this.addresses, ...addressArray]
);
They're all very similar ways of achieving the same thing.
Leveraging Angular's async Pipe
Here's a very different way: Output your result array directly in your stream!
Instead of an array like this:
addresses: any[] = [];
You have an Observable and a Subject like this:
newAddresses = new Subject<any[]>();
addresses = newAddresses.pipe(
scan((acc, val) => ([...acc,...val]), [])
)
Then your search emits results directly to your subject.
const URL ='some url';
onSearch() {
this.httpClient
.get(`URL${{some data to generate the http observable}}`)
.subscribe(response => this.newAddresses.next(response.address));
}
Now in your HTML, you can subscribe to this array using angular's async pipe
<button (click)="onSearch()"> search address </button>
<ul *ngFor="let address of addresses | async">
<li>{{address.one}} - {{address.two}} - {{address.three}}</li>
</ul>
This is a reasonable amount of extra work. So why bother?
Flexibility: Now any number of methods can update your addresses, and they'll all get managed by scan in a uniform way.
Maintainability: If you want to change or format addresses in the future, there's a simple & singular place to do it.
Extenability: You can add in fancy RxJS operators like debounce to control how results appear on the screen. It's a really nice way to stop display-stuttering when events get queued up.
Performance: If your application relies on pushing data rather than binding it, you can turn off angular's change detection entirely and boost the performance of your app :).
Even if you're not going to turn off change detection, you can mutate a massive array in memory and not worry about whether change detection will pick it up or need to explicitly tell change detection to check the contents of your array for changes.
You can try this.
.subscribe(address => this.addresses = [...address, ...this.addresses]);
Im trying to aplly tis simple filter ased on a query of a list that with each input the list will narrow the possibilities
HTML:
<mat-form-field>
<mat-label>Search for users</mat-label>
<input #query type="text" matInput placeholder="search" (keyup)="filter(query.value)">
</mat-form-field>
<div *ngIf="filteredUsers">
And the function:
filter(query){
this.filteredUsers= query?
this.users.filter(user=>user.username.includes(query)):
this.users;
}
<ul *ngFor="let u of filteredUsers">
<li>
{{u.username}}
</li>
</ul>
</div>
The error I get as I input a character id that filter is undefined, but since both arrays fileterdUsers and Users are populated, I can't understand why this error is beig thrown...Any ideas?
EDIT: the ngOninit:
ngOnInit(): void {
this.dataService.getUsers()
.pipe(first())
.subscribe(users=>{
this.filteredUsers=this.users=users;
console.log(users, this.filteredUsers) -> i get the data
})
}
Edit 2 :
EDIT
filter(query){
console.log(this.users)
this.filteredUsers= query?
this.users.filter(user=>user.username.includes(query)):
this.users;
console.log(this.users)
}
Like this I don't see any logs...
EDIT 3: snapshot
Ok, maybe I have an idea of whats going on. The first elements of my ngFor are empty as you can see in the picture. Is this the reason for Angular's complaint? (your last fix didn't solve the issue as well)
If console.log doesnt log your array, it means its empty at the time filter(query) is being called. And I just noticed the way you are assigning the array isnt really what you want since arrays work by reference.
ngOnInit(): void {
this.dataService.getUsers()
.pipe(first())
.subscribe(users=>{
this.filteredUsers=users;
this.users=users; //just to be sure
console.log(users, this.filteredUsers) -> i get the data
})
}
filter returns an array so I guess you want to assign that to the filteredUser. I removed the code that made no sense.
filter(query){
console.log(this.users)
this.filteredUsers = query ? this.users.filter(user=> user.username && user.username.includes(query)): this.users
console.log(this.filteredUsers)
}
Whith the help and envolvment of #ukn I found the solution: the first elements of the array - seen on pic - , in this case users, were empty, so the solution was deleting those elements. The filter function is now working properly, giving me the names as I input a single char. The list gets updated trying to match the chars I input.
I know this question has been asked before, but please read this completely before you FLAG me... I have tried what the other posts have said, but I cannot stop the error.
Question: I am getting this cursed error,"Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays." and I don't understand what I am doing wrong, so WHAT AM I DOING WRONG?
Explanation: I am reaching out to saleforce to retrieve an array of over 1400+ responses. I have been working with Angular long enough to know how to put the code together, but don't fully understand it. This is the same approach I am using for other request I have sent to salesforce.
My Attempts: So the first SO post I read (Angular: Cannot find a differ supporting object '[object Object]') spoke about the format of the array. The array is coming back in the same format as others (which have no error), and I have tried to even format the response into .json() to be sure.
The second post (Cannot find a differ supporting object '[object Object]') said something about using similar names, but I don't believe that applies to me.. and regardless they didn't solve my issue.
THE CODE:
This reaches out the a service that reaches to the salesforce API
getProviders(){
this.reqObj.email = this.cus.getCurrentUser();
this.dss.allProviders(this.reqObj).subscribe(res => {
this.serviceProvider = res
console.log("All Providers", res);
})
} //End getProviders
How I link to the API
allProviders(reqObj): Observable<Object>{
return this.http.get("https://xxxxxx.execute-api.us-east-
.amazonaws.com/prod").map(res => res);
}
iteration in the HTML
<div class="table-row n-txt" *ngFor="let sList of
serviceProvider; let i=index" (click)="openProviderAction(sList)">
<div class="table-cell c1">{{sList.Name}}</div>
<div class="table-cell c2" > -- -- </div>
<div class="table-cell c2" > {{sList.county__c}} </div>
<div class="table-cell c3" > {{sList.Phone}}</div>
<div class="table-cell c4" >
Is it possible that the size of the array is causing the error???
I really hope everyone can empathize with me here. Not sure how i was so careless and didn't think to look, but i declared the object serviceProvider likes so => serviceProvider: any = {} ... This was a simple fix, to serviceProvider: any = []; Thank you guys regardless, i appreciate the information and outside eyes.
Application
A simple Search bar and a button where user enters a keyword and the response returned is from a RESTful server (HTTP GET requests)
simplesearch.ts
export class SimpleSearch {
kw: string; // keyword
resp: string; // response from Server
}
simplesearch.service.ts
Has a simple method called searchData which does a HTTP GET request with the user's keyword as a query search. (Code not included for brevity)
simplesearch.component.ts
/*All respective headers and #Component removed from brevity*/
const OUTPUT: SimpleSearch[] = []; // create an array for storing Objects
export class SimpleSearchComponent {
Output = OUTPUT; // define variable for array
constructor(private httpServ: SimpleSearchService, private temp: SimpleSearch) {}
/*Search button on HTML file does this*/
Search(inputVal: string) {
this.temp.kw = inputVal; // store the value of user's input
this.httpServ.searchData(inputVal)
.then(res => this.temp.resp = res); // store the response in temp.resp
// push the Object on the Output Array
this.Output.push({kw: this.temp.kw, resp: this.temp.resp});
}
}
Interpolation Variable
I use Output as an Interpolation Variable for my HTML template. I show the data in an unordered list
<ul>
<li *ngFor="let keyword of Output">
<span>{{keyword.kw}}</span>
</li>
</ul>
Response:
<ul>
<li *ngFor="let answer of Output">
<span>{{answer.resp}}</span> <!-- WHAT TO DO HERE for Array Index-->
</li>
</ul>
Result
I can see the keywords in a list every time a user inputs new keywords but
the responses in the wrong way
How do I pass Indexing with the Interpolation? Or am I thinking wrong?
The easy way out was to create two separate Array<String> for keywords and responses and this works great since I can use the index to delete the contents on the page too but with an Object in an Array I am confused with the key: value representation and the index of the Array (OUTPUT) itself.
The problem lies exactly where developer noticed, this.temp.resp is outside the async function. So when you are pushing items in your Output array, it's always pushing the previous search with the new keyword, therefore you are getting the behavior that the resp is always "one step behind". You can check this to understand this async behavior: How do I return the response from an Observable/http/async call in angular2?
So let's look at the code and explain what is happening. I assume you have initialized 'temp' since it isn't throwing an error on first search, where temp.resp would be undefined unless temp is initialized.
this.httpServ.searchData(inputVal)
// this takes some time to execute, so the code below this is executed before 'this.temp.resp' has received a (new) value.
.then(res => this.temp.resp = res);
// old value of 'temp.resp' will be pushed, or if it's a first search, empty value will be pushed
this.Output.push({kw: this.temp.kw, resp: this.temp.resp});
So how to solve this, would be to move the this.Output.push(... line inside the callback (then), so that the correct values will be pushed to the array.
Also I'd change your model to be an Interface instead of Class. But as to how to change the function and do the assignment inside the callback, I'd also shorten the code a bit and do:
Search(inputVal: string) {
this.httpServ.searchData(inputVal)
.then(res => {
// we are pushing values inside callback now, so we have correct values!
// and 'SimpleSearch' stands for the interface
this.Output.push(<SimpleSearch>{kw: inputVal, resp: res});
});
}
}
This should take care of it that the corresponding keyword will have the corresponding response! :)
PS. Worth noticing here, is that you'd maybe want to display the keyword while we are waiting for the response for that keyword, I ignored it here though and applied the same as you are currently using.
I have the following data setup in firebase.
{
"data": {
"lava14mod1":"down",
"lava14mod2":"up"
}
}
I'm able to access it in my React app. But I can only access it as a (key,value) pair combined as one string.
I cannot access the key or value separately using,
<li>{this.state.data[0]['.key']}</li>
I'm getting this error,
Uncaught TypeError: Cannot read property '.key' of undefined
Uncaught TypeError: Cannot read property '_currentElement' of null
Here's the full code, running on plunker, (without the error line)
http://plnkr.co/edit/zjFKsYAzfYrzGmedVEV6?p=preview
I've been pouring through the docs and still can't figure this out. I'm following the data structure shown in firebase docs,
[
{
".key": "-Jtjl482BaXBCI7brMT8",
".value": 100
},
{
".key": "-Jtjl6tmqjNeAnQvyD4l",
"first": "Fred"
"last": "Flintstone"
},
{
".key": "-JtjlAXoQ3VAoNiJcka9",
".value": "foo"
}
]
Firebase documentation
Is there some problem with my syntax? How do I access the key or value separately?
Thank you so much.
You're getting that error because the this.state.data object does not have the keys you're trying to access in render.
This is easy to see if you simply put a console.log(this.state.data) in render. Doing so gives me something like this in the console:
> []
> [Object]
> [Object, Object]
> [Object, Object, Object]
So, render is being called four different times, once each time that this.state.data is being updated with a new key. But, in your render, you don't consider the fact that keys [0] and [1] might not exist - you try to access them without checking them first.
Now I'm not sure exactly why your state is being updated piecemeal, but it probably has something to do with the ReactFireMixin which I have no experience with. Fetching form Firebase is clearly an asynchronous action (since you have to grab data from the cloud) so you need to make accommodations for that.
The most straightforward fix is simply checking to make sure that the key you want to access actually exists on an object before you try to access it (which is a good practice in most cases to avoid exactly this kind of error!)
render: function() {
return (
<div>
from firebase,
{this.state.data ? <li>{this.state.data}</li> : undefined}
{this.state.data && this.state.data[0] && this.state.data[0][".key"] ? <li>{this.state.data[0][".key"]}</li> : undefined}
{this.state.data && this.state.data[1] && this.state.data[1][".key"] ? <li>{this.state.data[1][".key"]}</li> : undefined}
local var,
<li>{data2[0]['.key']}</li>
</div>
);
}
http://plnkr.co/edit/7XXaOIQCQcyGneqEqa88?p=preview
An even better solution would be to use Array.map to convert whatever array this.state.data is at the time directly into some kind of list. This is a very common pattern in React applications because you'll often keep lists of data as arrays and then want to display it dynamically.
render: function() {
return (
<div>
from firebase,
{this.state.data.map(function(ea){return <li>Key: <strong>{ea[".key"]}</strong>, Value: <strong>{ea[".value"]}</strong></li>})}
local var,
<li>{data2[0]['.key']}</li>
</div>
);
}
http://plnkr.co/edit/b1j10M1i635hvapFxZbG?p=preview
More about Array.map()