Move an element at the beginning of an array of objects - javascript

I have array format like this
response = {
"data": [{
"districts": [{
"id": 1,
"name": "sikkim district",
"statistics": [{
"food saftey": 2,
"food ": 2,
"air pollution": 0
}]
}]
},
{
"districts": [{
"id": 2,
"name": "Bhojpur",
"statistics": [{
"food saftey": 1,
"food ": 1,
"air pollution": 1
}]
}]
}
],
}
and the required format is
{
"data": [
{
"district": "sikkim district",
"food saftey": 2,
"food ": 2,
"air pollution": 0
},
{
"district": "Bhojpur",
"food saftey": 1,
"food ": 1,
"air pollution": 1
},
],
}
The array format is in dynamic which keeps changing except the district
and the district has to be at the beginning of the array.

What you can do is put the property you know first in a column array and then get the other properties and loop over using the order in the column array.
Something like this:
Stackblitz
import {
Component
} from '#angular/core';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
data = [{
"Bal Vivah": 1,
"Type": 0,
"districts": "East District"
},
{
"Bal Vivah": 1,
"Type": 0,
"districts": "West District"
},
{
"Bal Vivah": 1,
"Type": 0,
"districts": "North District"
}
]
columns: string[] = ["districts"];
constructor() {
// get the columns from the data
if (this.data) {
var dataObject = this.data[0];
for (var property in dataObject) {
if (property != "districts" && dataObject.hasOwnProperty(property)) {
this.columns.push(property);
}
}
}
}
}
<table>
<thead>
<tr *ngFor="let column of columns">
<th>{{column}}</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let columnData of data">
<tr *ngFor="let column of columns">
<td>
{{columnData[column]| json}}
</td>
</tr>
</ng-container>
</tbody>
</table>
Note: I changed your data to be valid json.

Related

EventEmitter is not trigger properly from ng-container & ng-template collapsible Tree | Angular 13

