Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Trouble updating data from customEditor: "onUpdate is not a function" #1030

Closed
bpraggastis opened this issue Feb 10, 2017 · 11 comments
Closed

Comments

@bpraggastis
Copy link

bpraggastis commented Feb 10, 2017

I have one column in my table which uses a customEditor. The props.defaultValue is an object 'contact' which has id and last_name fields. I created a drop down using these fields and want the user to be able to choose an item and have the id bubble back up to the table so that it will save the information to the model as it does successfully in the non custom columns. I had hoped to pass a function from the parent component into the customEditor using customEditorParameters, but when I try this I found that only strings were accepted, not methods in the customEditorParameters object.
Here is the customEditor component:

class ContactEditor extends React.Component {
    constructor(props) {
        super(props);
        this.updateData = this.updateData.bind(this);
        this.onChange = this.onChange.bind(this);
        this.state = {
            contact: props.defaultValue
        }
    }
    focus() { }
    updateData() {
        this.props.onUpdate( { contact: this.state.contact } );
    }
    onChange(evt) {
        this.setState({ contact: evt.currentTarget.value });
        this.updateData();
    }
    render() {
        return (
            <div>
            <select value={ this.state.contact.id }
                    onChange={ this.onChange } >
                {
                    myState.contacts.map(function(contact){
                    return <option
                                key={contact.last_name}
                                value={ contact.id }>
                            { contact.last_name }
                            </option>
                })}
            </select>
            <button
                className='btn btn-info btn-xs textarea-save-btn'
                onClick={ this.updateData }>
                save
            </button>
            </div>
        );
    }
}

It is called from the following column:

            <TableHeaderColumn dataField='contact'
                               dataSort
                               dataFormat={ this.formatName }
                               sortFunc={ this.sortByLastName }
                               filter={{ type: 'TextFilter' }}
                               customEditor={ { getElement: this.createContactEditor, customEditorparameters: { updateContact: this.updateContact } } }>
                    Primary Contact
            </TableHeaderColumn>

element is created here:

createContactEditor(onUpdate, props, updateContact ) {
        console.log("props: ",props);
        return (
            <ContactEditor onUpDate={ onUpdate } onChange={ function(evt) { updateContact(props.row.id, evt.currentTarget.value); } }
                {...props}/>
        );
    };

When I click on the column I get the dropdown and am able to update the selected item. But when I click the save button I get the error:
Uncaught TypeError: this.props.onUpdate is not a function at ContactEditor.updateData

When I first open the contact editor I get this error:
Uncaught Error: addComponentAsRefTo(...): Only a ReactOwner can have refs.

Would be grateful for any ideas on how to use the customEditor and bubble up the data!
Thanks

@AllenFang
Copy link
Owner

AllenFang commented Feb 11, 2017

@bpraggastis, I try to pass a function into customEditorParameters and call it in the custom Editor and it works, BTW, I try your example but I get following question,

  1. I dont know what's the myState in the render method in ContactEditor component
  2. The onChange attribute on the ContactEditor component seems never be called inside the ContactEditor

BTW, sorry for busy day, I've no time to constructor you example with a hard work, so hope you can check your code again, and if the problem is still remain, please post a fully simple and minimal example which can let me run it quickly for reproducing this issue.

Another thing is I find you give onUpDate attribute on the ContactEditor component, is that correct??, because you should call onUpDate inside the ContactEditor component instead onUpdate. Maybe you have a wrong wording

thanks

@bpraggastis
Copy link
Author

bpraggastis commented Feb 14, 2017

Hi @AllenFang,
Thanks for being so quick. I pruned the code to make a complete somewhat minimal example for you to look at. Note there are two drop downs. One works because it uses the built in select option that comes with the table. The other one is using a custom editor; that one doesn't work. I have a Rails back end that I am retrieving the contact information and data from for this table. I need to be able to use custom editors for several fields. I keep getting this error:

