2016-05-11 13 views
1

Ich habe ein Problem, den Wert von einem <select> zu bekommen. Die Komponente, die es rendert, ist DualListBox, die dem <select>-Tag einen ref von selected hinzufügen sollte.Wie bekomme ich den Wert in der Eltern von einem Kind Komponenten <select> in ReactJS onSubmit()

<div className="rdl-selected"> 
      <select className="form-control" name={this.props.name} ref="selected" multiple> 
      {selected} 
      </select> 
     </div> 

Das Problem ist, dass in der übergeordneten Komponente von UpdateDialog Ich bin nicht in der Lage, den Wert für den DOM zu erhalten, wenn ich auf der Seite in der

ReactDOM.findDOMNode(this.refs['selected']).value.trim(); 

Frage handleSubmit klicken sparen:

Was fehlt mir? Ich frage mich, ob die Komponente mit etwas eingewickelt werden muss, um den Wert korrekt zurückzugeben.

aktualisieren hinzugefügt Änderungen wie für die Weitergabe von onChange @Klassman zur Kenntnis genommen und es funktioniert

diese Weise wird die onChange weitergegeben wird mit dem Zustandswert bezeichnet. Vielen Dank, dass Sie

** Gibt es eine andere Möglichkeit, dies zu tun, nicht den Wert innerhalb des Staates vorbei? ** ** Warum war ich nicht in der Lage es durch die ref zu verweisen? **

Danke

-Code

UpdateDialog

'use strict'; 

const React = require('react'); 
const ReactDOM = require('react-dom'); 

const when = require('when'); 
const client = require('./client'); 
const follow = require('./follow'); // function to hop multiple links by "rel" 


const ListBoxWidget = require('./ListBoxWidget'); 

const root = '/api'; 

class UpdateDialog extends React.Component { 

    constructor(props) { 
    super(props); 
    this.state = {selectedListObjects: null, unselectedListObjects: null}; 
    this.handleSubmit = this.handleSubmit.bind(this); 
    this.onChange = this.onChange.bind(this); 
    this.setupTestSuite = this.setupTestSuite.bind(this); 
    } 

    onChange(selected) { 
    this.setState({ selected }); 
    } 

    handleSubmit(e) { 
    e.preventDefault(); 
    var updatedObject = {}; 
    this.props.attributes.forEach(attribute => { 
     var rd = ReactDOM.findDOMNode(this.refs[attribute]).value.trim(); 
     console.log("rd for attribute " + attribute); 
     console.log(this.refs[attribute]); 
     console.log(rd); 
     updatedObject[attribute] = rd; 
    }); 


     console.log("going into test suite "); 
     console.log(this.props.objectName); 

    // save the list box value for the test cases 
    if (this.props.objectName == "Test Suite") { 
     console.log("going into test suite "); 
     console.log("this.refs['selected']"); 
     console.log(this.refs.selected); 
     console.log(this.refs.available); 

     console.log("this.state.selected"); 
     console.log(this.state.selected); 
     console.log("?"); 
     var selectedTestCases = ReactDOM.findDOMNode(this.refs['selected']).value.trim(); 
     console.log("selected react dom"); 
     console.log(selectedTestCases); 
     if (selectedTestCases != null) { 
     console.log("selected react dom . value"); 
     console.log(selectedTestCases.value); 
     } 

    } 
    this.props.onUpdate(this.props.object, updatedObject); 
    window.location = "#"; 
    } 

    componentDidMount() { 
    console.log("calling componentDidMount in update"); 

    if (this.props.objectName == "Test Suite") { 
     this.setupTestSuite(); 
    } 
    } 

    render() { 
    var inputs = this.props.attributes.map(attribute => 
     <p key={attribute}> 
      <input type="text" placeholder={attribute} 
       defaultValue={this.props.object.entity[attribute]} 
       ref={attribute} className="field" /> 
     </p> 
    ); 

    var dialogId = "updateObject-" + this.props.object.entity._links.self.href; 

    return (
     <div> 
     <a href={"#" + dialogId}>Update</a> 

     <div id={dialogId} className="modalDialog"> 
      <div> 
      <a href="#" title="Close" className="close">X</a> 

      <h2>Update an {this.props.objectName}</h2> 

      <form> 
       {inputs} 
       {this.props.objectName == "Test Suite" && this.state.selectedListObjects != null && this.state.unselectedListObjects != null && 
       <ListBoxWidget 
        onChange={this.onChange} 
        refName='selectedTestCasesRef' 
        name='selectedTestCases' 
        unselectedListObjects={this.state.unselectedListObjects} 
        selectedListObjects={this.state.selectedListObjects} /> 
       } 
       <button onClick={this.handleSubmit}>Update</button> 
      </form> 
      </div> 
     </div> 
     </div> 
    ) 
    } 

} 

module.exports = UpdateDialog 

ListBoxWidget