I have the below structure:
app-component --> based on the value received do-something
- side-resultview.component --> Emits a value to app-component
- collapsible-tree.component --> Emits a value to side-resultview.component
Where I am trying to display a collapsible tree based on some JSON returned from a service.
I have another requirement to do something based which node/sub-node is clicked by user, so I am emitting a value (ID key) to the parent and that is again emitted by to the parent of it as shown in the above structure.
Issue:
This is working fine only on the first level node i.e. only while clicking on Column Name 1, Column Name 2, Column Name 3, Column Name 4 and Column Name 5 but not the child-nodes in the tree. See the JSON below:
{
"results": [
{
"id": "1001",
"title": "Column Name 1",
"child": {
"results": [
{
"id": "10001",
"title": "Column Name 10",
"child": {
"results": [],
"size": 0
}
},
{
"id": "10002",
"title": "Column Name 11",
"child": {
"results": [
{
"id": "100011",
"title": "Column Name 101",
"child": {
"results": [],
"size": 0
}
},
{
"id": "100022",
"title": "Column Name 111",
"child": {
"results": [],
"size": 0
}
}],
"size": 2
}
}],
"size": 2
}
},
{
"id": "1002",
"title": "Column Name 2",
"child": {
"results": [],
"size": 0
}
},
{
"id": "1003",
"title": "Column Name 3",
"child": {
"results": [],
"size": 0
}
},
{
"id": "1004",
"title": "Column Name 4",
"child": {
"results": [
{
"id": "100041",
"title": "Column Name 401",
"child": {
"results": [
{
"id": "1000041",
"title": "Column Name 4011",
"child": {
"results": [],
"size": 0
}
},
{
"id": "1000042",
"title": "Column Name 4111",
"child": {
"results": [],
"size": 0
}
}],
"size": 2
}
},
{
"id": "100042",
"title": "Column Name 411",
"child": {
"results": [],
"size": 0
}
}],
"size": 2
}
},
{
"id": "1005",
"title": "Column Name 5",
"child": {
"results": [],
"size": 0
}
}],
"size": 5
}
Detailed code is as below:
The collapsible-tree.component.ts:
import { Component, Input } from '#angular/core';
import { Output, EventEmitter } from '#angular/core';
import { Observable } from "rxjs";
#Component({
selector: '[recursive]',
templateUrl: './collapsible-tree.component.html',
styleUrls: ['./collapsible-tree.component.css']
})
export class CollapsibleTreeComponent {
self = this;
#Output()
public nodeTokenValue = new EventEmitter<any>();
constructor() { }
#Input() level: number | undefined;
#Input() children: any;
#Input() parent: any;
#Input() search: (() => Observable<any>) | undefined;
expandNode(_item: { isOpen: boolean; }): void {
_item.isOpen = !_item.isOpen;
}
public nodeItemNameClicked(_item: any): void {
this.nodeTokenValue.emit(_item.id); // Emitting the ID key of the node
}
}
The collapsible-tree.component.html
<ul class="tree" *ngIf="level==undefined">
<ng-container *ngTemplateOutlet="tree;context:{children:children,search:search}">
</ng-container>
</ul>
<ng-container *ngIf="level!=undefined">
<ng-container *ngTemplateOutlet="tree;context:{children:children,search:search}">
</ng-container>
</ng-container>
<ng-template #tree let-children="children" let-search="search">
<li *ngFor="let item of children">
<span [ngClass]="item.child.size>0?'plus-node':'close-node'">
<a (click)="item.child.size>0 && expandNode(item)">
<i [ngClass]="item.child.size>0?'bi bi-plus-square-fill':'bi bi-dash-square'"></i>
</a>
<!-- This is a click event applied on the nodes -->
<a (click)="nodeItemNameClicked(item)"> {{item.title}}</a>
</span>
<ul recursive *ngIf="item.child.size>0 && item.isOpen"
[children]="item.child.results"
[parent]="self"
[level]="level!=undefined?level+1:0"
[search]="search">
</ul>
</li>
</ng-template>
Which generates a Collapsible Tree as below:
The side-resultview.component.html:
<div recursive [children]="searchResultJson"
[search]="commonSettingsService.getElasticResult"
(nodeTokenValue)="getNodeTokenValue($event)">
</div>
The side-resultview.component.ts:
import { Component, OnInit, ViewChild } from '#angular/core';
import { Output, EventEmitter } from '#angular/core';
import { CommonSettingsService } from '/services/common-settings.service';
import { CollapsibleTreeComponent } from '/collapsible-tree/collapsible-tree.component';
#Component({
selector: 'treeview-result',
templateUrl: './side-resultview.component.html',
styleUrls: ['./side-resultview.component.css']
})
export class SideResultviewComponent implements OnInit {
#Output()
public getNodeTokenValueFromTree = new EventEmitter<any>();
#ViewChild(CollapsibleTreeComponent, { static: false })
nodeTokenValue = 0;
searchResultJson: any[];
constructor(public commonSettingsService: CommonSettingsService) {
this.searchResultJson = [];
}
ngOnInit(): void {
this.commonSettingsService.getElasticResult().subscribe(data => {
this.searchResultJson = data.results;
});
}
public getNodeTokenValue($event: any): void {
this.nodeTokenValue = $event;
this.getNodeTokenValueFromTree.emit(this.nodeTokenValue);
}
}
In app-component.html:
<div id="grouping" class="tab-pane fade active">
<treeview-result (getNodeTokenValueFromTree)="checkNodeItemId($event)"></treeview-result>
</div>
In app-component.ts:
import { Component, OnInit, ViewChild } from '#angular/core';
import { SideResultviewComponent } from '/side-resultview/side-resultview.component';
#Component({
selector: 'app-compoment',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
#ViewChild(SideResultviewComponent, { static: false })
tabIndex: any;
constructor() { }
ngOnInit() { }
checkNodeItemId($event: any) {
this.tabIndex = parseInt($event);
console.log(this.tabIndex); // Giving console out for first-level Node only not the next level.
}
}

Loop through Javascript object with children in Angular template

