Filter nested JSON in angular - angularjs

I have mega menu that generate from JSON object like this:
[
{
"name":"Menu Item 1",
"id":"8",
"children":[
{
"name":"Sub Menu 1-1",
"id":"1",
"children":[
{
"name":"Sub Menu 1.1.1",
"id":"2"
},
{
"name":"Sub Menu 1.1.2",
"id":"3"
}
]
},
{
"name":"Sub Menu 1.2",
"id":"4"
}
]
},
{
"name":"Menu Item 2",
"id":"7",
"children":[
{
"name":"Sub Menu 2.1",
"id":"5"
},
{
"name":"Sub Menu 2.2",
"id":"6"
}
]
}
]
How can I create a custom filter to retrieve only the matched items by their name recursively without changing the original structure?

Try this.
var menuItems =[{ name: 'Menu Item 1', id: '8', children:[{ name: 'Sub Menu 1-1', id: '1', children: [{ name: 'Sub Menu 1.1.1', id: '2' }, { name: 'Sub Menu 1.1.2', id: '3' }] }, { name: 'Sub Menu 1.2', id: '4' }]},{ name: 'Menu Item 2', id: '7', children: [{ name: 'Sub Menu 2.1', id: '5' }, { name: 'Sub Menu 2.2', id: '6' }] }];
var result = [];
function isObject(obj) {
return (!!obj.name && !!obj.id);
}
function hasChildren(obj) {
return !!obj.children && Array.isArray(obj.children);
}
function getNameList(item, matchName) {
if(Array.isArray(item)) {
for(var i = 0; i < item.length; i++) {
if(hasChildren(item[i])) {
getNameList(item[i].children, matchName);
}
if(isObject(item[i])) {
if(matchName == item[i].name) result.push(item[i]);
}
}
}
}
getNameList(menuItems, 'Menu Item 1');
console.log(result);

Related

Access Array from Property in Nested Object