Uncaught Error: addComponentAsRefTo(...): Only a ReactOwner can have refs. You might be adding a ref to a component that was not created inside a component's `render` method, or you have multiple copies of React loaded 

When I click the save button I get:

Uncaught Error: removeComponentAsRefFrom(...): Only a ReactOwner can have refs. You might be removing a ref to a component that was not created inside a component's `render` method, or you have multiple copies of React loaded 

Here is the code:

var myState = {
    selections : [],
    contacts : [{id: 1, last_name: "Fankhauser"},{id: 2, last_name: "Larson"},{id: 3, last_name: "Denlinger"},{id: 4, last_name: "Kreyling"}]
};

const defaultData = { events: [
    {id: 1, event_type: 'Information Session (Virtual or On Campus)', contact: {id: 1, last_name: "Fankhauser"} },
    {id: 2, event_type: 'Virtual Q&A session', contact: {id: 2, last_name: "Larson"}},
    {id: 3, event_type: 'Research Colloquium', contact: {id: 2, last_name: "Larson"}},
    {id: 4, event_type: 'Virtual Q&A session', contact: {id: 3, last_name: "Denlinger"}},
    {id: 5, event_type: 'Research Colloquium', contact: {id: 4, last_name: "Kreyling"}}
]}

const eventOptions = ['Information Session (Virtual or On Campus)','Virtual Q&A session','Research Colloquium','One on One Event (Student Centered or Faculty Staff Centered','Other (please specify under Notes)'];

var afterSearch = function(searchText, result) {
    console.log('Your search text is ' + searchText);
    console.log('There are ', result.length,' results');
}

$('#getSelected').on('click',function(){
    var str = 'Selections were: '+ myState.selections.join(',');
    alert(str);
});

var onAfterSaveCell = function(row, cellName, cellValue) {
    console.log("onAfterSave called");
};

class ContactEditor extends React.Component {
    constructor(props) {
        super(props);
        this.updateData = this.updateData.bind(this);
        this.onChange = this.onChange.bind(this);
        this.state = {
            contact: props.defaultValue
        }
    }
    focus() { }  //// this is here because I saw it recommended in another issue

    updateData() {
        this.props.onUpdate( { contact: this.state.contact } );
    }
    onChange(evt) {
        var contact = myState.contacts.filter(function(contact) {return contact.id == evt.currentTarget.value})[0]
        this.setState({ contact: contact });
        this.updateData();
    }

    render() {
        return (
            <div>
                <select value={ this.state.contact.id }
                        onChange={ this.onChange } >
                    {
                        myState.contacts.map(function(contact){
                            return <option
                                key={contact.last_name}
                                value={ contact.id }>
                                { contact.last_name }
                            </option>
                        })}
                </select>
                <button
                    className='btn btn-info btn-xs textarea-save-btn'
                    onClick={ this.updateData }>
                    save
                </button>
            </div>
        );

    }
}


