I created an angular Quiz project, every ID can make quizzes and I want to display every Quiz that a logged user has done. I did it like this.
// here I call the user id and the id_category inside Result[], but I can't use it on the html part.
ngOnInit() {
this.supabase.authChanges((_, session) => this.session2 = session);
this.getProfile();
this.supabaseQuiz.getResult().subscribe((res: Result[]) => {
let resultIdAns = res
this.result = resultIdAns
//here I get the result = [Object, Object]
})
//passare category id
this.supabase.profile.then(profile => {
if(profile.data){
this.userProfile = profile.data
}else{
// this.router.navigateByUrl('account/addProfile')
}
})
}
this is the html part:
here I try to if the result.id_category with the userID to display only his quiz but this if inside the for destroy all the table!
<nz-table #headerTable [nzData]="result" [nzPageSize]="50" [nzScroll]="{ y: '240px' }">
<thead>
<tr>
<th>Category</th>
<th>Date</th>
<th>Correct answers</th>
<th>Final score</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let res of result">
<div *ngIf="this.userProfile?.id === res.id_profile">
<td>{{ res.categoryTitle }}</td>
<td>{{ res.date }}</td>
<td>{{ res.rightAnswer }}</td>
<td>{{ res.finalScore }}</td>
<button nz-button nzType="primary" success (click)="goToDetail(parameterValue)">Dettagli</button>
<!--<td>{{ res.json_answer }}</td>-->
</div>
</tr>
</tbody>
</nz-table>
there is way to do another subscribe maybe and get the result id_category outside the for? Thanks
1- You can make the filtering of the ids check in the ts file using array.find() against res.id_profile and userProfile?.id.
2- You do not need to create a new property named resultIdAns since you can assign the API endpoint response to the result property directly.
3- You cannot make the *ngIf="" check outside the *ngFor="" because the condition predicate in the *ngIf="" depends on the variable named res from the outer element which is the container of *ngFor=""
4- Your usage of this. keyword in the template inside the *ngIf="" is invalid.
5- whenever you want to use structural directive it is better for performance reasons to use <ng-container *ngIf="" ></ng-container> or <ng-container *ngFor="" ></ng-container>
I have below JSON, from this I want to extract Skills where it is true.
[
{
"_id":"5de9f351baca28556c6a4b71",
"Name":"Harsha",
"Age":20,
"Gender":"M",
"Skills":{
"Java":"",
"Mule":true,
"Angular":""
}
},
{
"_id":"5de9f358baca28556c6a4b72",
"Name":"Anji",
"Age":21,
"Gender":"M",
"Skills":{
"Java":"",
"Mule":true,
"Angular":true
}
},
{
"_id":"5dea110297c2b65298b136e4",
"Name":"Abhi",
"Age":25,
"Gender":"M",
"Skills":{
"Java":"",
"Mule":true,
"Angular":""
}
}
]
I am able to print rest of the data using below code
<table *ngIf="formTemplate">
<tr>
<th *ngFor="let header of questionTitleArray" >{{header}}</th>
</tr>
<tr *ngFor="let data of surveyDataFromDB">
<ng-container *ngFor="let head of questionTitleArray">
<td>{{data[head]}}</td>
</ng-container>
</tr>
</table>
(Here JSON is "surveyDataFromDB")
Below is the output I am getting
Name Age Gender Skills
Harsha 20 M [object Object]
Anji 21 M [object Object]
Abhi 25 M [object Object]
I want Skills which are true in place of [object Object]. Please help.
You can first map your object to only have the truthy ones. You can use lodash pickBy.
mappedSurveyDataFromDB = this.surveyDataFromDB.map(
entry => ({...entry, Skills: _pickBy(entry.Skills, Boolean)}),
);
After change the template like this:
<table>
<tr>
<th *ngFor="let header of questionTitleArray">{{ header }}</th>
</tr>
<tr *ngFor="let data of mappedSurveyDataFromDB">
<ng-container *ngFor="let head of questionTitleArray">
<td *ngIf="head !== 'Skills'">{{ data[head] }}</td>
<td *ngIf="head === 'Skills'">
<ng-container *ngFor="let entry of (data[head] | keyvalue); let last = last">
{{ entry.key }}
<ng-container *ngIf="!last">, </ng-container>
</ng-container>
</td>
</ng-container>
</tr>
</table>
Stackblitz: https://stackblitz.com/edit/angular-ga7lqg
you can try like this
<table *ngIf="formTemplate">
<tr>
<th *ngFor="let header of questionTitleArray" >{{header}}</th>
</tr>
<tr *ngFor="let data of surveyDataFromDB">
<ng-container *ngFor="let head of questionTitleArray">
// here we are iterating a loop with keyvalue pipe bcs "Skills" is object
<span *ngIf="typeOf(data[head]) === 'object' else elsePart">
<td *ngFor="let j of data[head] | keyvalue">
{{j.key}} {{j.value}}
<td>
</span>
<ng-template #elsePart>
{{data[head]}}
<ng-template>
</ng-container>
</tr>
</table>
<table>
<tr>
<th *ngFor="let header of questionTitleArray">{{ header }} </th>
</tr>
<tr *ngFor="let data of surveyDataFromDB">
<ng-container *ngFor="let head of questionTitleArray">
<span *ngIf="checkType(data[head]) else elsePart">
<span *ngFor="let j of data[head] | keyvalue">
<td *ngIf="j.value==true">
{{j.key}}
</td>
</span>
</span>
<ng-template #elsePart>
<td>{{data[head]}}</td>
</ng-template>
</ng-container>
</tr>
</table>
in ts:
checkType(Ob:any)
{
if(typeof (Ob) === 'object')
return true;
else
return false;
}
You can process before render on ui, and create a comma seprated string of skill key on the basics of its value
let list=[{"_id":"5de9f351baca28556c6a4b71","Name":"Harsha","Age":20,"Gender":"M","Skills":{"Java":"","Mule":true,"Angular":""}},{"_id":"5de9f358baca28556c6a4b72","Name":"Anji","Age":21,"Gender":"M","Skills":{"Java":"","Mule":true,"Angular":true}},{"_id":"5dea110297c2b65298b136e4","Name":"Abhi","Age":25,"Gender":"M","Skills":{"Java":"","Mule":true,"Angular":""}}];
let result = list.map((o) => { return {...o, 'Skills': Object.entries(o.Skills).reduce((acc, i) => acc+= i[1] ? `${i[0]},`: '' , '').slice(0, -1) } });
console.log(result)
Or You can checkout this demo may this helps you
In template you can use KeyValuePipe to iterate on object i.e skills and show skill key only when its value is not falsy
<ng-container *ngFor="let entry of (data[head] | keyvalue); let last = last">
{{ entry.value ? entry.key: '' }}
<ng-container *ngIf="!last && entry.value">,</ng-container>
</ng-container>
Okey so you want to extract all the skills that are true and place them in a new object. From what I know there is not built in function to do this, however I wrote some code to to exactly this
var skills = {
Java: "",
Mule: true,
Angular: "",
Cpp: true,
Maja: false,
NodeJs: true
}
let keys = Object.keys(skills);
let trueSkills = {};
keys.forEach(keyValue => {
if (skills[keyValue] == true)
trueSkills[keyValue] = true;
});
console.log(trueSkills); // These are all the true skills
I hope this helps
Dealing with object keys based on their values can be complex in an Angular template. You could try to transform "Skills" into an array of strings. For example you could do it like this:
get formattedData() {
// this.data would be your json object
return this.data.map(i => ({...i, Skills: Object.keys(i.Skills).filter(j => i.Skills[j] === true)}))
}
This loops through every entry and transforms the skills object into an array with the skills.
This would return:
[
{
"_id":"5de9f351baca28556c6a4b71",
"Name":"Harsha",
"Age":20,
"Gender":"M",
"Skills":[
"Mule"
]
},
{
"_id":"5de9f358baca28556c6a4b72",
"Name":"Anji",
"Age":21,
"Gender":"M",
"Skills":[
"Mule",
"Angular"
]
},
{
"_id":"5dea110297c2b65298b136e4",
"Name":"Abhi",
"Age":25,
"Gender":"M",
"Skills":[
"Mule"
]
}
]
A minimal working example: Stackblitz
I would strongly advise against this method though, if you have a lot of entries as it would take a long time.
Skills attribute in your data is an Object. ngFor can only be used with Iteratable. Therefore first you should convert Skills to an Array. It can be done as below.
dataForView: any[];
prepareDataForView() {
this.dataForView = [...this.surveyDataFromDB];
this.dataForView.map(item => {
item.Skills = Object.keys(item.Skills).map(key => {
if (item.Skills[key]) {
return key;
}
});
});
}
And then you bind new array to View.
Find working Stackblitz Demo.
In my project I getting the data from JSONPlaceholder - Users
I'm new in Angular, so if you find something stupid in the way I get the data please warn me.
dataFromServer;
constructor(private http: HttpClient){
this.dummyPromise.then(
response => {
console.log("response from dummy promise:", response);
this.dataFromServer = response;
},
error => {
console.log("Error happened with dummy promise:", error)
}
)
}
dummyPromise = new Promise((resolve, reject) => {
this.http.get('https://jsonplaceholder.typicode.com/users').subscribe(data => {
console.log(data);
this.dataFromServer = data;
});
//resolve(returnData);
});
Problem is, in my HTML file, I write the data into a table like this:
<tr *ngIf="dataFromServer">
<td>{{dataFromServer[0].id}}</td>
<td>{{dataFromServer[0].name}}</td>
<td>{{dataFromServer[0].username}}</td>
<td>{{dataFromServer[0].email}}</td>
</tr>
<tr *ngIf="dataFromServer">
<td>{{dataFromServer[1].id}}</td>
<td>{{dataFromServer[1].name}}</td>
<td>{{dataFromServer[1].username}}</td>
<td>{{dataFromServer[1].email}}</td>
</tr>
... for all the 10 people. I want to do it dynamically, as many lines as many people's data I get.
I think that you should try to use *ngFor instead of *ngIf. I will give you an example.
<tr *ngFor="let data of dataFromServer">
<td>{{data.id}}</td>
<td>{{data.name}}</td>
<td>{{data.username}}</td>
<td>{{data.email}}</td>
</tr>
So, it will repeat for every object in your dataFromServer
use ngFor to iterate on an array of data:
<table *ngIf="dataFromServer">
<tr *ngFor="let item of dataFromServer">
<td>{{item.id}}</td>
...
</tr>
</table>
the ngIf condition on the table will prevent console errors/rendering issues if dataFromServer is null/undefined before receiving from your API
You can replace your html code as bellow
<tr *ngFor="let row of dataFromServer">
<td>{{row.id}}</td>
<td>{{row.name}}</td>
<td>{{row.username}}</td>
<td>{{row.email}}</td>
</tr>
You can use *ngFor to do it. It's pratically a for in the html. As an example we assume that we have a component like this :
private users: User[] = [];
ngOnInit(){
this.service.getUser()
.subscribe(userList => {
this.users = userList;
});
}
The User class :
export class User {
public id: number;
public name: string;
}
You can use the *ngFor in your html like this way :
<span *ngFor="let user of users">
UserID: {{user.id}} - User Name: {{user.name}}
</span>
So basically, related to your code, just put in an object of User the json data you get from the http call, then modify the html like this way :
<tr *ngFor="let user of users">
<td>{{user.id}}</td>
<td>{{user.name}}</td>
.....
</tr>
I need to populate a dynamic amount of Tabs on an Angular 2 site using a For Loop that is using a subscribe to get data from a database service and I am wondering if it is even possible.
My data set is broken down by Classes: A, B and C and they each have a Sub-Class of 1 and 2. So I would like to have my results dynamically create 3 tabs (Tab A, Tab B, and Tab C). So far I have this working.
I then need each of these tabs to then display the data of their Sub-Classes. As of now the loop runs the 3 times and provides the data needed but every page just shows data for Class C as it was the last one to run and the model is updated with all of its data.
Below is what I have thus far.
classdata.component.html
<mat-tab-group>
<mat-tab *ngFor="let classresult of classresults;" label="Class -{{classresult.Class_Name}}">
<table class ="responstable">
<thead>
<tr>
<th>Sub-Class Name</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let subclassresult of subclassresults;">
<td>{{subclassresult.Sub_Class_Name}} </td>
<td>{{subclassresult.Value}} </td>
</tr>
</tbody>
</table>
</mat-tab>
</mat-tab-group>
classdata.component.ts
// Populate Class Level Data
this.databaseService.getClass()
.subscribe(classresults => this.classresults = classresults,
error => console.log('ERROR!'),
// Populate Sub-Class Level Data
() => { for (const classresult of this.classresults) {
this.selectedClassId = classresult.Class_ID;
console.log(this.selectedClassId);
this.databaseService.getSubClass(this.selectedClassId)
.subscribe(subclassresults => this.subclassresults = subclassresults);
}
}
);
database.service.ts
getClass(): Observable<ClassResult[]> {
const url = 'http://localhost:3000/c';
const data = ({
});
return this._http.post(url, data)
.pipe(
map((res) => {
console.log(res);
return <ClassResult[]> res;
})
);
}
getSubClass(Class_ID): Observable<SubClassResult[]> {
const url = 'http://localhost:3000/sc';
const data = ({
classid: Class_ID
});
return this._http.post(url, data)
.pipe(
map((res) => {
console.log(res);
return <SubClassResult[]> res;
})
);
}
You are fetching each Subclass for every parentClass but you are reassigning each result to the class variable this.subclassresults. Basically you are overwriting each previous result with the current result. Thats why every page just shows data for Class C.
There are many different solutions how you can solve this problem.
On solution could be you are using forkJoin Observable and save the result of your parentClass with their subClasses in their own object:
this.databaseService.getClass().pipe(
switchMap(classResults => {
const subClassRequests = classResults.map(
classResult => this.dabaseService
.getSubClass(classResult)
.pipe(map(subClassResults => {classResult, subClassResults}))
)
return forkJoin(subClassRequests)
})
).subscribe(results => this.results = results);
results holds your data as an array.
And in your template use it like this:
<mat-tab-group>
<mat-tab *ngFor="let result of results;" label="Class -{{result.classresult.Class_Name}}">
<table class ="responstable">
<thead>
<tr>
<th>Sub-Class Name</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let subclassresult of result.subclassresults;">
<td>{{subclassresult.Sub_Class_Name}} </td>
<td>{{subclassresult.Value}} </td>
</tr>
</tbody>
</table>
</mat-tab>
</mat-tab-group>
I implemented a small working Demo: StackBlitz Demo
I have a scenario to bind a html table using angular js. In my table i need to show an a tag based on another column value(Payment Status). If its fully paid no need to show the a tag, else need to show it for very next element. I am a new one in angular.
<table>
<thead>
<tr>
<th>Month</th>
<th>Installement</th>
<th>PaymentAmount</th>
<th>PaymentDate</th>
<th>Payment Status</th>
<th>Pay</th>
</tr>
</thead>
<tbody>
<tr dir-paginate="row in rowCollection|orderBy:type:reverse|filter:searchKeyword|itemsPerPage:maxsize">
<td>{{row.Month}}</td>
<td>{{row.MonthlyInstallement}}</td>
<td>{{row.PaymentAmount}}</td>
<td>{{row.PaymentDate}}</td>
<td>{{row.PaymentStatus}}</td>
<td ng-if="row.PaymentStatus == 'UNPAID'">
Pay Online
</td>
<td ng-if="row.PaymentStatus == 'FULLY_PAID'">
</td>
</tr>
</tbody>
</table>
function bindFinanceDetails() {
var finUrl = baseurl + 'api/FinancialStatement/GetCarFinanceInfo';
var req = {
method: 'post',
data: {
LoginID: LoginID,
ContractNumber: 11170200669,
CustomerId: 2355898046
},
url: finUrl,
headers: {
RequestedPlatform: "Web",
RequestedLanguage: cookiePreferredLanguage,
Logintoken: $cookieStore.get('LoginToken'),
LoginId: LoginID
}
};
$http(req).then(function(response) {
var getData = response.data.FinanceList;
$scope.rowCollection = getData;
}, function(error) {
toastr.error($filter('translate')('Error Occured'));
});
}
A quite hacky solution will be something like the following (just showing you the needed change in the unpaid td element):
<td ng-if="row.PaymentStatus === 'UNPAID'" ng-show="$index === data.payOnlineIndex"
ng-init="data.payOnlineIndex = (!data.payOnlineIndex || (data.payOnlineIndex > $index)) ? $index : data.payOnlineIndex">
Pay Online
</td>
This way ng-init will run for all unpaid elements, setting the smallest index to the payOnlineIndex variable. ng-show will make sure to only show that one element that has the smallest index.
I encapsulate payOnlineIndex with a data object to keep a stable reference to it. This also requires the following addition to the controller code:
$scope.data = { payOnlineIndex: null };
See a working jsFiddle example here: https://jsfiddle.net/t3vktv0r/
Another option is running your filter and orderBy in the controller, searching for the first occurrence of an "unpaid" row, and marking that element for the "pay online" feature with some flag you can test with ng-if in your view.