How to extract keys from JSON where its value is true - html

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.

Related

Angular looping values individually

In my component.ts I have something like this
testOptions: SelectItem[] = Object.keys(TestEnum).map(a => {
return { label: this.translate.instant('TestEnum.' + a), value: a };
});
test.enum.ts:
export enum TestEnum {
A = 'A',
B = 'B',
C = 'C',
}
"translate" uses a json file in my case en.json:
"TestEnum": {
"test1": "Test 1",
"test2": "Test 2",
"test3": "Test 3",
my component.html looks like this:
<ng-template #tBody let-row="row">
<td class="iconLinkColumn">
<span *ngIf="!isRowMenuDisabled(row)" class="newton-more" [title]="'common.options' | translate"
style="padding-bottom: 8px;" (click)="toggleRowMenu($event, rowMenu, row)"></span>
</td>
<td>
<span *ngFor="let f of avrFlagOptions">
{{ f.label}}
</span>
</td>
When I start my angular app I am getting displaying all of my json names in each row of my table. How is it possible to loop the values individually, so that in row one appears Test 1 and in row two appears Test 2?
Very sure testOptions is an KeyValue Array and I cannot see a ngFor in your code.
This I believe will do the trick.
Answer:
<si-newton-section>
<cc-scrollable-table>
<ng-template #tBody let-row="row">
<td class="iconLinkColumn" *ngFor="let option of testOptions | keyvalue">
<span *ngIf="!isRowMenuDisabled(row)" class="newton-more" [title]="'common.options' | translate"
style="padding-bottom: 8px;" (click)="toggleRowMenu($event, rowMenu, row)"></span>
<td>{{option.value}}</td>
</ng-template>
</cc-scrollable-table>
</si-newton-section>

Why are the form inputs in my html table unclickable?

I am trying to render a table in html that is the representation of a reactive form array.
I loop over the complex object (FormGroup > FormArray > FormGroup > FormArray) With the final array being the data that we want to represent to the UI in its transformed format. (Happens in the function groupFormGroup())
Everything works fine, the data is correctly transformed and displayed.
However, now I am trying to add inputs (NOT formControlName inputs) to the table and when they load, they load without me being able to click into, or change their value. They are not disabled, just unclickable. No errors come up in the console.
What I have tried:
Modifying the class names so it is no longer class='form-control'
Increasing the z-index of inputs to be on the forefront
Making the inputs position relative
Here is the code:
<div class="row" [formGroup]="address">
<div class="col-12">
<table class="w-100">
<thead>
<th></th>
<th>Order ID</th>
<th>GP Code</th>
<th>Product</th>
<th>Quantity</th>
<th>Preset Suite Number</th>
<th></th>
</thead>
<tbody>
<ng-template formArrayName="addressOrders" ngFor let-addressOrder [ngForOf]="address.get('addressOrders').controls" let-ao="index">
<ng-container [formGroup]="addressOrder">
<ng-template ngFor let-fulfillment [ngForOf]="groupFormGroup(addressOrder.get('fulfillments').getRawValue(),'orderDetailId')" let-f="index">
<tr>
<td class='table-input'>
<input type="checkbox"
(change)="updateFulfillmentFormControl(fulfillment,addressOrder,'selected',$event)"/>
</td>
<td>{{addressOrder.get('orderId').value}}</td>
<td>{{fulfillment['gpItemNumber']}}</td>
<td>{{fulfillment['name']}}</td>
<td>{{fulfillment['quantity']}}</td>
<td>
<ng-container *ngIf="fulfillment['isSuite']">
<!-- <input class="form-control" (keydown)="updateFulfillmentFormControl(fulfillment,addressOrder,'suiteNumber',$event)" /> -->
<input type="text" class="form-control" />
</ng-container>
</td>
<td>Edit Detail</td>
</tr>
</ng-template>
</ng-container>
</ng-template>
</tbody>
</table>
</div>
</div>
And the code generating the Form Group:
draw() {
this.formGroup = this.initFormGroup(this.ordersToDisplay)
}
initFormGroup(ordersToDisplay: Array<BulkFulfillCustomer>):FormGroup {
var queueList = this.queues.map(q => q['queue_id']);
return this.fb.group({
customers: this.fb.array(
ordersToDisplay.map(otd => {
return this.fb.group({
gpCustomerNumber:[otd.gpCustomerNumber],
customerName:[otd.customerName],
customerAddresses: this.initAddressForm(otd.addressOrders, queueList)
})
})
)
})
}
initAddressForm(addressOrders: Array<BulkFulfillAddress>, queues:Array<string>):FormArray {
return this.fb.array(
addressOrders.map(ao => {
return this.fb.group({
addressLine:[ao.addressLine],
bundleShipment:[true],
addressOrders: this.initAddressOrdersForm(ao.orders,queues)
})
})
)
}
initAddressOrdersForm(orders: Array<BulkFulfillOrder>, queues:Array<string>):FormArray {
return this.fb.array(
orders.map(o => {
return this.fb.group({
orderId: [o.order.id],
fulfillments: this.initFulfillmentsForm(o.orderDetailFulfillments,queues)
})
})
)
}
initFulfillmentsForm(fulfillments: Array<FulfillmentLine>, queues:Array<string>) {
return this.fb.array(
fulfillments.map(f => {
return this.fb.group({
selected:[true],
expandDetail:[false],
isSuite:[f.inventory.isSuite],
suitePrefix:[f.inventory.suitePrefix],
gpItemNumber:[f.inventory.gpItemNumber],
name:[f.inventory.name],
queueId:[{value:f.inventory.pulseQueueId,disabled:(!this.isPulseFulfill && f.inventory.fulfillmentMethod != INVENTORY_FULFILLMENT_PULSE)}, Validators.compose([Validators.required, ContainsListValidator(queues,'Queue ID')])],
orderDetailId:[f.orderDetailFulfillment.orderDetailId],
pulseOrderId:[f.orderDetailFulfillment.pulseOrderId],
suiteNumber:[{value:f.orderDetailFulfillment.suiteNumber,disabled:!this.isPulseFulfill || !f.inventory.isSuite}, Validators.required],
quantity:[{value:f.orderDetailFulfillment.quantity,disabled:f.orderDetailFulfillment.fulfillmentMethod != 1},Validators.compose([Validators.required, Validators.min(1),Validators.max(f.orderDetailFulfillment.quantity)])]
})
})
)
}

Why can't I iterate through JSON data in Angular (typescript)? [duplicate]

This question already has answers here:
Why are Objects not Iterable in JavaScript?
(7 answers)
Closed 2 years ago.
dataset: any = {
"days": [
{
"datevalue":1,
"status":"disable",
"addclass":"notallowed",
"avability":[]
},
{
"datevalue":2,
"status":"disable",
"addclass":"allowed",
"avability":[]
}
]
}
<tr *ngFor="let row of dataset.days; let i = index">
<td *ngFor="let day of row | keyvalue">
{{day.datevalue}}
</td>
</tr>
How to print the datevalue inside td ? Currently its showing blank.
I AM STUCK VERY BADLY. PLEASE HELP
row is an object, and you re trying to loop it, it's not possible, you need to use Object.keys: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
for (let row of this.dataset.days) {
for (let data of Object.keys(row)) {
console.log(row[data]);
}
}
EDIT:
To loop an object through a *ngFor, there is a pipe KeyValuePipe:
https://angular.io/api/common/KeyValuePipe
<div *ngFor="let item of yourObject | keyvalue">
{{item.key}}:{{item.value}}
</div>
in your case :
<tr *ngFor="let row of dataset.days; let i = index">
<td *ngFor="let item of row | keyvalue">
{{ item.value }}
</td>
</tr>
While this.dataset.days is an array (the outer for ... of loop doesn't throw an error), its elements are objects, and you can't iterate through them.
for (let row of this.dataset.days) {
console.log(row);
}
Will work.

Angular write data lines dynamically to a table

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>

Api Delete Angular 5

Hello i'm working in Angular 5 and my api delete works (postman & site --> link = api/employees/:id --> localhost:4200/api/employees/5a2025db26589c230865527a) , but I think my HTML code is wrong or the steps in between. Can somebody help me plz i've been stuck at this all day..
HTML :
<h2>Info employees</h2>
<table class="table">
<tr>
<th>Name</th>
<th>Age</th>
<th>Email</th>
<th>Wage</th>
</tr>
<tr *ngFor="let employee of employees">
<td>{{ employee.name }}</td>
<td>{{ employee.age }}</td>
<td> {{ employee.email }} </td>
<td> {{ employee.wage}}</td>
<td>{{employee._id}}</td>
<td>
Delete
</td>
</tr>
</table>
Component.ts :
deleteEmployee(id){
var employees = this.employees;
this._dataService.deleteEmployee(id)
.subscribe(data => {
if(data.n == 1){
for(var i = 0;i < employees.length;i++){
if(employees[i]._id == id){
employees.splice(i, 1);
}
}
}
});
}
data.service :
deleteEmployee(id){
return this._http.delete('/api/employees/'+id)
.map(res => res.json());
}
replace data-ng-click with (click) and make the HTML element a <button> since it's not redirecting anywhere.
Edit: employees.splice(i, 1); replace with this.employees.splice(i, 1); to reflect the change in the view.
Actually, you need to change the logic of
if(data.n == 1){
for(var i = 0;i < employees.length;i++){
if(employees[i]._id == id){
employees.splice(i, 1);
}
}
}
Because basically you are deleting user in the view without considering the data of the backend service. What response do you get from that DELETE endpoint ? Does it return the deleted user or the id of the deleted user ?
maybe little late ..but that if you try something like this .. with .filter() ES6 (also Typescript) Array native method ..
so like:
eleteEmployee(id){
var employees = this.employees; //why use it?? ..no need it
this._dataService.deleteEmployee(id)
.subscribe(data => {
if(data.n == 1){
this.employees = this.employees.filter(xx=> xx._id != id) // <--- so filter and RETURN ALL non matching given ID
}
});
}
Hope it helps you !! ...