export default class RecruitingEventsPages extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            data: [],
            selected: [],
            contacts: []
        };
        this.handleSelected = this.handleSelected.bind(this);
        this.handleSelectAll = this.handleSelectAll.bind(this);
        this.onContactChange = this.onContactChange.bind(this);
    };

    renderShowsTotal(start, to, total) {
        return (
            <p style={ { color: 'blue' } }>
                From { start } to { to }, totals is { total }&nbsp;&nbsp;(its a customize text)
            </p>
        );
    }

    eventDelete(cell,row){
        return '<a href="/admin/recruiting/events/'+ row.id + '" data-method="delete" rel="nofollow">' +
               '<span class="glyphicon glyphicon-trash text-danger" style="" aria-hidden="true"></span></a>   ' + cell;
    }

    formatName(cell,row, enumObject, index){
        return row.contact.last_name;
    }

    handleSelected(row,isSelected,event){
        var sel, idx, that = this;
        sel = myState.selections;

        if (isSelected){
            sel.push(row.id);

        } else {
            idx = sel.indexOf(row.id);
            sel.splice(idx,1);
        }
        that.setState({ selected : sel });
        console.log(that.state.selected,myState.selections);
    }

    handleSelectAll(isSelected, rows){
        var sel = [], that = this;
        console.log(that.state.selected);
        if (isSelected) {
            console.log(isSelected);
            sel = rows.map(function(row){ return row.id });
        }
        that.setState({ selected : sel });
        myState.selections = sel;
        console.log(that.state.selected, myState);
    }

    onContactChange(evt,row){
        console.log("contact changed", evt, row.id);

    }

    updateContact(rowID, contactID) {
        alert( "ID " + rowID.toString() + " " + contactID.toString() );  ////// I can't get this to activate.
    }

    createContactEditor(onUpdate, props) {
        return (
            <ContactEditor onUpdate={ onUpdate } onChange={ function(evt) { props.updateContact(props.row.id, evt.currentTarget.value); } }
                           {...props}/>
        );
    };



    componentDidMount(){
        this.setState({data : defaultData.events, contacts : myState.contacts});
    };


    componentWillMount(){
        console.log("willMount ",this.state);
    }
    componentDidUnMount(){
        console.log("didUnMount ",this.state);
    }

    render() {

        const selectRowProp = {
            mode: 'checkbox',
            bgColor: 'rgba(200, 251, 251, 0.9)',
            clickToSelect: true,
            showOnlySelected: true,
            onSelect: this.handleSelected,
            onSelectAll: this.handleSelectAll
        };


        const cellEditProp = {
            mode: 'click',
            blurToSave: true,
            afterSaveCell: onAfterSaveCell
        };

        const options = {
            page: 1,  // which page you want to show as default
            sizePerPageList: [
                { text: '10', value: 10 },
                { text: '15', value: 15 },
                { text: '25', value: 25 },
                { text: '50', value: 50 }
            ], // you can change the dropdown list for size per page
            sizePerPage: 10,  // which size per page you want to locate as default
            pageStartIndex: 1, // where to start counting the pages
            paginationSize: 3,  // the pagination bar size.
            prePage: 'Prev', // Previous page button text
            nextPage: 'Next', // Next page button text
            firstPage: 'First', // First page button text
            lastPage: 'Last', // Last page button text
            paginationShowsTotal: true, // this.renderShowsTotal  // Accept bool or function
            afterSearch: afterSearch, //define an after search hook
            onRowClick: this.handleRowClick
        };
        return (
            <BootstrapTable
                data={ this.state.data }
                search={ true }
                multiColumnSearch={ true }
                pagination={ true }
                selectRow={ selectRowProp }
                hover={ true }
                cellEdit={ cellEditProp }
                options={ options }
                editable={ true }>
                <TableHeaderColumn isKey={ true } width='50' autoValue={ true } dataField='id' dataFormat={ this.eventDelete }>ID</TableHeaderColumn>
                <TableHeaderColumn dataField='event_type' editable={ { type: 'select', options: { values: eventOptions } } } width='150' dataSort filter={{ type: 'TextFilter' }} >Event Type</TableHeaderColumn>
                <TableHeaderColumn dataField='contact'
                                   dataSort
                                   dataFormat={ this.formatName }
                                   sortFunc={ this.sortByLastName }
                                   filter={{ type: 'TextFilter' }}
                                   onChange={this.onContactChange.bind(this)}
                                   customEditor={ { getElement: this.createContactEditor, customEditorParameters: { updateContact: this.updateContact } } }>
                    Primary Contact
                </TableHeaderColumn>

            </BootstrapTable>
        );
    }
};

RecruitingEventsPages.PropTypes = {
    events: React.PropTypes.object
};

Thanks

@bpraggastis
Copy link
Author

bpraggastis commented Feb 14, 2017

Please note that I was able to get the code to update the model.
The only issue I am having is with those two error messages at the top and the dropdown and save button never revert back to the field. So once I click on the field to edit, I can't do anything else.