I have the following js object:
{
"id": "1554038371930_ajhnms9ft",
"name": "CPS",
"nodes": [
{
"id": "1554038905938_le34li2cg",
"name": "Consumer Journey",
"nodes": [
{
"id": "1554039157958_kwab8rj5f",
"name": "Average Rating",
"nodes": []
},
{
"id": "1554039174126_p47ugwkbv",
"name": "Product Quality",
"nodes": [
{
"id": "1554039298091_ewdyefkql",
"name": "Performance",
"nodes": []
},
{
"id": "1554039306834_qf54k1dqe",
"name": "Reliability",
"nodes": []
},
{
"id": "1554039320002_vfkenjmct",
"name": "Comfort",
"nodes": []
}
]
},
{
"id": "1554039197951_ajvv8587d",
"name": "Supply & Delivery",
"nodes": []
},
{
"id": "1554735679177_g5tini7ga",
"name": "Behind Product",
"nodes": [
{
"id": "1554736595466_nt4owp9in",
"name": "Influencers",
"nodes": []
},
{
"id": "1554736608593_58yomqpya",
"name": "Brand Confidence",
"nodes": []
}
]
},
{
"id": "1554736413715_jhro1oh0r",
"name": "Economical Value",
"nodes": [
{
"id": "1554736664421_wng97pbz8",
"name": {
"en": "Price"
},
"nodes": []
},
{
"id": "1554736676408_d4kiy2wv8",
"name": "Promotion & Reward",
"nodes": []
}
]
}
]
}
]
}
I want to loop through it in my angular HTML template so I can have the following list:
CPS
Consumer Journey
Average Rating
Product Quality
Performance
Reliability
Comfort
Supply & Delivery
Behind Product
Influencers
Brand Confidence
Economical Value
Price
Promotion & Reward
PS: I have an unknown number of levels!
I tried to implement that but my solution works only if I have a known number of levels:
<div *ngFor="let item of data">
<p>{{ item.name }}</p>
<div *ngIf="item.nodes.length">
<div *ngFor="let subItem of item.nodes">
<p>{{ subItem.name }}</p>
<div *ngIf="subItem.nodes.length">
<div *ngFor="let child of subItem.nodes">
{{ child.name }}
</div>
</div>
</div>
</div>
</div>
you need a recursive component to loop through n levels:
#Component({
selector: 'recursive-list',
template: `
<div *ngFor="let item of data">
<p>{{ item.name }}</p>
<recursive-list *ngIf="item.nodes.length" [data]="item.nodes"></recursive-list>
</div>
`
})
export class RecursiveListComponent {
#Input() data;
}

Group table cells by decimal points

Group the table cells based on the decimal points.
Plunker
Sample JSON:
[
{
"data1": [
{
"name": "Download",
"id": "1.1.1"
},
{
"name": "Download",
"id": "1.1.2"
},
{
"name": "Download",
"id": "1.2"
},
{
"name": "Download",
"id": "1.3"
},
{
"name": "Download",
"id": "1.4"
}
]
},
{
"data2": [
{
"name": "Download",
"id": "2.1"
},
{
"name": "Download",
"id": "2.2"
}
]
},
{
"data3": [
{
"name": "Download",
"id": "3.1.1"
},
{
"name": "Download",
"id": "3.1.2"
},
{
"name": "Download",
"id": "3.2"
}
]
},
{
"data4": [
{
"name": "Download",
"id": "4.1.1"
},
{
"name": "Download",
"id": "4.1.2"
}
]
}
]
HTML:
<table border="0" class="table table-bordered">
<tbody ng-repeat="(key,result) in results">
<tr ng-repeat="r in result['data'+[key+1]]">
<td rowspan="5">{{r.id}}</td>
</tr>
</tbody>
</table>
using ng-repeat to display each id in single cell of the table.
Actual Result:
Expected Result
Because of ng-repeat the cell are displaying next to each other. The expected result is to divide the table cell using the decimal points.
Example:
Row1 => 1.1.1, 1.1.2, 1.2, 1.3, 1.4
Row2 => 2.1, 2.2
The Row2 first cell(2.1) should take the width of row1(1.1.1 and 1.1.2). And 2.2 should take the rest of the width of 1.2, 1.3 and 1.4
Thanks in advance.
your data structure not clear, seems need to review and refactor it.
but for now this plunker can help you. (i hope!)
link:
plunker

Binding Object to Vue multi-select

