I want to display display list of districts from the json, receiving the following error
'TypeError: suggestion.districts.slice(...).toLowerCase is not a function'
json file.
How can I get the list of districts details, so that I can perform autocomplete using downshift?
any help appreciated.
json format
{
"states":[
{
"state":"Andhra Pradesh",
"districts":[
"Anantapur",
"Chittoor",
"East Godavari",
]
},
{
"state":"Arunachal Pradesh",
"districts":[
"Tawang",
"West Kameng",
"East Kameng",
]
},
}
component
import React, { Component } from 'react'
import statedist from "./StateDistrict.json";
const suggestions = statedist.states;
/*.... */
function getSuggestions(value, { showEmpty = false } = {}) {
// const StatesSelected=props.StatesSelected;
const inputValue = deburr(value.trim()).toLowerCase();
const inputLength = inputValue.length;
let count = 0;
//console.log(StatesSelected)
return inputLength === 0 && !showEmpty
? []
: suggestions.filter(suggestion => {
const keep =
count < 5 &&
suggestion.districts.slice(0, inputLength).toLowerCase() === inputValue;
if (keep) {
count += 1;
}
return keep;
});
}
function renderSuggestion(suggestionProps) {
const {
suggestion,
index,
itemProps,
highlightedIndex,
selectedItem
} = suggestionProps;
const isHighlighted = highlightedIndex === index;
const isSelected = (selectedItem || "").indexOf(suggestion.districts) > -1;
return (
<MenuItem
{...itemProps}
key={suggestion.districts[0]}
selected={isHighlighted}
component="div"
style={{
fontWeight: isSelected ? 500 : 400
}}
>
{suggestion.districts[0]} -- how can I get all the values instead of one here
</MenuItem>
);
}
class autoCompleteState extends Component {
constructor(props) {
super(props);
this.state = {
SelectedState:'',
}
// this.showProfile = this.showProfile.bind(this)
}
setSelectedDistrict = (newState) => {
this.setState({ SelectedState: newState });
console.log(newState)
this.props.onDistrictSelected(newState);
}
render() {
const { classes, } = this.props;
console.log(this.state.SelectedState)
const StatesSelected=this.props.StateList;
return (
<div>
<DownshiftMultiple
classes={classes}
setSelectedDistrict={this.setSelectedDistrict}
StatesSelected={StatesSelected}
/>
</div>
)
}
}
export default withStyles(Styles)(autoCompleteState);
I want the district details to come as suggestion like state in the below image
Currently, you are doing this:
suggestion.districts.slice(0, inputLength).toLowerCase() === inputValue;
This is throwing an error because .slice is copying inputLength items from your districts array and then trying to call .toLowerCase() on that array.
If I understand correctly, you are trying to filter your districts according to the inputValue. One way of doing this would be to use reduce on the districts array like this:
suggestion.districts.reduce((acc,curr)=>curr.substring(0,inputLength)===inputValue?[...acc,curr.substring(0,inputLength)]:acc, [])
If you only want the first 5 then you can slice the result of this:
suggestion.districts.reduce((acc,curr,index)=>index<5&&curr.substring(0,inputLength)===inputValue?[...acc,curr.substring(0,inputLength)]:acc, [])
Related
I'm making a Trello Board with cards that store information.
I have 3 important components to undertake this:
TrelloBoard.js - containing 5 lanes which are divs. It also stores all the cards.
Lanes.js - which are divs that hold cards visually only
Cards.js - which contain info and can be dragged onto other lanes
In TrellBoard.js my cards are stored inside the state as follows:
cards: [ [], [], [], [], []],
At the moment each component has a state that keeps track of which column it is in [0-4] and the position in the column [0+]
When I drag a card from one lane to the other, a method onDragStart in Card.js stores the position (column - lane X - and index within the column - position Y) and then once I drop it on a lane a method onDrop(...) in Lane.js is called which lets TrelloBoard.js know that I am moving a card from lane X with position Y, to the last place in lane Z
In TrelloBoard, I now splice the card from the cards array and move it to another position via transferCards(...) method:
INITIAL:
0 1 2 3 4
- - - - -
ONE MOVE
0 - 2 3 4
- - 1 - -
Now if I move Card 2 from the 3rd column to 2nd column like so :
FAILURE
0 2 1 3 4
- - - - -
The state inside of my Card 1 believes that it is at position (1) instead of position (0) as it is displayed. This is because after I moved Card 2, the rest of the cards in the 3rd column (i.e Card 1 in the first diagram) did not update their state, which contains their index.
I was thinking I could manage all the data about the cards in TrelloBoard.js, but then once I integrate ore complex data such as due dates, descriptions, status, maybe even avatars... it becomes too complex to manage inside of TrelloBoard.js and I would rather manage all the data in each individual component.
So at the moment I'm quite stuck figuring out how to manage the cards
I'm not too familiar with React yet, but I was thinking I could attach a ref to every card that has been created, and from the method that splices and updates the cards[] array in TrelloBoard.js, that I could call each a method in each Card (in the affected column) that will move their position/index within the column (e.g., something like shiftDown() { this.setState({ index: this.index-1}) } ) and call that from TrelloBoard.js
TrelloBoard.js
import React, { Component } from 'react';
import './TrelloBoard.css';
import Card from '../components/Lane/Card';
import Lane from '../components/Lane/Lane';
import Modal from '../components/UI/Modal/Modal';
import Axios from 'axios';
class TrelloBoard extends Component {
constructor(props) {
super(props);
this.child = React.createRef();
}
state = {
cardModal: false,
modalTitle: "default",
modalStatus: "default",
modalStatusNumber: -1,
cards: [ [], [], [], [], []], // this should actually be lanes - thought
lanes: [],
testInit: false
}
setModalTitle = (name) => {
this.setState({modalTitle : name});
}
setModalStatus = (status) => {
this.setState({modalStatus : status})
}
closeModalHandler = () => {
this.setState({cardModal : false});
}
cardClickedHandler = (name, col) => {
this.setModalTitle(name);
this.setModalStatus(this.titles[col]);
this.setState({modalStatusNumber: col})
this.openModalHandler();
}
openModalHandler = () => {
this.setState({cardModal : true});
}
titles = ['Open', 'In progress', 'To be tested', 'Re-opened', 'Closed'];
colors = ['open', 'progress', 'test', 'reopened', '
componentDidMount() {
this.initCards();
}
initCards() {
if (!this.state.testInit) {
let initCards = [ [], [], [], [], []];
let initLanes = [];
let i = 0;
let n = 0;
for (const t of this.titles) {
n = 0;
const keyName = i+"-"+n;
const card = (<Card clicked = {this.cardClickedHandler}
ref={this.child}
keyID = {keyName}
column={i} >Column {i} card</Card>);
initCards[i].push(card);
const lane = (<Lane color={this.colors[i]} columnNumber={i}
title = {t} transfer={this.transferCards}>
{card}
</Lane>);
initLanes.push(lane);
i++;
}
this.setState({ cards: [...initCards] });
this.setState({ lanes: [...initLanes] });
this.setState({ testInit: true});
}
}
transferCards = (dragStartColumn, dragStartIndex, dragDestinationColumn, name) => {
const tempCards = [...this.state.cards];
// const spliced
tempCards[dragStartColumn].splice(dragStartIndex, 1);
// now all the cards after this one will have the incorrect position so we will have to update them all
// but then I don't have access to the rest of the data in the lane after that... damn!
const keyName = dragDestinationColumn+"-"+(tempCards[dragDestinationColumn].length);
const newCard = (<Card clicked = {this.cardClickedHandler}
keyID = {keyName} ref={this.child} index={tempCards[dragDestinationColumn].length-1}
column={dragDestinationColumn} name={name}>{name}</Card>);
tempCards[dragDestinationColumn].push(newCard);
// THIS IS WHAT I HAVE IN MIND!
// for (const c of tempCards[dragStartColumn]) {
// c.child.shiftDown();
// }
// alert("col: " + dragDestinationColumn + " | item: " + (tempCards[dragDestinationColumn].length-1) +
// " | destCol: " + dragDestinationColumn);
// spliced[0] needs to update its column index
const tempLanes = [...this.state.lanes];
const i = dragStartColumn;
const originalLane = (<Lane color={this.colors[i]} columnNumber={i}
title = {this.titles[i]} transfer={this.transferCards}>
{tempCards[dragStartColumn]}
</Lane>);
const d = dragDestinationColumn;
const destinationLane = (<Lane color={this.colors[d]} columnNumber={d}
title = {this.titles[d]} transfer={this.transferCards}>
{tempCards[dragDestinationColumn]}
</Lane>);
// problem 2 lines tempCards[..]..
alert(tempCards[2].length);
tempLanes[dragStartColumn] = originalLane;
tempLanes[dragDestinationColumn] = destinationLane;
this.setState( { cards: tempCards });
this.setState( { lanes: tempLanes });
// alert(array.length + " nuber of columns");
}
render () {
return (
<div className="wrapper">
{this.state.lanes}
<Modal
show = {this.state.cardModal} modalClosed = {this.closeModalHandler}
status={this.state.modalStatus} title={this.state.modalTitle}
color={this.state.modalStatusNumber} >
</Modal>
</div>
);
}
}
export default TrelloBoard;
Lane.js
import React, { Component } from 'react';
import './Lane.css';
import LaneContents from './LaneContents';
import LaneTitle from './LaneTitle';
import Space from './Space';
class Lane extends Component {
state = {
columnNumber: -1
}
onDragOver(ev) {
ev.preventDefault();
}
onDrop(ev, cat) {
let id = ev.dataTransfer.getData("id");
let name = ev.dataTransfer.getData("name");
const draggedCardColumn = id.split("-");
if (+draggedCardColumn[0] === this.state.columnNumber) {
return alert("dragging and dropping to same col");
}
// let TrelloBoard know to remove the card and put it in the other column
alert("col: " + +draggedCardColumn[0] + " | item: " + +draggedCardColumn[1] + " | destCol:" + +this.state.columnNumber);
this.props.transfer(+draggedCardColumn[0], +draggedCardColumn[1], this.state.columnNumber, name);
}
componentDidMount() {
this.setState({ columnNumber: this.props.columnNumber })
}
render() {
return (
<div className= "box"
onDrop={(e)=>{this.onDrop(e, "wip")}}
onDragOver={(e) => this.onDragOver(e)}>
<LaneTitle color={this.props.color}>{this.props.title}</LaneTitle>
<Space/>
<LaneContents>
{this.props.children}
</LaneContents>
</div>
);
}
};
export default Lane;
Card.js
import React, { Component } from 'react';
import './Card.css';
class Card extends Component {
state = {
column: 2,
index: -1,
name: "default"
}
componentDidMount() {
this.setState({column: this.props.column});
this.setState({title: this.props.children});
this.setState({index: this.props.index});
this.setState({name: this.props.name});
}
onDragEnd = (ev) => {
ev.preventDefault();
}
onDragStart = (ev, id) => {
console.log('dragstart:',id);
ev.dataTransfer.setData("id", id);
ev.dataTransfer.setData("name", this.props.children);
}
updateIndex(newIndex) {
this.setState({ index: newIndex });
}
shiftDown() {
// THIS IS WHAT I HAVE IN MIND - to be called from TrelloBoard.js
// this.setState({ index: this.state.index - 1});
}
updateColumn(newColumn) {
this.setState({ column: newColumn });
}
setTitle() {
this.setState({ title: this.props.children})
}
render() {
return (
<div
key = {this.props.keyID}
className="box-contents--card droppable"
draggable="true"
onClick = {() => this.props.clicked(this.props.children, this.state.column) }
onDragStart = {(e) => this.onDragStart(e, this.props.keyID)}
onDragEnd = {(e) => this.onDragEnd(e)}
>
{this.props.children}
</div>
);
}
};
export default Card;
I am trying to use the group by function on a JSON array using the inner JSON value as a key as shown below. But unable to read the inner JSON value. Here is my JSON array.
NotificationData = [
{
"eventId":"90989",
"eventTime":"2019-12-11T11:20:53+04:00",
"eventType":"yyyy",
"event":{
"ServiceOrder":{
"externalId":"2434",
"priority":"1"
}
}
},
{
"eventId":"6576",
"eventTime":"2019-12-11T11:20:53+04:00",
"eventType":"yyyy",
"event":{
"ServiceOrder":{
"externalId":"78657",
"priority":"1"
}
}
}
]
GroupBy Logic:
const groupBy = (array, key) => {
return array.reduce((result, currentValue) => {
(result[currentValue[key]] = result[currentValue[key]] || []).push(
currentValue
);
return result;
}, {});
};
const serviceOrdersGroupedByExternalId = groupBy(this.NotificationData, 'event.ServiceOrder.externalId');
//this line of code is not working as
// it is unable to locate the external id value.
Desired output
{ "2434":[{
"eventId":"90989",
"eventTime":"2019-12-11T11:20:53+04:00",
"eventType":"yyyy",
"event":{
"ServiceOrder":{ "priority":"1" }
}
}],
"78657":[{
"eventId":"6576",
"eventTime":"2019-12-11T11:20:53+04:00",
"eventType":"yyyy",
"event":{
"ServiceOrder":{ "priority":"1" }
}
}]
}
Does this solves your purpose?
let group = NotificationData.reduce((r, a) => {
let d = r[a.event.ServiceOrder.externalId] = [...r[a.event.ServiceOrder.externalId] || [], a];
return r;
}, {});
console.log(group);
Try like this:
result = {};
constructor() {
let externalIds = this.NotificationData.flatMap(item => item.event.ServiceOrder.externalId);
externalIds.forEach(id => {
var eventData = this.NotificationData.filter(
x => x.event.ServiceOrder.externalId == id
).map(function(item) {
delete item.event.ServiceOrder.externalId;
return item;
});
this.result[id] = eventData;
});
}
Working Demo
I am developing an application with React and I have a problem. I have made a fetch from a json and the values are repeated. I want only each value to appear once.
I hope that it only returns once each of the elements of "Padre": "CRM", "Permisos" and "Telefonia". The same for "Hijo".
I've tried changing ".concat" to ".reduce" because I've read that it can be done like this, but it does not work. I have seen examples but none of them works on "this.state" so I do not know how to do it.
Can you help me with the correct way to do it?
This is a part of my json file
[
{
"Id":"114",
"Padre":"CRM",
"Hijo":"Argumentarios"
},
{
"Id":"115",
"Padre":"CRM",
"Hijo":"Argumentarios"
},
{
"Id":"116",
"Padre":"CRM",
"Hijo":"Argumentarios"
},
{
"Id":"44",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"45",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"46",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"47",
"Padre":"Permisos",
"Hijo":"root"
},
{
"Id":"50",
"Padre":"Telefonia",
"Hijo":"Audio"
},
{
"Id":"51",
"Padre":"Telefonia",
"Hijo":"Audio"
},
{
"Id":"52",
"Padre":"Telefonia",
"Hijo":"Configuracion"
},
{
"Id":"70",
"Padre":"Telefonia",
"Hijo":"Rutas"
}
]
For confidential reasons I can not show the actual file.
In my next code I change the fetch for a fake json url
I Edit my code with #Avanthika and #blaz code. Currently the code works but shows only the first value it finds of "Father" and "Son". I need to show only once each of the different values of "Father" and "Son". Example result:
Father:
CRM
PERMISOS
Son:
ARGUMENTARIOS
ROOT
Currenly, with my new code i see only: CRM (Padre) and Argumentarios (Son). The rest of the elements are not shown.
import React, { Component } from 'react';
class Nav extends Component{
constructor(props){
super(props)
this.state = {
menuSubmenu:[]
}
}
componentWillMount(){
fetch('http://FAKE.php')
.then(response => response.json())
.then(menuSubmenu =>{
menuSubmenu.forEach(datos => {
let data = {
menu:datos.Padre,
submenu:datos.Hijo,
id:datos.Id
}
//console.log( data )
//console.log (data.menu);
this.setState({
menuSubmenu:this.state.menuSubmenu.concat([data])
})
})
})
}
render() {
const array = [...this.state.menuSubmenu];
const distinctValues = array.filter(
({ Padre, Hijo }, index) => {
return array.findIndex(item => item.Padre === Padre && item.Hijo === Hijo) === index;
});
//console.log(this.state.menuSubmenu);
//console.log(distinctValues);
if (distinctValues.length > 0) {
return(
<div>
{distinctValues.map(datos => <Navegacion key={datos.id} menu={datos.menu} submenu={datos.submenu} />)}
</div>
);
}
return (<p>Cargando usuarios...</p>);
}
}
class Navegacion extends Component{
render(){
return (
<ul className="list">
<li className="list__item">{this.props.menu}
<ul className="list">
<li className="list__item">
{this.props.submenu}
</li>
</ul>
</li>
</ul>
)
}
}
export default Nav;
This image is the result of my json.My json result
I hope you can help me with the "reduce" function.
¡Thanks a lot!
Hi you can use lodash and function uniqBy https://lodash.com/docs#uniqBy
_.uniqBy(menuSubmenu, function (e) {
return e.Padre;
});
You can use new Set to unique your array list by Padre and Hijo.
const array = [{"Id":"114","Padre":"CRM","Hijo":"Argumentarios"},
{"Id":"115","Padre":"CRM","Hijo":"Argumentarios"},
{"Id":"116","Padre":"CRM","Hijo":"Argumentarios"},
{"Id":"44","Padre":"Permisos","Hijo":"root"},
{"Id":"45","Padre":"Permisos","Hijo":"root"},
{"Id":"46","Padre":"Permisos","Hijo":"root"},
{"Id":"47","Padre":"Permisos","Hijo":"root"},
{"Id":"50","Padre":"Telefonia","Hijo":"Audio"},
{"Id":"51","Padre":"Telefonia","Hijo":"Audio"},
{"Id":"52","Padre":"Telefonia","Hijo":"Configuracion"},
{"Id":"70","Padre":"Telefonia","Hijo":"Rutas"}];
const distinctValues = Array.from(new Set(array.map(elem => `${elem.Padre}-${elem.Hijo}`))).map(distinctVal => {
const [ Padre, Hijo ] = distinctVal.split("-");
return ({
Id: array.find(elem => elem.Padre === Padre && elem.Hijo === Hijo).Id,
Padre,
Hijo
});
});
console.log(distinctValues);
Or you can go for lodash as mentioned to extract uniq values. You have to use uniqBy.
const array = [{"Id":"114","Padre":"CRM","Hijo":"Argumentarios"},
{"Id":"115","Padre":"CRM","Hijo":"Argumentarios"},
{"Id":"116","Padre":"CRM","Hijo":"Argumentarios"},
{"Id":"44","Padre":"Permisos","Hijo":"root"},
{"Id":"45","Padre":"Permisos","Hijo":"root"},
{"Id":"46","Padre":"Permisos","Hijo":"root"},
{"Id":"47","Padre":"Permisos","Hijo":"root"},
{"Id":"50","Padre":"Telefonia","Hijo":"Audio"},
{"Id":"51","Padre":"Telefonia","Hijo":"Audio"},
{"Id":"52","Padre":"Telefonia","Hijo":"Configuracion"},
{"Id":"70","Padre":"Telefonia","Hijo":"Rutas"}];
const distintValues = _.uniqBy(array, elem => [elem.Padre, elem.Padre].join());
console.log(distintValues);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
You need to fix the solution this way:
render() {
const array = [...this.state.menuSubmenu];
// Put the solution you like here.
// const distinctValues = the solution you like
if (distinctValues.length > 0) {
return(
<div>
{distinctValues.map(datos => <Navegacion key={datos.id} menu={datos.menu} submenu={datos.submenu} />)}
</div>
);
}
return (<p>Cargando usuarios...</p>);
}
Original solution from Avanthika doesn't work because in your data, some item might possibly have Padre value containing dash character ("-"). Use a character that will never appear in Padre value as separator instead and you will be good.
My solution:
Use Array.prototype.filter
const distinctValues = this.state.menuSubmenu.filter(
({ Padre, Hijo }, index) => {
return this.state.menuSubmenu.findIndex(item => item.Padre === Padre && item.Hijo === Hijo) === index;
});
With Array.prototype.reduce it will look more bulky:
const distinctValues = this.state.menuSubmenu.reduce(
(array, item) => {
if (array.findIndex(i => i.Padre === item.Padre && i.Hijo === item.Hijo) === -1) {
array.push(item);
}
}, []);
Below is my custom pipe where I am unable to access the members of the customfilter array which is of type Item.
import { Pipe, PipeTransform } from '#angular/core';
import {Bus} from '/home/pavan/Desktop/Pavan/apstrtcAngular/src/app/Bus';
import { Item } from './Item';
#Pipe({
name: 'busFilter'
})
export class BusFilterPipe implements PipeTransform {
transform(items: Bus[], customfilter: Item): Bus[] {
if(!items || !customfilter)
{
return items;
}
return items.filter((item: Bus)=>
this.applyFilter(item, customfilter));
}
applyFilter(bus:Bus, customfilter: Item):
boolean{
if( customfilter[0].item_id){
if(typeof customfilter[0].item_id==='string'){
if(typeof bus.bustype==='string')
{
if(customfilter[0].item_id===bus.bustype)
{
return false;
}
} }
}
return true;
}
}
Below is my Item.ts and ng multiselect.
export class Item {
/**
* #type {number} id Unique numeric identifier.
*/
item_id: string;
item_text:string;
}
<ng-multiselect-dropdown class="ngfilter"
[placeholder]="'Select BusType'"
[data]="BusTypes"
[(ngModel)]="customfilter"
[settings]="dropdownSettings"
(onSelect)="onItemSelect($event)"
(onSelectAll)="onSelectAll($event)"></ng-multiselect-dropdown>
I am unable to find the issue here, I cannot look at the value of item_id during debugging too. please help me to know where the issue is. Thank you.
import { Pipe, PipeTransform } from '#angular/core';
import {Bus} from '/home/pavan/Desktop/Pavan/apstrtcAngular/src/app/Bus';
import { Item } from './Item';
import { forEach } from '#angular/router/src/utils/collection';
#Pipe({
name: 'busFilter'
})
export class BusFilterPipe implements PipeTransform
{
transform(items: Bus[], customfilter: Item[]): Bus[] {
let ResultSet: Bus[] = [];
if (!items || !customfilter) {
return items;
}
else if (customfilter.length == 0) {
return items;
}
else{
for (let i = 0; i < items.length; i++) {
for (let j = 0; j < customfilter.length; j++) {
if (customfilter[j].item_text === items[i].bustype) {
ResultSet.push(items[i]);
console.log("Result Set =" + ResultSet);
}
}
}
return ResultSet;
}
}
}
Based on your comments and my understanding of your code written in the pipe, modify your pipe like this (please read through the comments in the code):
transform(items: Bus[], customfilter: Item[]): Bus[] {
if(!items || !customfilter)
{
return items;
}
// making custom filter an Array if it isn't already
customFilter = customFilter instanceof Array ? customFilter : [customFilter];
// you seem to ignore the custom filters which don't have item_id
customFilter = customFilter.filter((eachCustom) => eachCustom.item_id);
// create an array of new items which satisfy your criteria
return items.reduce((acc, eachBus) => {
// if bus's bustype is not string then no need to filter
if (typeof eachBus.bustype != 'string') {
acc.push(eachBus)
}
else {
// if the bustype is a string
// then you have to see if this bus's bustype matches any of the custom filters and it's id type
// if not found then that bus should be present in the final bus list
let filterFound = customFilter.findIndex((eachFilter) => {
return (typeof eachFilter.item_id === 'string') && (typeof eachBus.bustype === 'string') && (eachFilter.item_id === eachBus.bustype);
});
if (filterFound === -1) {
// this bus is not found in the filter
acc.push(eachBus)
}
}
return acc;
}, [])
}
Below is a script in javascript to verify the result
function transform(items, customfilter) {
if(!items || !customfilter)
{
return items;
}
// making custom filter an Array if it isn't already
customFilter = customFilter instanceof Array ? customFilter : [customFilter];
// you seem to ignore the custom filters which don't have item_id
customFilter = customFilter.filter((eachCustom) => eachCustom.item_id);
// create an array of new items which satisfy your criteria
return items.reduce((acc, eachBus) => {
// if bus's bustype is not string then no need to filter
if (typeof eachBus.bustype != 'string') {
acc.push(eachBus)
}
else {
// if the bustype is a string
// then you have to see if this bus's bustype matches any of the custom filters and it's id type
// if not found then that bus should be present in the final bus list
let filterFound = customFilter.findIndex((eachFilter) => {
return (typeof eachFilter.item_id === 'string') && (typeof eachBus.bustype === 'string') && (eachFilter.item_id === eachBus.bustype);
});
if (filterFound === -1) {
// this bus is not found in the filter
acc.push(eachBus)
}
}
return acc;
}, [])
}
let buses = [{bustype: 1}, {bustype: "volvo-ac"}, {bustype: "volvo-non-ac"}, {bustype: "non-volvo-ac"}, {bustype: "non-volvo-non-ac"}]
let customFilter = [{item_id: "volvo-ac"}, {item_id: "non-volvo-ac"}]
console.log(transform(buses, customFilter))
// expected output won't contain the buses present in the filter
When I pass an array of dbIds to be turned off the viewer is turning every node off in my model.
Autodesk.Viewing.Viewer3D.prototype.turnOff = function(dbIds) {
var node;
$(dbIds)
.each(function(index, item) {
node = viewer.model.getData().instanceTree.nodeAccess.nodes[item];
viewer.impl.visibilityManager.setNodeOff(node, true);
});
}
If you pass the id of a parent, it will turn off all its children, which is probably what happens in your case. Turning nodes off definitely works fine, you can take a look at my demo at https://forge-rcdb.autodesk.io.
Select a row in the database view or a segment in the pie chart:
What you need to do is to get the leaf node ids, only leaf nodes are represented by geometry in the viewer.
Here is some ES6 code sample, extracted from there:
static getLeafNodes (model, dbIds) {
return new Promise((resolve, reject)=>{
try {
const instanceTree = model.getData().instanceTree
dbIds = dbIds || instanceTree.getRootId()
const dbIdArray = Array.isArray(dbIds) ? dbIds : [dbIds]
let leafIds = []
const getLeafNodesRec = (id) => {
var childCount = 0;
instanceTree.enumNodeChildren(id, (childId) => {
getLeafNodesRec(childId)
++childCount
})
if (childCount == 0) {
leafIds.push(id)
}
}
for (var i = 0; i < dbIdArray.length; ++i) {
getLeafNodesRec(dbIdArray[i])
}
return resolve(leafIds)
} catch(ex){
return reject(ex)
}
})
}
static async isolateFull (viewer, dbIds = [], model = null) {
try {
model = model || viewer.activeModel || viewer.model
viewer.isolate(dbIds)
const targetIds = Array.isArray(dbIds) ? dbIds : [dbIds]
const targetLeafIds = await ViewerToolkit.getLeafNodes(
model, targetIds)
const leafIds = await ViewerToolkit.getLeafNodes (model)
const leafTasks = leafIds.map((dbId) => {
return new Promise((resolveLeaf) => {
const show = !targetLeafIds.length ||
targetLeafIds.indexOf(dbId) > -1
viewer.impl.visibilityManager.setNodeOff(
dbId, !show)
resolveLeaf()
})
})
return Promise.all(leafTasks)
} catch (ex) {
return Promise.reject(ex)
}
}