react-dropdown-tree-select ,After the child nodes are selected one by one, the parent node is not automatically selected. Is there a solution? - javascript

I'm using react-dropdown-tree-select , After the child nodes are selected one by one, the parent node is not automatically selected. Is there a solution?
import React from 'react'
import DropdownTreeSelect from 'react-dropdown-tree-select'
import 'react-dropdown-tree-select/dist/styles.css'
import './test.css'
const data = {
label: 'search me',
value: 'searchme',
children: [
{
label: 'search me too',
value: 'searchmetoo',
children: [
{
label: 'No one can get me',
value: 'anonymous',
},
],
},
],
}
export default function testRcTree() {
const onChange = (currentNode, selectedNodes) => {
console.log('onChange::', currentNode, selectedNodes)
}
const onAction = (node, action) => {
console.log('onAction::', action, node)
}
const onNodeToggle = currentNode => {
console.log('onNodeToggle::', currentNode)
}
return (
<div >
<DropdownTreeSelect
multiSelect
className='mdl-demo'
data={data}
onChange={onChange}
onAction={onAction}
onNodeToggle={onNodeToggle} />
</div>
)
}

You would need to explicitly mark the checked attribute of parent node as true. There are two variables for a node's parent, "checked" and "expanded" I see expanded is true. But please find the parent of that node,which is sitting at selectedNode.parent and set it as
selectedNode.parent.checked=true;

Related

React-Select if value exists show value if not show placeholder text

I'm using this component here: https://react-select.com/home
What I'm trying to accomplish is on page load if my array contains a value in my Assigned element then I want to display that by default in my react-select box if not then I want my placeholder Assign to to show.
Here is how my select looks:
<Select
value={this.state.Product[0].Assigned ? this.state.Product[0].Assigned : selectedAssigned}
onChange={this.handleChangeAssigned}
options={this.state.AssignedList}
placeholder={<div>Assign to:</div>}
/>
In my Product[0].Assigned there is currently a value, however the dropdown still has the placeholder Assign To. I tried changing value to value={this.state.Product[0].Assigned} but still no luck.
Here is my change handle:
handleChangeAssigned = selectedAssigned => {
this.setState(
{ selectedAssigned },
() => console.log(`Option selected:`, this.state.selectedAssigned)
);
};
It seems like you are storing the same data in multiple places. You are checking if you have an assignment by looking at this.state.Product[0].Assigned. But when you select an assignment you are not updating that property. Instead you are updating a completely separate property this.state.selectedAssigned. this.state.Product[0].Assigned never changes so if you see a placeholder at first then you will always see a placeholder.
The value that you set on the Select needs to be the same as the value that you update.
import React from "react";
import Select from "react-select";
interface Option {
label: string;
value: string;
}
interface State {
Product: any[];
AssignedList: Option[];
selectedAssigned: Option | null;
}
export default class MyComponent extends React.Component<{}, State> {
state: State = {
Product: [],
AssignedList: [
{ label: "a", value: "a" },
{ label: "b", value: "b" },
{ label: "c", value: "c" }
],
selectedAssigned: null
};
handleChangeAssigned = (selectedAssigned: Option | null) => {
this.setState({ selectedAssigned }, () =>
console.log(`Option selected:`, this.state.selectedAssigned)
);
};
render() {
return (
<Select
value={this.state.selectedAssigned}
onChange={this.handleChangeAssigned}
options={this.state.AssignedList}
placeholder={<div>Assign to:</div>}
/>
);
}
}
Code Sandbox Link

Why do these similar react function passing statements work in different ways? [duplicate]

This question already has answers here:
What are the differences (if any) between ES6 arrow functions and functions bound with Function.prototype.bind?
(3 answers)
Why and when do we need to bind functions and eventHandlers in React?
(2 answers)
Closed 2 years ago.
I have simple react page showing a Tree component and a series of fields. Right now I have the Tree hardcoded, but the fields as passed in as props. Also passed in as props are two parent callbacks. One for the Tree onSelect and one for the <input> onChange event. In both cases, the specific 'on' Event is a local function that in turn calls the parent's callback. This is all working....
In both cases the local functions reference the 'this' variable. In one local function, the Tree onSelect', I had to use the '.bind(this)' way but, in the other I did not. Both local functions can access the 'this.props.' values. However, if I remove the '.bind(this)' from the one used in the Tree component it fails. The 'this' is undefined.
I am new to react, so I'm just trying to figure what goes where. I'm guessing this has something to do with the Tree being a component and the <input> as something more basic?
Thanks for any insights...
import React, { Component } from "react";
import Tree from '#naisutech/react-tree'
import './RecipePage.css';
class RecipePage extends Component {
constructor(props){
super(props);
this.state = { items: props.items,};
}
onMySelect (props) {
debugger;
const items = this.state.items.slice();
console.log("HI" , props);
}
handleChange = ({ target }) => {
debugger;
const items = this.state.items.slice();
items[parseInt(target.id)+1].defaultValue = target.value;
this.setState({items: items,});
var props = {items, target};
this.props.onInputChanged(props); // call the parent's update function send relavent data.
};
render() {
const t8000 = [
{
label: 'Gauge 1',
id: 1234,
parentId: null,
items: null
},
{
label: 'Target',
id: 1236,
parentId: 1234,
items: null
},
{
label: 'Gage Factor',
id: 5678,
parentId: 1234,
items: null
},
{
label: 'Gauge 2',
id: 1235,
parentId: null,
items: null
},
{
label: 'Target',
id: 2236,
parentId: 1235,
items: null
},
]
const myTheme = {
'my-theme': {
text: '#161616',
bg: '#f1f1f1',
highlight: '#cccccc', // the colours used for selected and hover items
decal: 'green', // the colours used for open folder indicators and icons
accent: '#f1f1f1' // the colour used for row borders and empty file indicators
}
}
return(
<div id="recipePage" className="recipeMenu" >
<div className="closeButton" >
<img src={require('./../CommonImages/closeButton_W_90x90.png')} height="90px" onClick={() => this.props.close()} alt="Menu buttons"></img>
<Tree nodes={t8000} onSelect={this.onMySelect.bind(this)} theme = {'my-theme'} customTheme={myTheme} />
<form>
<fieldset>
<legend>this.state.items[0].label}</legend>
{this.state.items.map((item, key) =>(
<div className={item.show===1 && key!==0 ?"ShowInputs":"HideInputs"}>
<label htmlFor={item.id}>{item.label} </label>
<input type="text" name={item.id}
id={item.id} value={item.defaultValue}
onChange={this.handleChange} />
</div>
))}
</fieldset>
</form>
</div>
</div>
);
}
}
export default RecipePage;

reset value when using react-select

I am using react-select for my select dropdown. The issue I am having is that there is no empty option to reset the dropdown value if the user changes their mind.
Currently I am taking the options and manually adding an empty string, but I feel there must be something already in the library to handle this? I cannot find anything in the docs.
My code looks like the below, and there is a code sandbox here.
import React from "react";
import Select from "react-select";
const App = () => {
const options = [
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" }
];
return <Dropdown options={options} />;
}
const Dropdown = ({ options }) => {
const optionsWithEmptyOption = [{ value: "", label: "" }, ...options];
return <Select options={optionsWithEmptyOption} />;
};
Plase check this out
https://codesandbox.io/s/zow1c?module=/example.js
import React, { Component } from 'react';
import CreatableSelect from 'react-select/creatable';
import { colourOptions } from './docs/data';
export default class CreatableSingle extends Component<*, State> {
handleChange = (newValue: any, actionMeta: any) => {
console.group('Value Changed');
console.log(newValue);
console.log(`action: ${actionMeta.action}`);
console.groupEnd();
};
handleInputChange = (inputValue: any, actionMeta: any) => {
console.group('Input Changed');
console.log(inputValue);
console.log(`action: ${actionMeta.action}`);
console.groupEnd();
};
render() {
return (
<CreatableSelect
isClearable
onChange={this.handleChange}
onInputChange={this.handleInputChange}
options={colourOptions}
/>
);
}
}
Empty Unicode
I add line to options, and write between the apostrophes empty unicode like this: ⠀⠀⠀⠀⠀⠀⠀⠀ .. you can mark it but dont see it.
const options = [
{ value: "", label: "⠀" },
{ value: "chocolate", label: "Chocolate" },
{ value: "strawberry", label: "Strawberry" },
{ value: "vanilla", label: "Vanilla" }
];
And I change this:
return <Select options={options} />;
what about const optionsWithEmptyOption = [{ value: null, label: "Select..." }, ...options];
I'm not really good with explanations, but #NicoHaase is right, so here it goes...
as far as I know, you must give a value to value null (if nothing) or string ... same for the label, #1 because of the user UX and second so react-select knows what to display. But if you really need to leave it in black, and you can try to modify in the styles, in order to have the same height as the other options.

How to build React checkbox tree