I have a table with two columns(Location and Users). The location is static information but the users is a multi-select using Vue-Select.
I need to shows users currently selected for a location on page load. I grab that information from a database.
I also need to be able to change the selected users of a location by using a multi-select that shows all users. Example Mock
Vue Outline
<table>
<thead>
<tr>
<th class="col-xs-2">Locations</th>
<th class="col-xs-8">Users</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in rows" v-bind:key="index">
<td>
<span>{{ row.location }}</span>
</td>
<td>
<v-select multiple v-model="row.users">
<option v-for="user in allUsers" :key="user.id" :value="user.id">{{ user.name }}</option>
</v-select>
</td>
</tr>
</tbody>
</table>
Vue
var app = new Vue({
el: '#el',
data() {
return {
errors: [],
loading: false,
rows: this.assignments,
allUsers: this.users
}
},
props: {
assignments: Array,
users: Array
},
})
Example of how rows are returned from database
[{
"locations":[
{ "id":1,
"name":"SomePlace, CA",
"users": [
{"id":1, "name":"Person One"},
{"id":2, "name":"Person Two"}
]
},
{ "id":2,
"name":"AnotherPlace, CA",
"users": [
{"id":3, "name":"Person Three"}
]
}
]
},
{
"locations":[
{ "id":1,
"name":"SomePlace, CA",
"users": [
{"id":1, "name":"Person One"},
{"id":2, "name":"Person Two"}
]
},
{ "id":2,
"name":"AnotherPlace, CA",
"users": [
{"id":3, "name":"Person Three"}
]
}
]
}]
Example of how all users are returned from database
[
["id":1, "name":"Person One"],
["id":2, "name":"Person Two"],
["id":3,"name":"Person Three"],
]
I had moved the data coming via props directly to data object, since your rows property has one item which contains locations array, i looped through the first item rows[0] and i put row as the select options :options="row" and for the second column i looped through the user of the selectedLocation :
Vue.component('v-select', VueSelect.VueSelect)
var app = new Vue({
el: '#app',
data() {
return {
errors: [],
loading: false,
rows: [{
"locations": [{
"id": 1,
"name": "SomePlace, CA",
"users": [{
"id": 1,
"name": "Person One"
},
{
"id": 2,
"name": "Person Two"
}
]
},
{
"id": 2,
"name": "AnotherPlace, CA",
"users": [{
"id": 3,
"name": "Person Three"
}]
}
]
},
{
"locations": [{
"id": 1,
"name": "SomePlace, CA",
"users": [{
"id": 1,
"name": "Person One"
},
{
"id": 2,
"name": "Person Two"
}
]
},
{
"id": 2,
"name": "AnotherPlace, CA",
"users": [{
"id": 3,
"name": "Person Three"
}]
}
]
}
],
allUsers: this.users
}
},
props: {
assignments: Array,
users: Array
},
})
<table>
<thead>
<tr>
<th class="col-xs-2">Locations</th>
<th class="col-xs-8">Users</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in rows[0].locations" v-bind:key="index">
<td class="lead-locations">
{{row.name}}
</td>
<td class="lead-users">
<v-select multiple v-model="row.users" label="name">
</v-select>
</td>
</tr>
</tbody>
</table>
for demo check this code
I believe that the sample data supplied to the "rows" variable are missing.
So, I will make an imaginary assumption here that you have some web servers distributed in multiple locations and you want to manage access of users.
The following is my imaginary data for "rows" variable which is close enough to your data:
[
{
"serverID": 1,
"serverName": "My Backend API Server",
"locations": [
{
"id": 1,
"name": "SomePlace, CA",
"users": [
{ "id": 1, "name": "Person One" },
{ "id": 2, "name": "Person Two" }
]
},
{
"id": 2,
"name": "AnotherPlace, CA",
"users": [{ "id": 3, "name": "Person Three" }]
}
]
},
{
"serverID": 1,
"serverName": "My Frontend App Server",
"locations": [
{
"id": 1,
"name": "SomePlace, CA",
"users": [
{ "id": 1, "name": "Person One" },
{ "id": 2, "name": "Person Two" }
]
},
{
"id": 2,
"name": "AnotherPlace, CA",
"users": [{ "id": 3, "name": "Person Three" }]
}
]
}
]
Now, we have to loop over the servers array first, then loop over the locations array to get some thing close to your mock as follows:
Check this pen for the implementation.
JS Code:
Vue.component('v-select', VueSelect.VueSelect)
let servers = [
{
"serverID": 1,
"serverName": "My Backend API Server",
"locations": [
{
"id": 1,
"name": "SomePlace, CA",
"users": [
{ "id": 1, "name": "Person One" },
{ "id": 2, "name": "Person Two" }
]
},
{
"id": 2,
"name": "AnotherPlace, CA",
"users": [{ "id": 3, "name": "Person Three" }]
}
]
},
{
"serverID": 1,
"serverName": "My Frontend App Server",
"locations": [
{
"id": 1,
"name": "SomePlace, CA",
"users": [
{ "id": 1, "name": "Person One" },
{ "id": 2, "name": "Person Two" }
]
},
{
"id": 2,
"name": "AnotherPlace, CA",
"users": [{ "id": 3, "name": "Person Three" }]
}
]
}
];
let users = [
{"id":1, "name":"Person One"},
{"id":2, "name":"Person Two"},
{"id":3,"name":"Person Three"},
];
var app = new Vue({
el: '#app',
data() {
return {
errors: [],
loading: false,
selectedLocation: {},
rows: servers,
allUsers: users
}
}
})
HTML Code:
<div id="app">
<table>
<thead>
<tr>
<th class="col-xs-2">Locations</th>
<th class="col-xs-8">Users</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, index) in rows" v-bind:key="index">
<td colspan="2">
<b>{{ row.serverName }}</b>
<table>
<tr v-for="(location, l_index) in row.locations" v-bind:key="l_index">
<td class="col-xs-2">{{ location.name }}</td>
<td class="col-xs-8">
<v-select multiple v-model="location.users" label="name" :options="allUsers">
</v-select>
</td>
</tr>
</table>
</td>
<td class="lead-locations">
{{ row.locations.name }}
</td>
<td class="lead-users">
</td>
</tr>
</tbody>
</table>
</div>