I have the following nested object:
export const mockData = {
sections: [
{
name: 'Client',
subSections: [
{
name: 'Client Detail'
}
]
},
{
name: 'Sales',
subSections: [
{
name: 'Overview',
subSections: [
{
name: 'Overview - A',
fields: [
{
key: 'firmName',
type: 'input',
templateOptions: {
label: 'Firm Name',
required: true
}
},
{
key: 'requestOption',
type: 'select',
templateOptions: {
label: 'Request Option',
required: true,
options: [
{ value: '1', label: '1' },
{ value: '2', label: '2' },
{ value: '3', label: '3' },
{ value: '4', label: '4' }
]
}
},
{
key: 'region',
type: 'select',
templateOptions: {
label: 'Region',
required: true,
options: [
{ value: '1', label: '1' },
{ value: '2', label: '2' },
{ value: '3', label: '3' },
{ value: '4', label: '4' },
{ value: '5', label: '5' }
]
}
}
]
}
]
}
]
}
]
};
and I would like to access the array from the fields property in order to render a form. Right now what I have is overviewA: FormlyFieldConfig[] = mockData.sections[1].subSections[0].subSections[0].fields, but I am facing the error of
Property 'subSections' does not exist on type '{ name: string; }'.
However, this error goes away when do a work-around and I add the property subSections to the nested object like so:
export const mockData = {
sections: [
{
name: 'Client',
subSections: [
{
name: 'Client Detail',
subSections: []
}
]
},
{
name: 'Sales',
subSections: [
{
name: 'Overview',
subSections: [
{
name: 'Overview - A', ...
I was wondering why
I was facing the issue without the work-around, and
How can I access the array from the property fields without the work-around?

How to loop through object for finding a particular value using typescript and react?

I want to hide a button if there is atleast one order or subareas that at least one orders whose status 'ACCEPTED' or 'DONE'.
How can I hide the "Hide me" Menu item when either item has at least one area with order status 'ACCEPTED' OR 'DONE' or at least one area with subareas order with status 'ACCEPTED' or 'DONE'.
Below is the react code with the item I am trying to process
function Parent() {
const item = {
owner: {
id: '1',
cognitoId: '2',
},
areas: [{
id: '1',
orders: [
{
id: '1',
status: 'ASSIGNED',
}, {
id: '2',
status: 'ACCEPTED',
}
],
subAreas: [
{
id: '1',
orders: [
{
id: '4',
status: 'DONE',
}
],
}
]
}, {
id: '2',
orders: [{
id: '3',
status: 'ASSIGNED',
}, {
id: '4',
status: 'ACCEPTED',
}
],
subAreas: [{
id: '2',
orders: [{
id: '5',
status: 'DONE',
}, {
id: '6',
status: 'ACCEPTED',
}
],
}
]
}
]
}
return ({
item && item.owner && item.owner.cognitoId && (
< Menu > Hide me < / Menu > )
});
}
this Item is reference to how the data will look.
For additional information...Item is of type Item which is like below
export interface Item {
id: string;
name: string;
areas?: Area[];
}
export interface Area {
id: string;
Orders?: Order[];
subAreas?: Area[];
}
export interface Order {
id: string;
status: OrderStatus; //this is the one we are looping through
}
export interface OrderStatus {
NEW = 'NEW',
ASSIGNED = 'ASSIGNED',
SENT = 'SENT',
ACCEPTED = 'ACCEPTED',
REJECTED = 'REJECTED',
DONE = 'DONE',
}
what i have tried is like below
const hasDoneAccepted = () => {
return Object
.keys(item)
.some(key =>
(key === 'status' &&
['DONE', 'ACCEPTED'].indexOf(item[key]) > -1) ||
(typeof item[key] === 'object' &&
hasDoneAccepted(item[key])));
}
But this gives me an error like below,
Element implicitly has any type because expression of type "status" cant be used on index type 'Item'. property status doesnt exist on type 'Item'.
i am new to using typescript and not sure whats going wrong. could someone help me with this. thanks.
EDIT:
using the solution provided
const hasAcceptedOrDoneOrders =
item.areas?.reduce<Order[]>((acc, area) => { //error here
area.orders?.forEach(order => acc.push(order));
area.subAreas?.forEach(subArea => subArea.orders?.forEach(order =>
acc.push(order)));
return acc;
}, [])
.some(order => order.status === "ACCEPTED" || order.status === "DONE");
}
this gives me an here at line
item.areas?.reduce<Order[]>((acc, area) =>
"parsing error: expression expected"
looping through the object is always an option. I'd use a helper method too. Something like this:
hideBool = false;
for(cost area of item.areas()) {
if(hideBool) { break; }
hideBool = this.checkArray(area.orders);
if(!hideBool) {
for(const subArea of area.subAreas) {
hideBool = this.checkArray(subArea);
if(hideBool) { break; }
}
}
}
checkArray(array: any[]) {
for( const item in array) {
if(item.status === 'ACCEPTED' || item.status === 'DONE') {
return true;
}
}
return false;
}
This is how you can write the filter function:
const hasAcceptedOrDoneOrders =
item.areas?.reduce<Order[]>((acc, area) => {
area.orders?.forEach(order => acc.push(order));
area.subAreas?.forEach(subArea => subArea.orders?.forEach(order => acc.push(order)));
return acc;
}, [])
.some(order => order.status === "ACCEPTED" || order.status === "DONE");
and use it to conditional display React element:
return !hasAcceptedOrDoneOrders && <Menu>Hide me</Menu>;
However, to eliminate all the TypeScript syntax errors you need to correctly type the item object and fix the data schema:
const item: Item = {
id: "1",
name: "Item1",
owner: { id: "1", cognitoId: "2" },
areas: [
{
id: "1",
orders: [
{ id: "1", status: OrderStatus.ASSIGNED },
{ id: "2", status: OrderStatus.ACCEPTED }
],
subAreas: [
{
id: "1",
orders: [{ id: "4", status: OrderStatus.DONE }]
}
]
},
{
id: "2",
orders: [
{ id: "3", status: OrderStatus.ASSIGNED },
{ id: "4", status: OrderStatus.ACCEPTED }
],
subAreas: undefined
}
]
};
Schema:
interface Item {
id: string;
name: string;
areas?: Area[];
owner: any;
}
enum OrderStatus {
NEW = 'NEW',
ASSIGNED = 'ASSIGNED',
SENT = 'SENT',
ACCEPTED = 'ACCEPTED',
REJECTED = 'REJECTED',
DONE = 'DONE',
}

At the same time, two identical elements can not be selected in dropDownList

Hello i am have two kendo ui drodDownList:
kendo-drop-down-list(
ng-model = 'vm.firstList'
k-data-source='vm.filterData'
k-data-text-field='"title"'
k-data-value-field='"name"'
k-value-primitive='true'
k-filter='"contains"'
k-on-change='vm.onChange($event)'
)
and
kendo-drop-down-list(
ng-model = 'vm.secondList'
k-data-source='vm.filterData'
k-data-text-field='"title"'
k-data-value-field='"name"'
k-value-primitive='true'
k-filter='"contains"'
k-on-change='vm.onChange($event)'
)
it is data source:
this.filterData = [
{ name: 'Brown', title: 'Soier' },
{ name: 'Maks', title: 'Inkl' },
{ name: 'Lint', title: 'Baks' },
{ name: 'Hover', title: 'Niyou' }
]
they have same data source, and i am want when choosing item in first dd then remove this item from other dd (and likewise for the second). At the same time, two identical elements can not be selected.
my solution:
in first dd add:
k-on-change='vm.onFirstSelect(kendoEvent)'
k-data-source='vm.firstFilterElements'
for second dd:
k-on-change='vm.onSecondSelect(kendoEvent)'
k-data-source='vm.secondFilterElements'
in controller add:
this.filterElements = [
{ name: 'Brown', title: 'Soier' },
{ name: 'Maks', title: 'Inkl' },
{ name: 'Lint', title: 'Baks' },
{ name: 'Hover', title: 'Niyou' }
]
this.firstFilterElements = this.filterElements;
this.secondFilterElements = this.filterElements;
onFirstSelect(e) {
this.secondFilterElements = this.filterByItem(e);
}
onSecondSelect(e) {
this.firstFilterElements = this.filterByItem(e);
}
filterByItem (e) {
return this.filterElements.filter(function (el) {
return el.name !== e.sender.dataItem(e.item)
[e.sender.options.dataValueField];
});
}
if someone can optimize it i will be glad)

Angular 2 pipe to filter grouped arrays

I have a group of arrays on my Angular2 app that I use to build a grouped list with *ngFor in my view:
[
{
category: 1,
items: [{ id: 1, name: "helloworld1" }, { id: 2, name: "helloworld2" }]
},
{
category: 2,
items: [{ id: 3, name: "helloworld3" }, { id: 4 }]
},
{
category: 3,
items:[{ id: 5 }, { id: 6 }]
}
]
I also have a boolean that when it's true should filter only the items that have the name property. If a group does not have any item that matches this condition it should not pass. So the result would be the following if the boolean is true:
[
{
category: 1,
items: [{ id: 1, name: "helloworld1" }, { id: 2, name: "helloworld2" }]
},
{
category: 2,
items: [{ id: 3, name: "helloworld3" }]
}
]
How can I implement a pipe to achieve this kind of result?
http://plnkr.co/edit/je2RioK9pfKxiZg7ljVg?p=preview
#Pipe({name: 'filterName'})
export class FilterNamePipe implements PipeTransform {
transform(items: any[], checkName: boolean): number {
if(items === null) return [];
let ret = [];
items.forEach(function (item) {
let ret1 = item.items.filter(function (e) {
return !checkName || (checkName && (e.name !== undefined));
});
if(ret1.length > 0) {
item.items = ret1;
ret.push(item);
}
});
return ret;
}
}

How to select multiple selected value from select option

My controller code looks like
$scope.items = [{
heading: 'Sports',
types: [{
name: 'Football',
}, {
name: 'Persie',
}, {
name: 'Ronaldo',
}, {
name: 'Messy',
}],
id: '1'
}, {
heading: 'Cricket',
types: [{
name: 'Tendulkar',
}, {
name: 'Lara',
}, {
name: 'Ponting',
}],
id: '2'
}];
My view contains something like this :
How can I get the selected values of options when user clicks submit button
Here is the jsfiddle
I used ng-repeat to build the select and ng-options to fill them, you then have to use the relative ng-model to get the selections.
HTML:
<div ng-app ng-controller="MyCtrl">
<select class="select fancy" ng-repeat="(i, item) in items" ng-model="searchOption[i]" ng-options="type.name for type in item.types"></select>
<button ng-click="submitIt()">Submit</button>
</div>
Javascript:
function MyCtrl($scope) {
$scope.submitIt = function () {
console.log($scope.searchOption);
};
$scope.searchOption = [];
$scope.items = [{
heading: 'Sports',
types: [{
name: 'Football',
}, {
name: 'Persie',
}, {
name: 'Ronaldo',
}, {
name: 'Messy',
}],
id: '1'
}, {
heading: 'Cricket',
types: [{
name: 'Tendulkar',
}, {
name: 'Lara',
}, {
name: 'Ponting',
}],
id: '2'
}];
}

Resources