ngFor not working angular5 - javascript

I am trying to display some data from server in my component.
export class CommerceComponent implements OnInit {
dealList;
ngOnInit() {
this.getDeals();
}
getDeals(){
this.gatewayService.searchDeals(this.searchParams).subscribe(
(data:any)=>{
this.dealList = data.result;
console.log("Deal list",this.dealList[0]);
},
(error)=>{
console.log("Error getting deal list",error);
}
);
}
Service
searchDeals(data){
var fd = new FormData();
fd.append('token',this.cookieService.get('token'));
fd.append('search',data.keyword);
return this.http.post(config.url+'hyperledger/queryByParams',fd);
}
In html
<div class="deal1" *ngFor="let deal of dealList">
{{deal.Deal.title}}
</div>
But the list is not rendering, however, I am getting console.log("Deal list",this.dealList[0]); single object and this.dealList return array of objects

If you get the result in this.dealList[0], you need also to iterate over [0] indexed item.
<div class="deal1" *ngFor="let deal of dealList[0]">
{{deal.title}}
</div>
But your dealList is undefined, so accessing [0] will throw an exception. You need to initialize it as well
dealList = []

The title you wanted to display is located result[i].Deal.title
Here is the image of your JSON response .
<div class="deal1" *ngFor="let deal of dealList">
{{deal.Deal.title}}
</div>

Related

Get a List buckets react components

I am trying to learn and produce something at the same time.
This is an example of code for a component that should read buckets and produce a list of them. On the paper the console.log is working, but I can get the code to save that list into a variable that I can use to produce the XHTML list.
class ListBuckets extends React.Component{
constructor(props){
super(props);
this.state = {
listBuckets: []
};
this.GetBuckets = this.GetBuckets.bind(this);
}
GetBuckets(){
let tmpCmp = this;
var BucketsApi = new ForgeSDK.BucketsApi(); //Buckets Client
// Get the buckets owned by an application.
// Use the oAuth2TwoLegged client object and the credentials object that were
// obtained from the previous step
// notice that you need do add a bucket:read scope for the getBuckets to work
BucketsApi.getBuckets({}, null, tmpCmp.props.credentials)
.then(function(buckets){
console.log(buckets.body.items);
}, function(err){
console.error(err);
});
}
render(){
return (
<div className="card" style={{width: '18rem'}, {margin: "10px"}}>
<div className="card-body">
<h5 className="card-title bg-dark text-white" style={{padding: "5px"}}><FaFolder/> Buckets</h5>
<h6 className="card-subtitle">List of buckets</h6>
<p className="card-text">
This card provides a list of available buckets
</p>
{
this.props.credentials!==null
? <ul>{this.GetBuckets()}</ul>
: <div>
<div className="spinner-border spinner-border-sm"></div>
<span> Waiting for credentials...</span>
</div>
}
</div>
</div>
)
}
}
Can ayone help me through this?
First off, you need to store or return buckets.body.items from the GetBuckets() function. You haven't done anything with them here, so they just get discarded after the function runs. Use setState() to store them in component state once you receive them:
GetBuckets(){
let tmpCmp = this;
var BucketsApi = new ForgeSDK.BucketsApi();
BucketsApi.getBuckets({}, null, tmpCmp.props.credentials)
.then(function(buckets){
console.log(buckets.body.items);
// The important thing happens here:
this.setState({ listBuckets: buckets.body.items });
}, function(err){
console.error(err);
});
}
Secondly, this function is going to get called every time the component re-renders, so you may only want to call it inside a one of the component lifecycle methods like componentDidMount(). Since it's an async function, you may have to do some null-checking in later in your render function.
Lastly, you can't just stick this array into your render function and expect it to return a an <li> for each element in the array. You need to map over the objects and return some JSX:
...
{
this.props.credentials!==null
? <ul>
{this.state.listBuckets.map(bucket =>
<li>bucket.data</li>)}</ul>}
: <div>
<div className="spinner-border spinner-border-sm"></div>
<span> Waiting for credentials...</span>
</div>
}
Adding to what #tobiasfried said:
Using Forge SDKs on the client side is not recommended as it could potentially expose the Forge application credentials. A better practice is to provide your own server endpoint which makes the Forge API calls and returns data in any format your client app needs. Take a look at the Learn Forge tutorial and its Node.js sample. The specific endpoint that lists all available buckets is here.

