I have two arrays of objects which have properties that match (id & name).
var result1 = [[
{ title: "Option 1", enable: false },
{ title: "Option 2", enable: false },
{ title: "Option 3", enable: false },
{ title: "Option 4", enable: false },
{ title: "Option 5", enable: false }
]
;
var result2 = [
{ title: "Option 3", enable: false },
{ title: "Option 4", enable: false },
];
result1.map(item => {
const isChecked = result2.some(({ title }) => title === item.title);
return {
...item,
checked: isChecked
}
});
Use the following code,
import _ from 'lodash';
this.setState({
result1: this.state.result1.map(item => {
return (_.find(this.state.result2, item)) ? { ...itemObj, checked: true }: { ...itemObj, checked: false }; });
// return (_.find(this.state.result2, {'title': item.title})) ? { ...itemObj, checked: true }: { ...itemObj, checked: false }; }); // if you want to check only title
});
lodash is a lib which will be helpful in such things.
Detailed documentation for the same can be found in this link, for find.
The above code with check the whole item object if you want to check only part of it say only title below is the code
Related
Let's take the default oneOf example from rjsf docs:
const schema = const schema = {
type: 'object',
oneOf: [
{
properties: {
lorem: {
type: 'string'
}
},
required: ['lorem']
},
{
properties: {
ipsum: { type: 'string' }
},
required: ['ipsum']
}
]
}
official codepen demo here
Is it possible to add a label to the select that switches between oneOf items?
You can add a field (in the example I named it "dropdownField") whose value is the condition for showing the other fields.
That field can have a title just like the others.
{
type: "object",
properties: {
dropdownField: {
title: "Your label goes here",
type: "string",
enum: ["Option 1", "Option 2"],
default: "Option 1",
},
},
dependencies: {
dropdown: {
oneOf: [
{
properties: {
dropdown: { enum: ["Option 1"] },
lorem: {
type: "string",
},
},
required: ["lorem"],
},
{
properties: {
dropdown: { enum: ["Option 2"] },
ipsum: {
type: "string",
},
},
required: ["ipsum"],
},
],
},
},
}
You can read more at the "dependencies" page.
I have a fixed array like this And a return function to find the desired value. My problem is that even when the submenu is empty, an empty variable returns to the submenu: [] which I do not want
const myMenu = {items : [{
title: "menu 1",
root: true,
icon: "flaticon2-architecture-and-city",
page: "dashboard",
bullet: "dot",
MenuId:1,
},
{
title: "menu 2",
root: true,
bullet: "dot",
icon: "flaticon2-browser-2",
MenuId:2,
submenu: [
{
title: "sub menu 1",
bullet: "dot",
page: "AssetMainPage",
MenuId:3,
},
{
title: "sub menu 2",
page: "Departments",
bullet: "dot",
MenuId:4,
},
],
},
{
title: "menu 3",
root: true,
icon: "flaticon2-architecture-and-city",
page: "dashboard",
bullet: "dot",
MenuId:5,
submenu: [
{
title: "sub menu 1",
page: "AssetMeter",
MenuId:6,
},
{
title: "sub menu 2",
page: "ChangeAssetStatus",
MenuId:7,
},
{
title: "گزارش وضعیت و کارکرد",
page: "AssetsMeterAndStatusReports",
MenuId:8,
},
],
},
]}
And I have this Recursively function for find and retrn array :
function filter(arr, term) {
var matches = [];
if (!Array.isArray(arr)) return matches;
arr.forEach(function(i) {
if (i.MenuId === term) {
const filterData = (i.submenu && Array.isArray(i.submenu))? i.submenu.filter(values => values.MenuId === term):[];
// console.log(filterData)
i.submenu =filterData;
matches.push(i);
} else {
//console.log(i.children)
let childResults = filter(i.submenu, term);
if (childResults.length ) {
matches.push(Object.assign({}, i,{submenu: childResults} ));
}
}
})
return matches;
}
My question is whether I can not return that variable at all when the submenu array is empty.
Like the following output:
// call function
const filterData = filter(myMenu.items,7);
console.log("filterData",filterData)
i want this result :
title: "menu 3",
root: true,
icon: "flaticon2-architecture-and-city",
page: "dashboard",
bullet: "dot",
MenuId:5,
submenu :[
title: "sub menu 2",
page: "ChangeAssetStatus",
MenuId:7,
// exactly here i dont want return empty submenu:[]
]
Inside your arr.forEach function:
function filter(arr, term) {
var matches = [];
if (!Array.isArray(arr)) return matches;
arr.forEach(function (i) {
if (i.MenuId === term) {
const filterData =
i.submenu && Array.isArray(i.submenu)
? i.submenu.filter((values) => values.MenuId === term)
: [];
// console.log(filterData)
i.submenu = filterData;
if (!i.submenu.length) {
delete i.submenu;
}
matches.push(i);
} else {
//console.log(i.children)
let childResults = filter(i.submenu, term);
if (childResults.length) {
matches.push(Object.assign({}, i, { submenu: childResults }));
}
}
});
return matches;
}
Just before assign value to submenu property you can check that filtedData is an empty array or not, by checking it's length property:
...
const filterData = (i.submenu && Array.isArray(i.submenu))? i.submenu.filter(values => values.MenuId === term):[];
if(filterData.length){ i.submenu =filterData; }
...
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',
}
I am trying to get React-Select to display a different dropdown menu list based on the user input:
const helpOptions = [
{ value: "user", label: "u:<String> User Operator" },
{ value: "day", label: "d:<Number> Date Operator" },
{ value: "week", label: "w:<Number> Week Operator" },
{ value: "month", label: "m:<Number> Month Operator" },
{ value: "bracket", label: "() Brackets Operator" },
{ value: "and", label: "&& AND Operator" },
{ value: "or", label: "|| OR Operator" },
{ value: "not", label: "~ NOT Operator" }
];
const userOptions = [
{ value: "john", label: "u:John" },
];
class Field extends Component {
state = {
menu: userOptions,
value: ""
};
onInputChange = e => {
if (e.substring(0, 1) === "?") {
this.setState(
{
menu: helpOptions,
value: e
},
() => {
console.log(this.state.menu);
}
);
} else {
this.setState({
menu: []
});
}
};
render() {
const { menu, value } = this.state;
console.log("rendering");
console.log(menu);
return (
<Select
isMulti
value={value}
options={menu}
onInputChange={this.onInputChange}
/>
);
}
}
The desired behavior is if the first character of the text entered into the search field is a '?' the menu will populate with the const of helpOptions. Otherwise it would be (for now) empty.
Codesandbox: https://codesandbox.io/s/runtime-sun-cfg71
From the console logs, I seem to be getting the values and the rendering seems to be working. However, I am still getting 'No Option' as a response from the React-Select component.
How can I dynamically change the React-Select menu items based on the user's input?
Update
If you want your state to update after calling setState you need use a function that will work only after updating the state:
this.setState(state => ({
...state,
menu: helpOptions
}));
First of all you need to call a constructor in your component. Secondly, in documentation to react-select prop value doesn't exist. Thirdly, it’s good practice to copy your state before changing.
Here is a valid code:
import React, { Component } from "react";
import Select from "react-select";
import "./styles.css";
const helpOptions = [
{ value: "user", label: "u:<String> User Operator" },
{ value: "day", label: "d:<Number> Date Operator" },
{ value: "week", label: "w:<Number> Week Operator" },
{ value: "month", label: "m:<Number> Month Operator" },
{ value: "bracket", label: "() Brackets Operator" },
{ value: "and", label: "&& AND Operator" },
{ value: "or", label: "|| OR Operator" },
{ value: "not", label: "~ NOT Operator" }
];
const userOptions = [
{ value: "john", label: "u:John" },
{ value: "stan", label: "d:Stan" },
{ value: "addison", label: "w:Addison" },
{ value: "dionis", label: "m:Dionis" }
];
class Field extends Component {
constructor(props) {
super(props);
this.state = {
menu: userOptions,
value: ""
};
}
onInputChange = e => {
if (e.substring(0, 1) === "?") {
console.log("help");
this.setState({
...this.state,
menu: helpOptions
});
} else {
this.setState({
...this.state,
menu: userOptions
});
}
};
render() {
const { menu, value } = this.state;
return <Select isMulti options={menu} onInputChange={this.onInputChange} />;
}
}
export default Field;
Here is an example on codesandbox.
i have these array object
$scope.filters = {
1 : {
name : 'Pattern',
options : {
1 : { name: 'Plain', selected: false },
2 : { name: 'Self Design', selected: false },
3 : { name: 'Check', selected: false },
4 : { name: 'Stripe', selected: false },
5 : { name: 'Print', selected: false },
6 : { name: 'Others', selected: false }
}
},
2 : {
name : 'Colour',
options : {
1 : { name: 'White', selected: false },
2 : { name: 'Blue', selected: false },
3 : { name: 'Grey', selected: false },
4 : { name: 'Pink', selected: false },
5 : { name: 'Purple', selected: false },
6 : { name: 'Black', selected: false },
//7 : { name: 'Others', selected: false }
}
},
3 : {
name : 'Material',
options : {
1 : { name: 'Cotton', selected: false },
2 : { name: 'Technicals', selected: false },
3 : { name: 'CVC', selected: false },
4 : { name: 'Cotton, Two Ply', selected: false }
}
}
}
how to get a group list of selected = true filters using $watch?
if possible i want the result stucture to be like this :
{
Pattern : {
1 : Plain,
2 : Self Design
},
Colour : {
1: Blue
}
}
Try this:
var name ,options, selected = {};
for (var idx in $scope.filters) {
name = $scope.filters[idx]['name'];
options = $scope.filters[idx]['options'];
for (var i in options) {
if(options[i]['selected']) {
selected[name] = selected[name] || {};
selected[name][i] = options[i]['name'];
}
}
}
console.log(JSON.stringify(selected,null,4));
/*
{
"Pattern": {
"1": "Plain",
"2": "Self Design"
},
"Colour": {
"2": "Blue"
}
}
*/
Look at this plunkr. Not the exact solution, but will give you a start.
I have used $watch (with objectEquality parameter) to get the changed object. You can then use underscore.js or pure javascript to filter out the selected items.