'use strict'; 

const React = require('react'); 

// import components 
const DualListBox = require('react-dual-listbox'); 

class ListBoxWidget extends React.Component { 
    constructor(props) { 
     super(props); 
     this.state = { selected: this.props.selectedListObjects }; 
     this.onChange = this.onChange.bind(this); 
    } 

    onChange(selected) { 
     this.setState({ selected }); 
     this.props.onChange(selected); 
    } 

    render() { 

     return (
      <DualListBox 
      name={this.props.name} 
      options={this.props.unselectedListObjects} 
      preserveSelectOrder 
      selected={this.state.selected} 
      defaultValue={this.props.selectedListObjects} 
      onChange={this.onChange} /> 

     ) 
    } 
} 

module.exports = ListBoxWidget 

Package.json

"babel-core": "^6.0.0", 
"babel-loader": "^6.0.0", 
"babel-preset-es2015": "^6.6.0", 
"babel-preset-react": "^6.5.0", 
"react": "^15.0.1", 
"react-dom": "^15.0.1", 
"rest": "^1.3.2", 
"sockjs-client": "^1.0.3", 
"stompjs": "^2.3.3", 
"webpack": "^1.13.0", 
"when": "^3.7.7", 
"react-bootstrap": "^0.29.3", 
"jquery": "^2.2.3", 
"react-dual-listbox": "^0.3.3" 

Hier ist die Klasse von reagieren-Dual-listbox

DualListBox

import React from 'react'; 

import Action from './Action'; 

class DualListBox extends React.Component { 
    static propTypes = { 
    name: React.PropTypes.string, 
    options: React.PropTypes.array, 
    available: React.PropTypes.array, 
    selected: React.PropTypes.array, 
    onChange: React.PropTypes.func, 
    preserveSelectOrder: React.PropTypes.bool, 
    }; 

    /** 
    * @param {Object} props 
    * 
    * @returns {void} 
    */ 
    constructor(props) { 
    super(props); 

    this.onClick = this.onClick.bind(this); 
    this.onDoubleClick = this.onDoubleClick.bind(this); 
    } 

    /** 
    * @param {Object} event 
    * 
    * @return {void} 
    */ 
    onClick(event) { 
    const { target } = event; 
    const { options, onChange } = this.props; 
    const direction = target.dataset.moveDirection; 
    const isMoveAll = target.dataset.moveAll; 
    const selectRef = direction === 'right' ? 'available' : 'selected'; 

    let selected = []; 

    if (isMoveAll === '1') { 
     selected = direction === 'right' ? this.makeOptionsSelected(options) : []; 
    } else { 
     selected = this.toggleSelected(
     this.getSelectedOptions(this.refs[selectRef]) 
    ); 
    } 

    onChange(selected); 
    } 

    /** 
    * @param {Object} event 
    * 
    * @returns {void} 
    */ 
    onDoubleClick(event) { 
    const value = event.target.value; 
    const selected = this.toggleSelected([value]); 

    this.props.onChange(selected); 
    } 

    /** 
    * Converts a flat array to a key/value mapping. 
    * 
    * @param {Array} options 
    * 
    * @returns {Object} 
    */ 
    getLabelMap(options) { 
    let labelMap = {}; 

    options.forEach((option) => { 
     if (option.options !== undefined) { 
     labelMap = { ...labelMap, ...this.getLabelMap(option.options) }; 
     } else { 
     labelMap[option.value] = option.label; 
     } 
    }); 

    return labelMap; 
    } 

    /** 
    * Returns the selected options from a given element. 
    * 
    * @param {Object} element 
    * 
    * @returns {Array} 
    */ 
    getSelectedOptions(element) { 
    return [...element.options] 
     .filter((option) => option.selected) 
     .map((option) => option.value); 
    } 

    /** 
    * Make all the given options selected. 
    * 
    * @param {Array} options 
    * 
    * @returns {Array} 
    */ 
    makeOptionsSelected(options) { 
    let selected = []; 

    this.filterAvailable(options).forEach((option) => { 
     if (option.options !== undefined) { 
     selected = [...selected, ...this.makeOptionsSelected(option.options)]; 
     } else { 
     selected.push(option.value); 
     } 
    }); 

    return [...this.props.selected, ...selected]; 
    } 

    /** 
    * Toggle a new set of selected elements. 
    * 
    * @param {Array} selected 
    * 
    * @returns {Array} 
    */ 
    toggleSelected(selected) { 
    const oldSelected = this.props.selected.slice(0); 

    selected.forEach((value) => { 
     const index = oldSelected.indexOf(value); 

     if (index >= 0) { 
     oldSelected.splice(index, 1); 
     } else { 
     oldSelected.push(value); 
     } 
    }); 

    return oldSelected; 
    } 