How to Loop Through Firebase JSON in React

I have the following JSON Data coming out of Firebase RTDB:
{"\"1\"":{"itemDescription":"This is a description of the item.","itemName":"Item Name","itemValue":{"costAmount":200,"costType":"dol"}},
"\"2\"":{"itemDescription":"This is an item description.","itemName":"Item Name 2","itemValue":{"costAmount":500,"costType":"dol"}}}
and so on...
The data is parsed (json.parse) and stored a variable called parsedJSONData in state.
I've tried looping through it using other recommendations on this site and others, such as:
this.state.parsedJSONData.map(json => {
<div className="item">
<p>Item: {json.itemName}</p>
</div>;
});
And I'm getting errors like the below:
Line 68: Expected an assignment or function call and instead saw an expression no-unused-expressions
Any tips on what I can do? I know the data is parsed correctly, because if I output the contents of the parsedJsonData to the console, I can see the data is structured correctly?
Try Using forEach() instead of map()
You are not returning anything inside of your map(). Here you can look at the different ways to return values from an arrow function, but in short, data will be returned in these two forms:
json => json.itemName
json => {return json.itemName;}
Drop the {} inside of your map or throw a return inside like so:
this.state.parsedJSONData.map(({itemName}) =>
<div className="item">
<p>Item: {itemName}</p>
</div>
);
or
this.state.parsedJSONData.map(({itemName}) => {
return <div className="item">
<p>Item: {itemName}</p>
</div>;
});
Try this. I have tried this and it works for the your json Data.
render() {
var tempData = [];
const theDataWeHave = this.state.parsedJSONData;
for(var row in theDataWeHave){
var rowEntry = theDataWeHave[row];
tempData.push(rowEntry);
}
var renderVariable = tempData.map(function(item,index) {
return ( <RenderThisComponent key={index} item={item}/> )
})
}
return(
//your code for other stuff
{renderVariable} //Place this where you want to render the component
)
The component you want to render will be a separate functional component with props passed to it.
You can write in the same file or you can write in separate file and import+export
.I have done in the same file so it do not need to be exported or imported.
const RenderThisComponent = (props) => (
<div className="item">
<p>Item: {props.item.itemName}</p>
</div>
)

implicit context is undefined, angular 7

Right, so I am iterating over an array of information and the information is showing the way that I want it to, however, I am getting some amaing looking errors in my console: ERROR TypeError: "_v.context.$implicit is undefined"
api service:
private extractData(res: Response) {
let body = res;
return body || {};
}
getWeather(city: string, isoCode: string): Observable<any> {
return this.http.get(`${this.endPoint}${city},${isoCode}${this.constants.apiKey}`)
.pipe(map(this.extractData));
}
component using api service:
theWeather:any = [];
countryList = COUNTRIES;
isLoading: boolean = true;
showWeather: boolean = false;
constructor(private apiCall:ApiService) { }
ngOnInit() {
this.retrieveWeather()
};
retrieveWeather() {
console.log('COUNTRY LIST', this.countryList);
this.theWeather = [];
this.countryList.map((element, i) => {
this.apiCall.getWeather(element.city, element.countryIso)
.subscribe((data: {}) => {
element.weatherInfo = data;
});
this.isLoading = false;
});
this.showWeather = true;
};
and the html file:
<div class="country-container">
<div *ngIf="isLoading">
<mat-card>
<mat-progress-spinner class="spinner-style" color="primary" mode="indeterminate"></mat-progress-spinner>
</mat-card>
</div>
<div *ngIf="showWeather" class="card-container">
<mat-card *ngFor="let c of countryList" class="card">
<mat-card-title>Weather for: {{c.city}}, {{c.countryName}}</mat-card-title>
<mat-card-content>The current weather is: {{c.weatherInfo.weather[0].description}}</mat-card-content>
</mat-card>
</div>
</div>
finally an image of my console:
Thank you for the help!
edit: made the title more specific to the issue.
You get this error when the angular template is looping through an array of items, and one or more of the array elements is undefined.
This can happen if you create your array by placing a value in a position using the following notation:
myArray[1] = myObject;
If you do this, then myArray[0] will not have a value, and when angular loops through it, you'll get the error "_v.context.$implicit is undefined".
It's safer to use:
myArray.push(myObject);
You might also get this if you remove an element from an array leaving an undefined value at an index.
according to me on the 5th element (it says line 10 index 4 (which is element 5)) in the list it cannot fetch the weather conditions... check for bad request parameters and/or bad return data. remove temp the element or check it and see if the error "moves". maybe the best thing is to check for undefined even when there is no http error.