Applying filter on parent if it has been applied on children AngularJS

I have a list with items and have applied filter on those items by value from text box. I want to get new filtered list with parent and its children if parent has at least one filtered item.If doesn't, that parent should not be displayed.
For an example: If I enter "3D" in the text box I don't want to get "Day parts" and "Week parts" listed below as they don't have children anymore after filtering.
<div ng-controller="Ctrl">
<input type="text" data-ng-model="inputValue.name"/>
<ul data-ng-repeat="condition in conditions">
<div data-ng-click="setHeadingStatus(condition)">
<div>
{{condition.name}}
</div>
<li ng-repeat="item in condition.items | filter:inputValue">
<div class="custom-typeahead-list-title">{{item.name}}</div>
<div class="custom-typeahead-list-desc">{{item.description}}</div>
</li>
</div>
</ul>
function Ctrl($scope , $filter){
$scope.countryCode = 1;
$scope.conditions = [
{
"id": 1,
"name": "Experiences",
"expanded": false,
"items": [
{
"id": 1,
"name": "3D"
},
{
"id": 2,
"name": "Imax"
},
{
"id": 3,
"name": "D-Box"
},
{
"id": 4,
"name": "THX"
}
]
},
{
"id": 2,
"name": "Day parts",
"expanded": false,
"items": [
{
"id": 1,
"name": "Early Bird"
},
{
"id": 2,
"name": "Matinee"
},
{
"id": 3,
"name": "Last Night"
}
]
},
{
"id": 3,
"name": "Week parts",
"expanded": false,
"items": [
{
"id": 1,
"name": "Monday"
},
{
"id": 2,
"name": "Wednesday"
},
{
"id": 3,
"name": "Weekend"
}
]
}
]
}
Here is the example of it
http://jsfiddle.net/KJ3Nx/48/
You can put an ng-show on the div that displays each block, and set the expression to the filtered length;
<div data-ng-show="(condition.items | filter:inputValue).length > 0" data-ng-click="setHeadingStatus(condition)">
Updated fiddle

Categories

Resources