Alan Thomas
Alan Thomas

Reputation: 1034

Multi-level Select Input

I think an example will best illustrate my question. Suppose I have a form to select books and their pages. The user will have one multi-select dropdown to select the books. Then, from each book, the user can select any number of pages. In the end, the data will look like a two-dimensional array. The top level is the array of books chosen, and the second level is the pages selected for each book:

books: {
  "Book 1": ["page1", "page2", "page3"],
  "Book 2": ["page1", "page2"]
}

The dropdown for books will be automatically populated. The options for the dropdown to select the pages for a book will be populated on loading.

Here is a hard coded example just to see the basic layout: http://codepen.io/alanqthomas/pen/JbVGzW?editors=1000

I'm doing this in React. I can't figure out a way to set these values in the component's state and display them dynamically. I'm using react-select for the multi-select selection inputs.

Upvotes: 4

Views: 2463

Answers (1)

Martin Dawson
Martin Dawson

Reputation: 7656

Sure. Here is what you want. As you want nested pages in books then the setState I did will need modifying but here's a start for you.

If you need help with it just ask.

class Form extends React.Component {
    constructor() {
        super();
    }

    render() {
        return (
        <form>
           <div>
            <Books />
          </div>
        </form>
        );
    }
}

class Books extends React.Component {
    constructor() {
        super();

        this.state = {
            books: {
              Book1: {
                pages: [
                {
                    text: Page 1,
                    value: Page1
                },
                {
                    text: Page 2,
                    value: Page2
                }],
                text: Book 1,
                value: Book1
              },
              Book2: {
                pages: [
                {
                    text: Page 1,
                    value: Page1
                },
                {
                    text: Page 2,
                    value: Page2
                }],
                text: Book 2,
                value: Book2
              }
            }
        }
        this.state.currentBookKey = this.state.books.Book1.value;
    }
    selectOnChange = (event) => {
        this.setState((state) => ({ currentBookKey: event.target.value }))
    }
    render() {
        return (
            <div>
                <label>Book</label>
                <select multiple name="book" onChange={this.selectOnChange}>
                    {this.state.books.map(book => <Book value={book.value}}>{book.text}</Book>)}
                </select>
                <label>Pages to select</label>
                <select multiple name="pages">
                    {this.state.books[this.state.currentBookKey].pages.map(page => <Page value={page.value}}>{page.text}</Page>)}
                </select>
            </div>
        )
    }
}

Stateless components:

//React.children.only gives us an exception if more than one child
const Book = ({children, value}) => (
    <option value={value}>{React.Children.only(children)}</option>
);

const Page = ({children, value}) => (
    <option value={value}>{React.Children.only(children)}</option>
);

Upvotes: 1

Related Questions