MEAN / Angular 4: Trouble displaying data in select

(Edit: corrected wrong bracket)
(Edit: Changed the HTML code to use *ngFor)
I am new (and struggling) with Angular and the MEAN stack, so please be patient.
I am having problems displaying the data i get from MongoDB in my HTML <select>.
my service:
getLacadorByClubeId(idClube){
let headers = new Headers();
headers.append('Content-Type','application/json');
let ep = this.authService.prepEndpoint('lacador/listByClubeId');
//important: returning the res with a Map. Maybe the problem is here?
return this.http.get(ep,{headers: headers, params: {idClube: idClube}})
.map(res => res.json());
}
my component:
ngOnInit() {
this.clubesdelacoService.getClubesdelaco().subscribe((clubesdelaco) => {
this.Clubedelaco = clubesdelaco;
console.log(clubesdelaco);
};
my HTML:
<div *ngIf="!isView" class="controls col-md-8 ">
<select name="clube" id="repeatSelect" class="input-md form-control" style="margin-bottom: 10px;">
<option *ngFor="let clube of this.Clubedelaco" [ngValue]="clube._id">{{clube.name}}</option>
</select>
<option ng-repeat="clube in this.Clubedelaco" value="{{clube._id}}">{{clube.name}}</option>
</select>
</div>
That results in messages on the browser CONSOLE:
Image of Browser console, please note that the data seems to be inside an object
And nothing in shown in the combobox :(
So, my view SUPPOSEDLY is receiving the data, but I am failing to show it.
My question is, what is the problem?
Wrong use of the <select> ? Need to transform the object into array? If so, how?
To populate the <select> you can use *ngFor. There is no ng-options or ng-repeat in Angular 2+. You can use [ngValue]="someExpression" to set the value attribute of each <option> to a given property on the clube data object, in your case it looks like that would be the _id property.
This assumes that Clubedelaco is an array of objects.
<div *ngIf="someCondition">
<select name="clube">
<option *ngFor="let clube of Clubedelaco.clubesdelacolist" [ngValue]="clube._id">{{clube.name}}</option>
</select>
</div>
Here is an example in action. It's been set up to demonstrate/simulate async setting Clubedelaco in the ngOnInit() lifecycle hook.
To bind the data as part of a form you'll need to either use Template Driven Forms or Reactive Forms.
To avoid using nested objects, you can utilize the RxJS map() operator to transform the result from your http get call. You could for example, just return the inner array. It would look something like this:
import { Observable } from 'rxjs/Observable';
// instead of any, you'd ideally want to make a class/interface to represent the clube object
getLacadorByClubeId(idClube): Observable<any[]> {
// setting headers as before
return this.http.get(ep,{headers: headers, params: {idClube: idClube}})
.map(res => res.json())
// additional map() chained on
// emit the clubesdelacolist array value instead of original object
// you can do more formatting/projection here if needed
.map(res => res['clubesdelacolist']) as Observable<any[]>;
}
Then in your component:
#Component({})
export class SomeComponent {
// create a class or interface to represent the clube objects and their properties
Clubedelaco: any[];
ngOnInit() {
this.clubesdelacoService.getClubesdelaco()
// clubesdelaco comes in as an array now
.subscribe((clubesdelaco) => {
this.Clubedelaco = clubesdelaco;
console.log(clubesdelaco);
};
}
This would allow you to do the following in the HTML:
<option *ngFor="let clube of Clubedelaco" [ngValue]="clube._id">{{clube.name}}</option>
Hopefully that helps!

Updating component in Angular 4

I am building a site what lets you build a time table (for school subjects) I wrote a component that fetches data from database and then calculates corect placement in time table
<div class="timetable col-lg-8">
<div class="rowT">
<div style="width : 16%">Day</div>
<div *ngFor="let head of timeTableHeader"
[style.width]="headerWidth">
{{head}}
</div>
</div>
<div *ngFor="let sublists of allData" class="rowT">
<div *ngFor="let sublist of sublists"
id="{{sublist[0]}}"
class="timetable-cell"
[style]="getMyStyles(sublist[1])"
(click)="showDetails($event,sublist[3])">
{{sublist[0]}}
</div>
</div>
now I wrote a form which allows somebody to edit particular subject(e.g. time changed or class room) it works fine it saves the changes in DB adn now I want to show these changes in my view I thought I just call the function that calculates subject placements in time table but that results in rendering the time table again and leaving the old one there.
#Component({
selector: 'time-table',
templateUrl: './timetable.component.html',
styleUrls: ['./timetable.component.css']
})
export class TimeTableComponent {
allSubjects: Array<Subject>;
headerWidth: string;
timeTableHeader: Array<string> = new Array();
allData: Array<Array<Array<string>>> = new Array<Array<Array<string>>>();
constructor(private http: Http) {
this.fetchAndMake(); //function that fetches data and calls other function
//to make calculations... too long and not sure if relevant
// so I wont post it here
}
fetchAndMake(){
this.allSubjects = new Array<Subject>();
let params : URLSearchParams = new URLSearchParams();
params.set('userName', this.authService.currentUser.userName);
let reqOption = new RequestOptions();
reqOption.params = params;
this.http.get(this.configurations.baseUrl + "/SubjectModel/TimeTable", reqOption).subscribe(result => {
this.makeSubjects(result.json());
});
}
updateSubject(subj){
let subject = subj as SubjectData;
this.http.post(this.configurations.baseUrl + "/SubjectModel/UpdateSubject",helper)
.subscribe();
this.editSubjectView = false;
this.fetchAndMake();
}
}
Thanks in advance for the help.
First you should not be fetching the data directly from the component but rather from a data service. When you use a data service and inject it into components that use it the data service is a singleton. Not sure if that solves your problem but in any case it is a design issue you should look into before you go any further down this road.
Also, you are calling the primary function in the constructor. The only thing you should be doing in the constructor is to inject the data service. You need to implement the OnInit interface and call your function that fetches the data from the data service you injected in the constructor in the ngOnInit() method.
Check out some of the many Angular 4 tutorials and look for examples of creating a data service, making it a provider in app.module.ts. Then in your component
import { Component, OnInit } from '#angular/core';
import { MyDataService } from '../shared/my-data.service';
....
export class TimeTableComponent Implements OnInit {
...
constructor(private _mydata: MyDataService) { }
ngOnInit(){
// call method in data service that fetches data and do whatever you need to
// with the returned result. Now everything will be available to your view
}
There is a chance that
this.fetchAndMake()
gets called before
this.http.post(this.configurations.baseUrl +
"/SubjectModel/UpdateSubject",helper)
.subscribe();
is complete in updateSubject function. Subscribe just initiates the call , if you want to ensure that the new data is updated only after the post is complete, edit the updateSubject() function as follows :-
updateSubject(subj){
let subject = subj as SubjectData;
this.http.post(this.configurations.baseUrl + "/SubjectModel/UpdateSubject",helper)
.subscribe(result =>{
this.allData = new Array<Array<Array<string>>>();
this.fetchAndMake();
});
this.editSubjectView = false;
}

Categories

Resources