    /** 
    * Filter options by a filtering function. 
    * 
    * @param {Array} options 
    * @param {Function} filterer 
    * 
    * @returns {Array} 
    */ 
    filterOptions(options, filterer) { 
    const filtered = []; 

    options.forEach((option) => { 
     if (option.options !== undefined) { 
     const children = this.filterOptions(option.options, filterer); 

     if (children.length > 0) { 
      filtered.push({ 
      label: option.label, 
      options: children, 
      }); 
     } 
     } else if (filterer(option)) { 
     filtered.push(option); 
     } 
    }); 

    return filtered; 
    } 

    /** 
    * Filter the available options. 
    * 
    * @param {Array} options 
    * 
    * @returns {Array} 
    */ 
    filterAvailable(options) { 
    if (this.props.available !== undefined) { 
     return this.filterOptions(options, (option) => 
     this.props.available.indexOf(option.value) >= 0 && 
     this.props.selected.indexOf(option.value) < 0 
    ); 
    } 

    // Show all un-selected options 
    return this.filterOptions(options, (option) => 
     this.props.selected.indexOf(option.value) < 0 
    ); 
    } 

    /** 
    * Filter the selected options. 
    * 
    * @param {Array} options 
    * 
    * @returns {Array} 
    */ 
    filterSelected(options) { 
    if (this.props.preserveSelectOrder) { 
     return this.filterSelectedByOrder(options); 
    } 

    // Order the selections by the default order 
    return this.filterOptions(options, (option) => 
     this.props.selected.indexOf(option.value) >= 0 
    ); 
    } 

    /** 
    * Preserve the selection order. This drops the opt-group associations. 
    * 
    * @param {Array} options 
    * 
    * @returns {Array} 
    */ 
    filterSelectedByOrder(options) { 
    const labelMap = this.getLabelMap(options); 

    return this.props.selected.map((selected) => ({ 
     value: selected, 
     label: labelMap[selected], 
    })); 
    } 

    /** 
    * @returns {Array} 
    */ 
    renderOptions(options) { 
    return options.map((option, index) => { 
     if (option.options !== undefined) { 
     return (
      <optgroup key={index} label={option.label}> 
      {this.renderOptions(option.options)} 
      </optgroup> 
     ); 
     } 

     return (
     <option key={index} value={option.value} onDoubleClick={this.onDoubleClick}> 
      {option.label} 
     </option> 
    ); 
    }); 
    } 

    /** 
    * @returns {React.Component} 
    */ 
    render() { 
    const { options } = this.props; 
    const available = this.renderOptions(this.filterAvailable(options)); 
    const selected = this.renderOptions(this.filterSelected(options)); 

    return (
     <div className="react-dual-listbox"> 
     <div className="rdl-available"> 
      <select className="form-control" ref="available" multiple> 
      {available} 
      </select> 
     </div> 
     <div className="rdl-actions"> 
      <div className="rdl-actions-right"> 
      <Action direction="right" isMoveAll onClick={this.onClick} /> 
      <Action direction="right" onClick={this.onClick} /> 
      </div> 
      <div className="rdl-actions-left"> 
      <Action direction="left" onClick={this.onClick} /> 
      <Action direction="left" isMoveAll onClick={this.onClick} /> 
      </div> 
     </div> 
     <div className="rdl-selected"> 
      <select className="form-control" name={this.props.name} ref="selected" multiple> 
      {selected} 
      </select> 
     </div> 
     </div> 
    ); 
    } 
} 

export default DualListBox; 

Antwort

2

Sie könnten auf dem onChange Rückruf übergeben von der ListBoxWidget zum UpdateDialog

vereinfachtes Beispiel:

class UpdateDialog extends React.Component { 

    onChange (selected) { 
    // tadaa! `selected` has passed on to this component 
    this.setState({ selected }) 
    } 

    handleSubmit() { 
    // instead of the `ReactDOM.findDOMNode` stuff, just point to your state: 
    const selected = this.state.selected 
    // ... 
    } 

    render() { 
    return <ListBoxWidget onChange={this.onChange} /> 
    } 
} 


class ListBoxWidget extends React.Component { 

    onChange (selected) { 
    this.setState({ selected }) 
    this.props.onChange(selected) 
    } 

    render() { 
    return <DualListBox onChange={onChange} /> 
    } 
} 
+0

Danke ich habe es funktioniert. Also rufe ich den onChange von UpdateDialog über 'this.props.onChange (selected) 'auf, was dann den Zustand korrekt setzt? – ALM

+0

Warum können Sie den Ref nicht wie gewohnt referenzieren? Liegt es daran, dass es sich um eine untergeordnete Komponente handelt? – ALM

+0

Referenzen sind an die Komponente gebunden, an der Sie sie definieren. Daher ist es nicht möglich, eine Referenz zu verwenden, die in einer untergeordneten Komponente definiert ist. Nur in dieser untergeordneten Komponente können Sie auf "this.refs.whatever" verweisen. – klaasman