`
Here's the code written in TypeScript.
This is code to build HTML table which display items from Nested objects. This code works fine but just there is an issue in printing like it should only create table with rows but it is also printing some coma's which are not even part of any line which is executed
import {Component} from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Angular';
data = {
id: '0001',
type: 'donut',
name: 'Cake',
ppu: 0.55,
batters: {
batter: [{
id: '1001',
type: 'Regular'
},
{
id: '1002',
type: 'Chocolate'
},
{
id: '1003',
type: 'Blueberry'
},
{
id: '1004',
type: "Devil's Food"
}
]
},
topping: [{
id: '5001',
type: 'None'
},
{
id: '5002',
type: 'Glazed'
},
{
id: '5005',
type: 'Sugar'
},
{
id: '5007',
type: 'Powdered Sugar'
},
{
id: '5006',
type: 'Chocolate with Sprinkles'
},
{
id: '5003',
type: 'Chocolate'
},
{
id: '5004',
type: 'Maple'
}
]
};
//Function which build HTML Table which get's dynamic values.
htmlStr = (data, wrapperClassName, tableClassName = 'table table-sm') => {
return `
<div class=${tableClassName}>
<table className=${tableClassName}>
<tbody>
${Object.keys(data).map( (k) => `
<tr>
${(!Array.isArray(data) && `
<td>${k.replace(/_/g, ' ')}</td>`) || ''} ${ data[k] && typeof data[k] === 'object' ? `
<td>
${this.htmlStr(data[k], wrapperClassName, tableClassName)}
</td>` : `
<td>
<span>${data[k] || ''}</span>
</td>` }
</tr>` )}
</tbody>
</table>
</div>`;
};
}
in your code the snippet Object.keys(data).map(.....) converts it to array. Now when you put this array in string literal JavaScript will try to convert it to a string so it will call .toString() on it which joins all the elements of array using , by default.
instead do this Object.keys(data).map(....).join("") this will join array with empty string
Related
I'm trying to limit access to some properties to only users with that property on their group in a deeply nested interface, and I'm unable to access the "groups" metadata in the nested components.
Here is a code example:
Example of response:
export class ProductResponseInterface {
// groups work fine here
#ValidateNested()
success: boolean;
#Type(() => ProductFetchResponseInterface)
data?: ProductFetchResponseInterface;
error?: string;
#Exclude()
groups?: string[];
constructor(partial: Partial<ProductResponseInterface>) {
Object.assign(this, partial);
}
}
export class ProductFetchResponseInterface {
// groups seem to be undefined here
#ValidateNested()
#Type(() => ProductInterface)
#Expose({ groups: ['eshop.products'] })
products: ProductInterface[];
#Exclude()
groups: string[];
count: number;
constructor(partial: Partial<ProductFetchResponseInterface>) {
Object.assign(this, partial);
}
}
export class ProductInterface {
// groups seems to be undefined here
#Expose({ groups: ['eshop.products.product.id', 'admin'] })
id: number;
#Expose({ groups: ['eshop.products.product.name'] })
name: string;
...
constructor(partial: Partial<ProductInterface>) {
Object.assign(this, partial);
}
}
The problem:
ProductFetchResponseInterface and ProductInterface don't have access to the "groups" tag, and their response returns empty products.
This is the call that uses those interfaces
const http_response = await this.handle_request(url);
// { success: true, data: { products: [ { id: 1, name: 'product_name' }]}}
return plainToInstance(
ProductResponseInterface,
{
...response,
groups: user.access_permissions_populated // ['eshop.products', 'eshop.products.product.id',...],
},
{},
);
Any idea on how to make it work?
Thanks.
You need call plainToInsatnce like this
plainToInstance(ProductResponseInterface, plainObject, { groups: ["eshop.products", "eshop.products.product.name", "eshop.products.product.id"] })
There is test example
import "reflect-metadata";
import { plainToInstance } from "class-transformer"
import { ProductResponseInterface } from "./test"
describe("", () => {
it("tranform items by groups attribute", () => {
const raw = { success: true, data: { products: [{ id: 1, name: 'product_name' }] } }
const res = plainToInstance(ProductResponseInterface, raw, { groups: ["eshop.products", "eshop.products.product.name", "eshop.products.product.id"] })
const exp: ProductResponseInterface = {
success: true,
data: {
products: [
{
id: 1,
name: "product_name"
}
],
groups: undefined,
count: undefined,
}
}
expect(res).toEqual(exp);
});
});
I want to get the following output for the following data.
・3
・1
and sample data :
export const dummyData = [
{
id: "1",
name: "a",
sub: [
{
id: "1#1",
name: "b",
sub_sub: [
{ id: "1#1#1", name: "b-a" },
{ id: "1#1#2", name: "b-b" },
]
},
{
id: "1#2",
name: "c",
sub_sub: [
{ id: "1#2#1", name: "c-a" },
]
},
]
},
{
id: "2",
name: "d",
sub: [
{
id: "2#1",
name: "e",
sub_sub: [
{ id: "1#2#1", name: "e-a" },
]
}
]
},
]
I want to count how many elements of sub_sub are includes in object "a" and "d".
So, I made the following code.
<template>
<div>
<ul>
<li v-for="item in items" :key="item.i">{{rowSpanCalc(item.id)}}</li>
</ul>
</div>
</template>
<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'
import { dummyData } from '~/store/dummy'
#Component({})
export default class extends Vue {
items: any = []
created() {
this.items = dummyData
}
rowSpanCalc(item: any) {
const count = item.sub.reduce(
(total: any, curr: any) => total + curr.sub_sub.length,
0
)
return count;
}
}
</script>
I ran my code and got an error in console like
item.sub.reduce is not a function
Could anyone please advise me how to fix this errors?
Methods in the template are used as events handler not for rendering, try to use that method inside a computed property then use that property for render your items :
#Component({})
export default class extends Vue {
items: any = []
created() {
this.items = dummyData
}
get customItems(){
return this.items.map(item=>({...item,count:this.rowSpanCalc(item.id)}))
}
rowSpanCalc(item: any) {
const count = item.sub.reduce(
(total: any, curr: any) => total + curr.sub_sub.length,
0
)
return count;
}
}
template :
...
<li v-for="item in customItems" :key="item.id">{{item.count}}</li>
...
I am attempting to reuse a kendoDropDownListBox and set the data by using a data-attribute in the parent to query the proper data source, combined with a switch case statement. The switch case portion of the code is not included as it works when the proper data is passed to it, I'm just unable to pull the proper data from the data-attribute (if I use buttons to pass the data it works fine)
I have tried a number of methods to pull the attribute including the following
element.dataset[keyname]
element.getAttribute('keyname']
If I do a console.log('element') I can see the proper data, but either of the above two methods come up empty (either null or undefined).
The HTML:
<div [attr.data-message-id]="1"> Listbox Component
<app-listbox></app-listbox>
</div>
The Typescript:
import { Component, OnInit, ElementRef } from '#angular/core';
#Component({
selector: 'app-listbox',
styleUrls: ['./listbox.component.scss'],
template: `
<kendo-dropdownlist style="width:400px;"
[data]="data"
[filterable]="true"
[textField]="'text'"
[valueField]="'value'"
(filterChange)="handleFilter($event)"
>
<ng-template kendoDropDownListNoDataTemplate>
<div>
No data found.
<ng-container *ngIf="filter">Do you want to add new item - '{{ filter }}' ?</ng-container>
<br />
<button *ngIf="filter" class="k-button" (click)="addNew()">Add new item</button>
</div>
</ng-template>
</kendo-dropdownlist>
`
})
export class ListboxComponent {
public filter: string;
public source: Array<{ text: string, value: number }> = [
{ text: "Small", value: 1 },
{ text: "Medium", value: 2 },
{ text: "Large", value: 3 }
];
public data: Array<{ text: string, value: number }>;
messages = [
{
id: 1,
text: "Table1"
},
{
id: 2,
text: "Table2"
},
{
id: 3,
text: "Table3"
},
{
id: 4,
text: "Table4"
}
]
Table1 = [
{ id: 1, text: "small"},
{ id: 2, text: "med"},
{ id: 3, text: "large"},
{ id: 4, text: "XL"},
]
Table2 = [
{ id: 1, text: "ford"},
{ id: 2, text: "dodge"},
{ id: 3, text: "chevy"},
{ id: 4, text: "GM"},
]
Table3 = [
{ id: 1, text: "fiat"},
{ id: 2, text: "audi"},
{ id: 3, text: "Mercedes"},
{ id: 4, text: "BMW"},
]
Table4 = [
{ id: 1, text: "toyota"},
{ id: 2, text: "nissan"},
{ id: 3, text: "datsun"},
{ id: 4, text: "kia"},
]
constructor(private elRef: ElementRef) {
this.data = this.source.slice(0);
}
public addNew(): void {
this.source.push({
text: this.filter,
value: 0
});
this.handleFilter(this.filter);
}
public handleFilter(value) {
this.filter = value;
this.data = this.source.filter((s) => s.text.toLowerCase().indexOf(value.toLowerCase()) !== -1);
}
ngOnInit() {
console.log("OnInit");
console.log("el");
var el = this.elRef.nativeElement.parentElement.dataset;
console.log(el);
console.log("elatt");
var elatt = this.elRef.nativeElement.parentElement.attributes;
console.log(elatt);
console.log("elkey");
var elkey = this.elRef.nativeElement.parentElement.dataset['messageId'];
console.log(elkey);
console.log("att");
var att = this.elRef.nativeElement.parentElement.getAttribute(['data-message-id']);
console.log(att);
}
}
Using the above code, the el variable contains the following:
enter image description here
The elatt variable contains the following:
enter image description here
the elkey variable reports "undefined" and the att variable reports "null".
I'm sure I'm probably doing this the hard way, but being new to Angular, I'm not sure of a better way of doing this.
Ultimately what I'm looking for is a way to reuse the kendoDropdownBox as a component, and pass it the data it needs to display when it is used.
ngOnInit() :
Initialize the directive/component after Angular first displays the data-bound properties and sets the directive/component's input properties.
Called once, after the first ngOnChanges().
ngAfterViewInit() :
Respond after Angular initializes the component's views and child views / the view that a directive is in.
Called once after the first ngAfterContentChecked().
You are not able to retrieve the data attribute from parent because, you are trying to access the parent from ngOnInit event. It should be in ngAfterViewInit Lifecyle event.
Refer the example below.
ParentComponent.html
<div [attr.data-message-id]="1">
<app-test-component></app-test-component>
</div>
ChildComponent.ts
import { Component, OnInit, ElementRef } from '#angular/core';
#Component({
selector: 'app-test-component',
templateUrl: './test-component.component.html',
styleUrls: ['./test-component.component.css']
})
export class TestComponentComponent implements OnInit {
constructor(private elRef: ElementRef) {
}
ngOnInit() {
}
ngAfterViewInit(){
console.log(this.elRef.nativeElement.parentElement);
console.log('message id : ', this.elRef.nativeElement.parentElement.dataset['messageId']);
}
}
Output Log
Is it possible to get an output array from the given that contains roles=1 without duplicate ?
Iam using angular 6 typescript. Is there any typescript array processing functions to do this operation
//Input Array
export const userMenus = [
{
name: 'Dashboard',
url: '/dashboards',
icon: 'icon-speedometer',
roles:'1,3,4'
},
{
name: 'Users',
url: '/Users',
icon: 'icon-bell',
roles:'1,2,3,4'
},
{
name: 'Article',
url: '/Users',
icon: 'icon-bell',
roles:'1,2,3,4',
children: [
{
name: 'Cards',
url: '/base/cards',
icon: 'icon-puzzle',
roles:'1,3,4',
},
{
name: 'Carousels',
url: '/base/carousels',
icon: 'icon-puzzle',
roles:'2,4',
},
{
name: 'Collapses',
url: '/base/collapses',
icon: 'icon-puzzle',
roles:'4'
}
]
}
]
--Need Output if role is 2.
removed items that not contain 2 in the role field
userMenus = [
{
name: 'Users',
url: '/Users',
icon: 'icon-bell',
roles:'1,2,3,4'
},
{
name: 'Article',
url: '/Users',
icon: 'icon-bell',
roles:'1,2,3,4',
children: [
{
name: 'Carousels',
url: '/base/carousels',
icon: 'icon-puzzle',
roles:'2,4',
},
]
}
You must filter your array and verify that you have 2 in your roles :
const filteredUserMenus = userMenus.filter((userMenu) => {
return userMenu.roles.find((role) => role === '2');
});
short syntax :
const filteredUserMenus = userMenus.filter((userMenu) =>
userMenu.roles.find((role) => role === '2'));
EDIT : your data structure is bad, roles shouldn't be a string but an array of role. Anyway, if you can't change it, here is a solution :
const filteredUserMenus = userMenus.filter((userMenu) => {
return userMenu.roles.split(',').find((role) => role === '2');
});
short syntax :
const filteredUserMenus = userMenus.filter((userMenu) =>
userMenu.roles.split(',').find((role) => role === '2'));
I am having a hard time doing an Angular filter to solve a problem as below.
The filter logic is as below:
1) If all listItem of that item has qtyLeft != 0, do not display that item
2) If any of the listItem of that item has qtyLeft == 0, display the item title as well as coressponding listItem that have qtyLeft == 0
Here's a basic example of my data structure, an array of items:
$scope.jsonList = [
{
_id: '0001',
title: 'titleA',
list: {
listName: 'listNameA',
listItem: [
{
name: 'name1',
qtyLeft: 0
},
{
name: 'name2',
qtyLeft: 0
},
]
}
},
{
_id: '0002',
title: 'titleB',
list: {
listName: 'listNameB',
listItem: [
{
name: 'name3',
qtyLeft: 2
},
{
name: 'name4',
qtyLeft: 0
},
]
}
},
{
_id: '0003',
title: 'titleC',
list: {
listName: 'listNameC',
listItem: [
{
name: 'name5',
qtyLeft: 2
},
{
name: 'name6',
qtyLeft: 2
},
]
}
},
]
Here is the final expected outcome:
<div ng-repeat="item in jsonList | filter: filterLogic">
<div> </div>
</div>
// final outcome
<div>
<div>Title: titleA, ListItem: Name1, Name2</div>
<div>Title: titleB, ListItem: Name4</div>
</div>
Created working Plunkr here. https://plnkr.co/edit/SRMgyRIU7nuaybhX3oUC?p=preview
Do not forget to include underscore.js lib in your project if you are going to use this directive.
<div ng-repeat="jsonItem in jsonList | showZeroElement track by $index">
<div>Title:{{ jsonItem.title}}, ListItem:<span ng-repeat="item in
jsonItem.list.listItem track by $index" ng-if="item.qtyLeft==0">
{{item.name}}</span>
</div>
</div>
And
app.filter('showZeroElement', function() {
return function(input) {
var items = []
angular.forEach(input, function(value, index) {
angular.forEach(value.list.listItem, function(val, i) {
var found = _.findWhere(items, {
'title': value.title
})
if (val.qtyLeft === 0 && found === undefined) {
items.push(value)
}
})
})
return items
}
})