I'm trying to work with a checkbox tree component like this: https://www.npmjs.com/package/react-checkbox-tree, except I'm storing the items that I have selected in Redux. Moreover, the only items that I'm actually storing are the leaf nodes in the tree. So for example, I'd have the full options data which would be used to render the tree:
const fam = {
cuz2: {
name: 'cuz2',
children: {
cuzKid2: {
name: 'cuzKid2',
children: {
}
}
}
},
grandpa: {
name: 'grandpa',
children: {
dad: {
name: 'dad',
children: {
me: {
name: 'me',
children: {}
},
sis: {
name: 'sis',
children: {}
}
}
},
aunt: {
name: 'aunt',
children: {
cuz: {
name: 'cuz',
children: {
name: 'cuzkid',
children: {}
}
}
}
}
}
}
and a separate object that stores the items selected. The following would be the only items that would appear if every checkbox was checked:
const selected = {
cuz2: true,
me: true,
sis: true,
cuz: true
}
I seem to be struggling with this method for having the UI determine which boxes to have fully, partially, or un-checked based on the selected object. I was wondering if anyone can recommend another strategy of accomplishing this.
So I have used react-checkbox-tree but I have customised a bit the icons in order to use another icons library.
Check my example on sandbox:
The library provides a basic example of how to render a tree with selected and/or expanded nodes.
All you need to do is:
set up the nodes with a unique 'value'
Choose which items should be selected (it may comes from Redux)
pass nodes & checked list to the CheckBox constructor
also be sure that when user select/unselect, you update the UI properly using the state
Your code should look similar to this:
import React from 'react';
import CheckboxTree from 'react-checkbox-tree';
const nodes = [{
value: '/cuz2',
label: 'cuz2',
children: [],
},
// other nodes
];
class BasicExample extends React.Component {
state = {
checked: [
'/cuz2'
],
expanded: [
'/cuz2',
],
};
constructor(props) {
super(props);
this.onCheck = this.onCheck.bind(this);
this.onExpand = this.onExpand.bind(this);
}
onCheck(checked) {
this.setState({
checked
});
}
onExpand(expanded) {
this.setState({
expanded
});
}
render() {
const {
checked,
expanded
} = this.state;
return (<
CheckboxTree checked={
checked
}
expanded={
expanded
}
nodes={
nodes
}
onCheck={
this.onCheck
}
onExpand={
this.onExpand
}
/>
);
}
}
export default BasicExample;

How to manage a state 'collapsed' for each deeply nested node in a tree

I have a complicated and dynamic data structure like so:
const tree = [
{
name: "Root Node",
collapsed: true,
nodes: [
{
name: "Node 1",
collapsed: true,
nodes: [
{
name: "Sub node"
}
]
},
{
name: "Node 2",
collapsed: true,
nodes: [
{
name: "Sub node "
}
]
},
{
name: "Node 3",
collapsed: true,
nodes: [
{
name: "Sub node"
}
]
}
]
}
];`
I am setting this as the initial state of my component.
I am then rendering this state out as a hierarchy tree in the UI.
When I click the top level node I want to update the collapsed property in state to have it open up and show the next set of nodes.
The problem I am having is how do I call setState() and update this complicated data structure without causing mutation and doing some ugly stuff like tree[0].nodes[0].collapsed: false.
So I first tried setting state like this
handleClick(el, event) {
this.setState({
tree: this.findAndUpdateState(event.target.id).bind(this)
});
}
So on the handleClick event of the node I call this which calls findAndUpdateState.
findAndUpdateState(id) {
this.state.tree.map((node) => {
//Map over the nodes somehow and find the node that needs its state updated using the ID?
});
});
}
I want to update the collapsed property of a node that has been attempted to be expanded. But to do so I have to loop through all the state and then create a new copy of the state just to update that one property. There must a nicer and simpler way that I am not thinking of.
React is pretty good at doing these sort of things.
I'm using the the Hooks in React, as I like them.. :)
Here is a working snippet below..
const tree = [{"name":"Root Node","collapsed":true,"nodes":[{"name":"Node 1","collapsed":true,"nodes":[{"name":"Sub node"}]},{"name":"Node 2","collapsed":true,"nodes":[{"name":"Sub node "}]},{"name":"Node 3","collapsed":true,"nodes":[{"name":"Sub node"}]}]}];
const {useState} = React;
function TreeItem(props) {
const {item} = props;
const [collapsed, setCollapsed] = useState(item.collapsed);
return <div className="item">
<span onClick={() => setCollapsed(!collapsed)}>{item.name}</span>
{!collapsed && item.nodes &&
<div style={{paddingLeft: "1rem"}}>
<TreeList list={item.nodes}/>
</div>
}
</div>
}
function TreeList(props) {
const {list} = props;
return <div>{list.map(f => <TreeItem key={f.name} item={f}/>)}</div>;
}
ReactDOM.render(<TreeList list={tree}/>, document.querySelector('#mount'));
.item {
cursor: pointer;
user-select: none;
}
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="mount"></div>
You could create a component Node with its own state collapsed:
class Node extends Component {
state = {
collapsed: true,
}
toggle = () => {
this.setState(prevState => ({ collapsed: !prevState.collapsed }))
}
render() {
return (
<div>
<p onClick={this.toggle}>{this.props.node.name}</p>
{!this.state.collapsed && (
<div>{this.props.children}</div>
)}
</div>
)
}
}
And create a parent component Tree that renders all the nodes recursively:
const TREE = [{ name: "Root Node", nodes: [...] }]
class Tree extends Component {
renderNodesRecursively = parent => {
return (
<Node node={parent} key={parent.name}>
{parent.nodes
? parent.nodes.map(node => this.renderNodesRecursively(node))
: null
}
</Node>
)
}
render() {
return TREE.map(node => this.renderNodesRecursively(node))
}
}
You will notice that the state of the children is lost each time you toggle the parent. It's "normal" since toggling the parent mounts/unmounts the children. If you want to avoid that, you can replace this code
{!this.state.collapsed && (
<div>{this.props.children}</div>
)}
with
<div style={{ display: this.state.collapsed ? 'none' : 'block' }}>
{this.props.children}
</div>

Categories

Resources