@AllenFang
Copy link
Owner

@bpraggastis, could you please confirm that you have duplicated react version in your node_modules?
maybe you can remove it all and install again,

BTW, what's the version of your react and react-bootstrap-table, and any dependencies have react as dependency also?

@bpraggastis
Copy link
Author

bpraggastis commented Feb 15, 2017

@AllenFang I am not sure what you mean by 'duplicated' react version in my node_modules.
Here is a copy of my dependencies:

    "babel": "^6.5.2",
    "babel-cli": "^6.6.5",
    "babel-core": "^6.7.4",
    "babel-loader": "^6.2.4",
    "babel-plugin-transform-es2015-modules-commonjs": "^6.18.0",
    "babel-polyfill": "^6.7.4",
    "babel-preset-airbnb": "^2.1.1",
    "babel-preset-es2015": "^6.6.0",
    "babel-preset-react": "^6.5.0",
    "babel-preset-stage-0": "^6.5.0",
    "babel-runtime": "^6.6.1",
    "es5-shim": "^4.5.7",
    "expose-loader": "^0.7.1",
    "imports-loader": "^0.6.5",
    "react": "^0.14.8 || ^15.0.0",
    "react-bootstrap-table": "^2.6.0",
    "react-dom": "^0.14.8 || ^15.0.0",
    "react-on-rails": "6.1.2",
    "webpack": "^1.12.14",
    "moment": "~2.17.1",
    "moment-timezone": "~0.5.10"
  },
  "devDependencies": {
    "chai": "^3.5.0",
    "enzyme": "^2.7.1",
    "jsdom": "3.1.2",
    "mocha": "^3.2.0",
    "react-addons-test-utils": "^15.4.2",
    "sinon": "^1.17.7",
    "webpack-dev-server": "^1.16.2"
  }```

@AllenFang
Copy link
Owner

maybe you can read JedWatson/react-select#606, please make sure it firstly, because the error said you have multiple copies of React loaded

@bpraggastis
Copy link
Author

bpraggastis commented Feb 15, 2017

@AllenFang
I am looking at JedWatson's piece. I think it is the root of that error. Another issue is that
when I force the update I am getting this error:

ncaught TypeError: this.props.cellEdit.__onCompleteEdit__ is not a function
    at TableBody.__handleCompleteEditCell__REACT_HOT_LOADER__ 

So when I save the custom editor won't go away. Perhaps fixing the multiple versions of React will help that as well?

@AllenFang
Copy link
Owner

AllenFang commented Feb 15, 2017

When you force a update?, BTW, I think you should remove the this.updateData() in the onChange function at ContactEditor,

And

updateData() {
        this.props.onUpdate( { contact: this.state.contact } );
    }

should be

updateData() {
        this.props.onUpdate(this.state.contact);
    }

@bpraggastis
Copy link
Author

@AllenFang thanks for your continued help on this.
I changed the updateData function as you indicated. But I still get the error

Uncaught TypeError: this.props.cellEdit.__onCompleteEdit__ is not a function
    at TableBody.__handleCompleteEditCell__REACT_HOT_LOADER__

I am working on pointing to a single copy of React. Maybe this will solve the problem?

@bpraggastis
Copy link
Author

bpraggastis commented Feb 15, 2017

I am pointing to a single copy of React as in JedWatson/react-select#606, but I am still getting the error:

Uncaught Error: removeComponentAsRefFrom(...): Only a ReactOwner can have refs. You might be removing a ref to a component that was not created inside a component's `render` method, or you have multiple copies of React loaded (details:

I believe the problem is that I am using Rails as per stackoverflow

@AllenFang
Copy link
Owner

AllenFang commented Feb 17, 2017

maybe, I'm not very familiar with Rails, but if this is a certainly issue for this repo, I believe it have already been opened, so just make sure that only one React in your application.

Let me know if you really concern this issue is cause by this repo, thanks your hard work

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants