Ckeditor and reactjs, this.getselection is null - javascript

I'm using CKEditor with my project and im trying to prefill the textarea with data im getting from the server, that way it is easier for a user to edit. I do so in this component:
import React from 'react';
import {Link,State, Route} from 'react-router';
import Router from 'react-router';
import ReportsActions from 'actions/ReportsActions';
import ReportsStore from 'stores/ReportsStore';
import $ from 'jquery';
export default class EditReport extends React.Component {
constructor(props) {
super(props);
this.state = ReportsStore.getState();
this.state.singleReport = [];
this.state.editReport = [];
this.state.link = window.location.href;
}
componentDidMount() {
let state = this.state.link;
state = state.split('/');
state = state[state.length-2];
ReportsActions.getSoloReport(state);
ReportsStore.listen(this._onChanges);
CKEDITOR.replace( 'ckedit', {
allowedContent : true,
pasteFromWordRemoveFontStyles : false,
pasteFromWordRemoveStyles : false
});
console.log(this.state.editReport);
}
componentWillUnmount() {
ReportsStore.unlisten(this._onChanges);
}
_onChanges = () => {
this.setState({
singleReport: ReportsStore.getState().singleReport,
duplicate: ReportsStore.getState().singleReport,
editReport: ReportsStore.getState().editReport
});
}
_onCreateReport = () => {
for ( var instance in CKEDITOR.instances )
CKEDITOR.instances[instance].updateElement();
let state = this.state.link;
state = state.split('/');
state = state[state.length-2];
const title = React.findDOMNode(this.refs.title).value;
const date = React.findDOMNode(this.refs.date).value;
const body = React.findDOMNode(this.refs.body).value;
const id = state;
ReportsActions.editReport({
title: title,
date: date,
body: body,
id: id
});
}
render() {
let singleReport = this.state.singleReport;
let editReport = this.state.editReport;
let ckbody = "fetching data..";
if(ckbody == undefined) {
ckbody = "Fetching data..";
}
else {
ckbody = editReport.body;
}
let state = this.state.link;
if(CKEDITOR.instances['ckedit'] == undefined) {
console.log('not defined');
}
else {
console.log(CKEDITOR.instances['ckedit'].getData());
CKEDITOR.instances['ckedit'].setData(ckbody);
}
state = state.split('/');
state = state[state.length-2];
return(
<div>
<main>
<h1>Hello im trying to edit</h1>
<fieldset className = "fieldSet2">
<input type = "text" placeholder = {editReport.title} ref = "title"/>
<input type = "text" placeholder = {editReport.date} ref = "date"/>
<textarea className = "ckeditor" id = "ckedit" ref = "body" name = "ckedit" defaultValue = {editReport.body}>{editReport.body}</textarea>
<Link to= "singlereports" params ={{id: state}}><button type="submit" rows = "5" cols = "5" className ="superButton" onClick={this._onCreateReport}>Edit Report</button></Link>
</fieldset>
<div className ="toMyEmployees2">
<div className="containers1">
<div className="spacer">
<Link to="AllEmployees">
<a className="wide blue">
<i className="fa fa-users"></i>
<h2>View All Employees</h2>
</a>
</Link>
</div>
</div>
<div className ="toMyEmployees3">
<div className="containers1">
<div className="spacer">
<Link to="dashboard">
<a className="wide redgay">
<i className="fa fa-tachometer"></i>
<h2>Dashboard</h2>
</a>
</Link>
</div>
</div>
</div>
</div>
</main>
</div>
);
}
}
I've set CKEditor and initialized it in ComponentDidMount, then in render I try to setHTML to one of my states. When I first visit the component the editor displays the data inside it that I wan't to edit, however if I hit the back button or any other link/route and return to the editor, it spits out 'cannot read property of getSelection of undefined', tracing it, it belongs to this.document.getWindow. This is a single page app as well. I don't know why it's doing this, but maybe I have to remove the instance as well?

Related

Show contents of array in render

I am using MERN stack and Redux. I have created an array in the state 'comments' which is updated via the clickHandler function with elements from the global state (accessed via props). When i try to show the contents of the array in the render i just get the length of it. How would i show the properties of the elements for example title.
import React, { Component } from "react";
import PropTypes from "prop-types";
import GoogleSearch from "./GoogleSearch";
import { connect } from "react-redux";
import { fetchSubjects } from "../../actions/subject";
import { fetchComments } from "../../actions/comment";
import store from "../../store";
class Subject extends Component {
// on loading the subjects and comments
// are fetched from the database
componentDidMount() {
this.props.fetchSubjects();
this.props.fetchComments();
}
constructor(props) {
super(props);
this.state = {
// set inital state for subjects description
// and summary to invisible
viewDesription: -1,
viewSummary: -1,
comments: [],
};
}
componentWillReceiveProps(nextProps) {
// new subject and comments are added to the top
if (nextProps.newPost) {
this.props.subjects.unshift(nextProps.newPost);
}
if (nextProps.newPost) {
this.props.comments.unshift(nextProps.newPost);
}
}
clickHandler = (id) => {
// when a subject title is clicked pass in its id
// and make the desciption visible
const { viewDescription } = this.state;
this.setState({ viewDescription: viewDescription === id ? -1 : id });
// clear the existing comments in state
this.setState({
comments: [],
});
// loop through the comment items in the global state
// and add any with the same subjects id passed in to the array
var i;
for (i = 0; i < this.props.comments.length; i++) {
if (this.props.comments[i].subject == id) {
console.log(this.props.comments[i]);
this.setState({
comments: this.state.comments.unshift(this.props.comments[i]),
});
}
} // set local storage to the id for the subject that has been clicked
localStorage.setItem("passedSubject", id);
};
// hovering on and off subjects toggles the visibility of the summary
hoverHandler = (id) => {
this.setState({ viewSummary: id });
};
hoverOffHandler = () => {
this.setState({ viewSummary: -1 });
};
render() {
const subjectItems = this.props.subjects.map((subject) => {
// if the state equals the id set to visible if not set to invisible
var view = this.state.viewDescription === subject._id ? "" : "none";
var hover = this.state.viewSummary === subject._id ? "" : "none";
var comments = this.state.comments;
return (
<div key={subject._id}>
<div
className="subjectTitle"
onClick={() => this.clickHandler(subject._id)}
onMouseEnter={() => this.hoverHandler(subject._id)}
onMouseLeave={() => this.hoverOffHandler()}
>
<p className="title">{subject.title}</p>
<p className="rating">Rating: {subject.rating}</p>
<p className="summary" style={{ display: hover }}>
{subject.summary}
</p>
</div>
<div className="subjectBody " style={{ display: view }}>
<div className="subjectAuthor">
<p className="author">
Subject created by: {subject.author} on {subject.date}
</p>
<a href="">
<div className="buttonRateSubject">RATE SUBJECT</div>
</a>
</div>
<div className="subjectDescription">
<p className="description">{subject.description}</p>
</div>
<div className="subjectLinks">Links:</div>
<div className="subjectComments">
<p>Comments:</p>
{/* ************HERE*********** */}
<p>{comments}</p>
{/* ********************************* */}
<a href="/addcomment">
<div className="buttonAddComment">ADD COMMENT</div>
</a>
</div>
</div>
</div>
);
});
return (
<div id="Subject">
<GoogleSearch />
{subjectItems}
</div>
);
}
}
Subject.propTypes = {
fetchSubjects: PropTypes.func.isRequired,
fetchComments: PropTypes.func.isRequired,
subjects: PropTypes.array.isRequired,
comments: PropTypes.array.isRequired,
newPost: PropTypes.object,
};
const mapStateToProps = (state) => ({
subjects: state.subjects.items,
newSubject: state.subjects.item,
comments: state.comments.items,
newComment: state.comments.item,
});
// export default Subject;
export default connect(mapStateToProps, { fetchSubjects, fetchComments })(
Subject,
Comment
);
I think I know your problem. You want to render items of an array.
Let me just give you a short overview.
Javascript:
this.setState({
comments: data
});
render (){
return (
<div>
{ this.state.comments.map(c=> <div>{c.body}</div> ) }
</div>
)
}
Thanks guys, i changed the for loop in the clickHandler to this which now has data rendering, it didn't like objects in the array for some reason.
var temp = [];
for (i = 0; i < this.props.comments.length; i++) {
if (this.props.comments[i].subject == id) {
console.log(this.props.comments[i]);
temp.unshift(this.props.comments[i].comment);
temp.unshift(this.props.comments[i].title);
}
}
this.setState({
comments: temp,
});

React & Redux: "form submission canceled because the form is not connected"

I am still new in React and Redux. So, I know the existence of redux-form, but Im not intend to use in this project. So, what I am doing is creating a form without using redux-form. This form will grab the data from the reducers and pass it to backend API.
This is my main CreateListing.jsx page.
// #flow
import React from 'react';
import { connect } from 'react-redux';
import { Helmet } from 'react-helmet';
import { SEOService } from '[services]';
import CreateListingFormPage1 from './CreateListing/CreateListingFormPage1';
import CreateListingFormPage2 from './CreateListing/CreateListingFormPage2';
import CreateListingFormPage3 from './CreateListing/CreateListingFormPage3';
import WhereAmI from './CreateListing/WhereAmI';
import SuccessCreateListing from './CreateListing/SuccessCreateListing';
type Props = {
...props...
};
class CreateListing extends React.Component<Props> {
getPageBySequence(pagenum) {
// depending on whether User is logged in or not, show/hide the Login/Signup form which is Page3
let sequence = [ CreateListingFormPage1, CreateListingFormPage2, CreateListingFormPage3 ];
if (this.props.isLoggedIn) {
sequence = [ CreateListingFormPage1, CreateListingFormPage2, CreateListingFormPage2 ];
}
return sequence[pagenum-1];
}
getSubmitCreateListing = (e) => {
e.preventDefault();
const propertyType = this.props.listingType;
const propertyName = this.props.suggestedBuildings.selected;
const propertyBuildingType = this.props.propertyBuildingType;
const bedrooms = this.props.bed;
const bathrooms = this.props.bath;
const price = this.props.price;
const builtUp = this.props.builtUp;
const title = this.props.title;
const tenure = this.props.tenure;
const description = this.props.description;
/* IN CASE USER NOT YET LOGGGED IN */
if(this.props.isLoggedIn === false) {
const email = this.props.email;
const password = this.props.password;
this.props.cacheCreateListing({ email, password, propertyType, propertyName, propertyBuildingType, bedrooms, bathrooms, price, builtUp, title, tenure, description });
}
this.props.cacheCreateListing({ propertyType, propertyName, propertyBuildingType, bedrooms, bathrooms, price, builtUp, title, tenure, description });
if(CreateListingFormPage1.landedTypes.includes(propertyBuildingType)) {
this.props.geocodingRequired(true);
}
else {
this.props.geocodingRequired(false);
}
this.props.onSubmitCreateListing();
}
onAuthenticateAndCreateListingButton() {
if(this.props.isLoggedIn) {
return(
<div role="presentation">
<div className={`column ${this.props.formCurrentPage === 1 ? '':'displayNone'}`}>
<button type="button" className="Button button-next is-red" onClick={this.props.onNextClick}>
NEXT
</button>
</div>
<div className={`column ${this.props.formCurrentPage === 2 || this.props.formCurrentPage === 3 ? '':'displayNone'}`}>
<button type="submit" className="Button button-create is-red" onClick={this.props.onLoadingCreateListing}>
CREATE LISTING
</button>
</div>
</div>
)
}
return <div className={`column ${this.props.formCurrentPage < 3 ? '':'displayNone'}`}>
<button type="button" className="Button button-next is-red" onClick={this.props.onNextClick}>
NEXT
</button>
</div>
}
render() {
if(this.props.isListingCreated){
return <SuccessCreateListing />;
}
else if(this.props.isListingLoading){
return <div className="create-listing-spinner" />
}
const CurrentPage = this.getPageBySequence(this.props.formCurrentPage);
return (
<div className={`CreateListing${this.props.isMobile ? '' : ' is-desktop'}`}>
<Helmet>
<title>{ SEOService.getMetaTitle('Create Property Listing') }</title>
{ SEOService.getCanonicalTag('/blogs') }
</Helmet>
<section className="CreateListing--Main">
<div className="CreateListing--Container">
<div className="CreateListing--WhereAmI">
<WhereAmI page={this.props.formCurrentPage} />
</div>
<div className="CreateListing--Body">
<form className="CreateListing--Form" onSubmit={ this.getSubmitCreateListing }>
<CurrentPage />
<div className='columns'>
<div className='column'/>
{/* CHANGE THIS this.props.formCurrentPage < 3 later */}
<div className={`column ${this.props.formCurrentPage > 1 && this.props.formCurrentPage < 4 ? '':'displayNone'}`}>
<button type="button" className="Button button-back" onClick={this.props.onPrevClick}>
BACK
</button>
</div>
{ this.onAuthenticateAndCreateListingButton() }
<div className='column'/>
</div>
</form>
</div>
</div>
</section>
</div>
);
}
};
const MapStateToProps = (state: State) => ({...});
const MapDispatchToProps = (dispatch: Dispatch) => ({
onLoadingCreateListing: () => dispatch({type: 'CREATE_LISTING_LOADING'}),
onSubmitCreateListing: () => dispatch({type: 'CREATE_LISTING_SUBMIT_FORM'}),})
export default connect(MapStateToProps,MapDispatchToProps)(CreateListing);
So, my <input type="text" /> are all from CreateListingFormPage1, CreateListingFormPage2 and CreateListingFormPage3 and put together in <CurrentPage />. My <form onSubmit={...}></form> is in this CreateListing.jsx page. Im not whether it is allowed to do it like this.
So, when I click submit, I got warning of Form submission canceled because the form is not connected.
My example of <input type="" /> in CreateListingFormPage1 are:
// #flow
import React from 'react';
import { connect } from 'react-redux';
import {Dropdown} from '[elements]';
type Props = {...props...};
class CreateListingFormPage2 extends React.Component<Props> {
static get selectTenure() { return ["Select Tenure"].concat(this.tenureTypes) };
static get selectTitle() { return ["Select Title"].concat(this.titleTypes) };
static get selectBedroom() { return["Select Bedrooms no"].concat(this.bedroomNo) };
static get selectBathroom() { return["Select Bathrooms no"].concat(this.bathroomNo) };
static get tenureTypes(){
return[
"FREEHOLD",
"LEASEHOLD",
"OTHERS"
]};
static get titleTypes(){
return[
"RESIDENTIAL",
"COMMERCIAL",
"INDUSTRIAL"
]};
static get bedroomNo(){
return[
"1",
"2",
"3",
"4",
"5"
]};
static get bathroomNo(){
return[
"1",
"2",
"3",
"4",
"5"
]};
get selectTenure() { return this.constructor.selectTenure; }
get selectTitle() { return this.constructor.selectTitle; }
get selectBedroom() { return this.constructor.selectBedroom; }
get selectBathroom() { return this.constructor.selectBathroom; }
get tenureTypes() { return this.constructor.tenureTypes; }
get titleTypes() { return this.constructor.titleTypes; }
get bedroomNo() { return this.constructor.bedroomNo; }
get bathroomNo() { return this.constructor.bathroomNo; }
hasInputError = (name) => {
if (this.props.errors[name]) {
return ' is-error';
}
return '';
}
render() {
return (
<div className={`Listing--Create${ this.props.isMobile ? '' : ' is-desktop' }`} id='form-second-page'>
{/* <form className="Listing--form"> */}
<div className="Listing--bedrooms-bathrooms">
<div className="type-title">No. of Bedrooms</div>
<Dropdown namespace="bedroom" selected={ this.selectBedroom[0] } options={ this.selectBedroom } onOptionSelect={ this.onBedroomDropdownSelect }/>
<div className="type-title">Asking Price</div>
<input className={`text-input price-input${ this.hasInputError('price')}`} type="text" onChange={ (e) => this.props.onPrice(e.currentTarget.value) } value={this.props.price} placeholder="RM"/>
</div>
<div className="Listing--price-built-up">
<div className="type-title">No. of Bathrooms</div>
<Dropdown namespace="bathroom" selected={ this.selectBathroom[0] } options={ this.selectBathroom } onOptionSelect={ this.onBathroomDropdownSelect }/>
<div className="type-title">Built-up Size</div>
<input className={`text-input built-up-input${ this.hasInputError('built_up_size')}`} type="text" onChange={ (e) => this.props.onBuiltUpSize(e.currentTarget.value) } value={this.props.builtUp} placeholder="sqft."/>
</div>
<div className="Listing--tenure">
<div className="type-tenure">Select Tenure</div>
<Dropdown namespace="tenure" selected={ this.selectTenure[0] } options={ this.selectTenure } onOptionSelect={ this.onTenureDropdownSelect }/>
</div>
<div className="Listing--title">
<div className="type-title">Select Title</div>
<Dropdown namespace="title" selected={ this.selectTitle[0] } options={ this.selectTitle } onOptionSelect={ this.onTitleDropdownSelect }/>
</div>
<div className="Listing--photos">
<div className="type-title">Upload Photos</div>
<button className={`text-input photos-input${ this.hasInputError('photos')}`}>Click to upload</button>
</div>
<div className="Listing--description">
<div className="type-title">Describe your property</div>
<textarea className={`text-input description-input${ this.hasInputError('description')}`} onChange={ (e) => this.props.onDescription(e.currentTarget.value) } value={this.props.description} placeholder="Describe your property"/>
</div>
</div>
);
}
};
const MapStateToProps = (state: State) => ({
...
});
const MapDispatchToProps = (dispatch: Dispatch) => ({
...
})
export default connect(MapStateToProps, MapDispatchToProps)(CreateListingFormPage2);
Basically, there is nothing wrong with my redux store. All the value of the input is captured successfully. The problem is when submitting the form, either the onSubmit or my form structure method is incorrect.
This is CreateListing.js reducer should it be helpful:
const INITIAL_STATE= {
isListingLoading: false,
isListingCreated: false,
}
const CreateListing = (state = INITIAL_STATE, action) => {
switch(action.type){
case 'CREATE_LISTING_LOADING':
return Object.assign({}, state, {isListingLoading: true});
case 'CREATE_LISTING_SUBMIT_FORM':
return Object.assign({}, state, {isListingCreated: true});
default:
return state;
} }
export default CreateListing;
Any help is much appreciated.
if you have any other buttons in your form you should add type="button".
so make changes like this.
<button type="button" className="Button button-create is-red" onClick={this.props.onLoadingCreateListing}>
CREATE LISTING
</button>

onClick function not working for anchor tag

i am new to react.
my onClick function in the anchor tag is not calling the hidePopover function.
i even gave debugger inside hidePopover function and its not stopping there, when I click the a tag
can you guys tell me why its not calling.
providng my code below
yeah i checked in console, its returning undefined, can you tell me how to fix it this.onclick
null
this.hidePopover
undefined
this.onClick
undefined
this.props
undefined
this.Function
function Function() { [native code] }
import React, {Component} from 'react';
import {connect} from 'react-redux';
import SportsBody from '../../components/scores/tracker/score-tracker-ira';
import Player from '../../components/player/snapshot/player-snapshot-presenter-ira';
import {setSidebarAppMode} from 'sports-template-standard/lib/redux/layout/layout-actions';
import {updateMenu} from '../../redux/menu/menu-actions';
import {Overlay} from 'sports-modal';
import SportsDefect from 'sports-logger';
import {version} from '../../../package.json';
const sportsDefect = new SportsDefect();
class KickIra extends Component {
constructor(props) {
super(props);
this.state = {popoverIsActive: true};
this.launchGoalDetails = this.launchGoalDetails.bind(this);
this.hidePopover = this.hidePopover.bind(this);
this.sportsFollow = this.sportsFollow.bind(this);
}
componentDidMount() {
sportsDefect.log('IRA Rollover Kick View Loaded Ver:' + version);
}
launchGoalDetails() {
this.props.dispatch(setSidebarAppMode(true));
this.props.dispatch(updateMenu('score-details'));
window.scrollTo(0, 0);
}
**hidePopover() {
console.log("insidePopup")
debugger;
this.setState({popoverIsActive: false});
}**
sportsFollow() {
const urlToLaunch = `/inet/iraRollover/IraRollOver/InvRolloverLandingPage?fundId=${this.props.playerInfo.fundingDetailId}`;
window.open(urlToLaunch, '_self');
}
getHeaderContent() {
const {profile} = this.props;
return (
<span>
<div className="title">WELCOME{profile && profile.firstName && `, ${profile.firstName}`}!</div>
<div className="subTitle">Digital Investment Adviser</div>
</span>
);
}
static basketballContent() {
return (
<p>
If you want to know more about the status of your IRA Rollover, use the link below
to visit the IRA Rollover Tracker on our website.
</p>
);
}
/*static basketballContentNoChange() {
console.log("inside basketballContentNoChange---->");
return (
<div></div>
);
}*/
static popupFooter() {
//debugger;
return (
<div>
<a className="closeButton" href="javascript:;" onClick={this.hidePopover}>Close</a>
<a className="iraRollover sports-modal-trigger" href="javascript:;" onClick={this.sportsFollow}>Check Out IRA Rollover Tracker</a>
</div>
);
}
/*static popupFooterClose() {
return (
<a className="closeButton" href="javascript:;" onClick={this.hidePopover}>Close</a>
);
}*/
render() {
//console.log("summary-ira this.props.playerInfo.fundingDetailId ---->" + this.props.playerInfo.fundingDetailId);
//debugger;
/*if(this.props.playerInfo.fundingDetailId === undefined || this.props.playerInfo.fundingDetailId === '' ) {
}*/
return (
<span>
<section className="gray-box snapshotContainer">
<div className="flex-container flex-2">
<div className="snapshot flex-item">
<Overlay
className="popover--IRA"
active={this.state.popoverIsActive}
headerTitle={this.getHeaderContent()}
enableCloseShortcuts={true}
maxWidth={800}
onClose={this.hidePopover}
>
<div className="dia-popover-content level1">
<p>
As you requested, we are in the process of rolling over the balance from your qualified plan to your new IRA.
</p>
</div>
<div className="dia-popover-content level2 dia-text-center">
<p>
Feel free to take a look around your new dashboard; this is where we'll show you what's
happening with your new investment player.
</p>
<p>
There isn't much to display yet, so don't let that concern you.
</p>
{/*(this.props.playerInfo.functionDetailId !== null ||
this.props.playerInfo.fundingDetailId !== '' ||
this.props.playerInfo.fundingDetailId !== undefined ) &&
KickIra.basketballContent()*/}
{
this.props.playerInfo.fundingDetailId === null || this.props.playerInfo.fundingDetailId === '' || this.props.playerInfo.fundingDetailId === undefined ?
null :
KickIra.basketballContent()
}
</div>
<div className="dia-popover-content level3">
{
this.props.playerInfo.fundingDetailId === null || this.props.playerInfo.fundingDetailId === '' || this.props.playerInfo.fundingDetailId === undefined ?
KickIra.popupFooterClose() :
KickIra.popupFooter()
}
</div>
</Overlay>
<SportsBody />
</div>
<div className="snapshot flex-item">
<Player />
</div>
</div>
</section>
</span>
);
}
}
KickIra.propTypes = {
playerInfo: React.PropTypes.object,
scoreDetails: React.PropTypes.object,
profile: React.PropTypes.object
};
export default connect(state => ({
scoreDetails: state.scoreDetails,
playerInfo: state.player,
profile: state.template.profile
}))(KickIra);
Basically I am not giving solution on React, but it is same as any functional constructor thingy.
May be use the below, in order to have a instance of you class available to you in every function/ module.
class KickIra extends Component {
constructor(props) {
super(props);
this.state = {popoverIsActive: true};
this.self = this;
this.launchGoalDetails = this.launchGoalDetails.bind(this);
this.hidePopover = this.hidePopover.bind(this);
this.sportsFollow = this.sportsFollow.bind(this);
}
......
......
}
Then, use it to call function:
onClick={self.hidePopover}
In react onClick elements should be used in something like
onClick={()=>{
}}
using the fat arrow notation does the binding thing for you and you can call the method now as this.function()

React changing props on grandchildren: object is not extensible / tree traversing react children

I am following a tutorial and cannot get the following code to run.
When I run the following code I get the error Can't add property attachToForm, object is not extensible. Are you no longer to allowed to change child props in this way (ie. with child.props.key = value)? If not can you see a better way of adding a function to nested children only if the element is an input?
React.Children.forEach(children, function (child) {
if (child.props.name) {
child.props.attachToForm = this.attachToForm;
child.props.detachFromForm = this.detachFromForm;
}
if (child.props.children) {
this.registerInputs(child.props.children);
}
}.bind(this));
I am using es6 if that changes anything but the tutorial can be found here: http://christianalfoni.github.io/javascript/2014/10/22/nailing-that-validation-with-reactjs.html
FormComponent:
'use strict';
import React from 'react';
import BaseComponent from '#client/base-component';
export default class Form extends BaseComponent {
constructor(props) {
super(props);
this.state = {};
}
render() {
var classString = this.props.className ? this.props.className : '';
var classArray = ['_common-form', this.props.type ? 'form--' + this.props.type : 'form--basic' ];
for(var i = 0; i < classArray.length; i++){
if(classArray[i]){
classString = classString + ' ' +classArray[i];
}
}
var props = {
type: this.props.type,
className: classString,
style: this.props.style,
onSubmit: this.props.onSubmit
};
return React.createElement(
'form',
props,
this.newChildren
);
}
componentWillMount() {
this.inputs = {};
this.model = {};
this.newChildren = [];
this.registerInputs(this.props.children);
}
registerInputs(children) {
React.Children.forEach(children, function (child) {
if (child.props.name) {
child.props.attachToForm = this.attachToForm;
child.props.detachFromForm = this.detachFromForm;
}
if (child.props.children) {
this.registerInputs(child.props.children);
}
}.bind(this));
}
attachToForm(component) {
this.inputs[component.props.name] = component;
this.model[component.props.name] = component.state.value;
}
detachFromForm(component) {
delete this.inputs[component.props.name];
delete this.model[component.props.name];
}
}
ModalComponent:
'use strict';
import React from 'react';
import Modal from '#client/common/modal';
import Button from '#client/common/buttons';
import AddParticipant from './add_participant';
import Form from '#client/common/form_elements';
import Input from '#client/common/form_elements/input.js';
import SlideToggle from '#client/common/form_elements/slide_toggle.js';
import FormField from '#client/common/form_elements/form_field.js';
import FormSection from '#client/common/form_elements/section.js';
import BaseComponent from '#client/base-component';
export default class EditTeam extends BaseComponent {
constructor(props) {
super(props);
this.state = {
values: {
name: props.team.name,
mission: props.team.mission,
globalSearch: props.team.globalSearch,
public: props.team.public,
witcryptSecured: props.team.witcryptSecured
},
addParticipantModal: false
};
}
render() {
var participantsList = [];
this.props.team.participants.forEach(function(participant) {
participantsList.push(
<div className="participant" key={participant.key}>
<span className="participant-avatar" style={{backgroundImage:`url("${participant.avatar}")`}}></span>
<span>{participant.name}</span>
<span className={`${participant.roll}-action roll`}>{participant.roll}<a></a></span>
</div>
);
}.bind(this));
return (
<Modal className="_common-edit-team-settings" title={`Edit ${this.props.team.name}`} isOpen={this.props.modalIsOpen && this.props.editTeamModal} onCancel={this.props.toggleEditTeamModal} backdropClosesModal>
<Form onSubmit={this.saveChanges}>
<FormSection className="edit-team-details" sectionHeader="Team Details">
<FormField label="Name">
<Input name="name" value={this.state.values.name} onChange={this.handleInputChange} type="text" placeholder={this.props.team.name}/>
</FormField>
<FormField label="Mission">
<Input name="mission" value={this.state.values.mission} onChange={this.handleInputChange} type="text" placeholder={this.props.team.kitMission || 'Kit Mission'} multiline />
</FormField>
</FormSection>
<FormSection className="privacy-settings" sectionHeader="Privacy Settings">
<FormField label="Included in global search results" >
<SlideToggle name="globalSearch" defaultChecked={this.state.values.globalSearch} onChange={this.handleCheckedChange} type="checkbox" />
</FormField>
<FormField label="Accessible by anyone" >
<SlideToggle name="public" defaultChecked={this.state.values.public} onChange={this.handleCheckedChange} type="checkbox" />
</FormField>
<FormField label="Secured with WitCrypt" >
<SlideToggle name="witcryptSecured" defaultChecked={this.state.values.witcryptSecured} onChange={this.handleCheckedChange} type="checkbox" />
</FormField>
</FormSection>
<FormSection sectionHeader="Participants">
{participantsList}
<div id="add-participant" className="participant" onClick={this.toggleAddParticipantModal}>
<span className="participant-avatar" style={{backgroundImage:'url(/img/blue_add.svg)'}}></span>
<span>Add a Participant</span>
<span className="add-action roll"><a></a></span>
</div>
</FormSection>
<Button type="hollow-primary" size="md" className="single-modal-btn" block submit>Save</Button>
</Form>
<AddParticipant people={this.props.people} toggleAddParticipantModal={this.props.toggleAddParticipantModal} modalIsOpen={this.props.modalIsOpen} toggleAddParticipantModal={this.toggleAddParticipantModal} addParticipantModal={this.state.addParticipantModal} />
</Modal>
);
}
toggleAddParticipantModal() {
this.setState({
addParticipantModal: !this.state.addParticipantModal
});
}
handleCheckedChange(event){
var newState = this.state;
newState.values[event.target.name] = !newState.values[event.target.name];
this.setState(
newState
);
}
handleInputChange(event){
var newState = this.state;
newState.values[event.target.name] = event.target.value;
this.setState(
newState
);
}
saveChanges(e){
e.preventDefault();
}
}
Are you using React v.14 or above? the props object is now frozen and cant be changed. You can use React.cloneElement instead

Dynamically load initialValues in Redux Form

Using the example of initializingFromState within Redux-Form, I am trying to set this up dynamically. This is to edit a particular book in a list of books, and is using a simple api set up in express.js.
The full container is below. I somehow need to pass in initialValues, within the mapStateToProps function. In the example, it is done via a static object, but I can't work out how to use the information I have pulled in via fetchBook, and pass it to initialValues.
Container:
import React, { Component, PropTypes } from 'react';
import { reduxForm } from 'redux-form';
import { connect } from 'react-redux';
import { Link } from 'react-router';
import { fetchBook, editBook } from '../actions/index';
class BookEdit extends Component {
componentWillMount() {
this.props.fetchBook(this.props.params.id);
}
static contextTypes = {
router: PropTypes.object
}
onSubmit(props) {
this.props.editBook(this.props.book.id, props)
.then(() => {
this.context.router.push('/');
});
}
const data = {
title: {this.props.book.title},
description: {this.props.author}
}
render() {
const { fields: { title, author }, handleSubmit } = this.props;
const { book } = this.props;
if (!book) {
return (
<div>
<p>Loading...</p>
</div>
)
}
return (
<div>
<Link to="/">Back</Link>
<form onSubmit={handleSubmit(this.onSubmit.bind(this))}>
<h2>Add a new book</h2>
<label>Title</label>
<input type="text" {...title} />
<div className="text-help">{title.touched ? title.error : ''}</div>
<label>Author</label>
<input type="text" {...author} />
<div className="text-help">{author.touched ? author.error : ''}</div>
<button type="submit">Add</button>
<Link to="/" className="button">Go back</Link>
</form>
</div>
);
}
}
function mapStateToProps(state) {
return {
book: state.books.book,
initialValues: // how do I pass in the books here?
};
}
export default reduxForm({
form: 'EditBookForm',
fields: ['title', 'author']
}, mapStateToProps, { fetchBook, editBook })(BookEdit);
Thank you.
Your form values aren't what's in state.books.book? I think this is all you're looking for:
function mapStateToProps(state) {
return {
book: state.books.book,
initialValues: state.books.book
};
}
Since you're only really looking at this.props.book to know if it's loaded or not, it might be more explicit to do something like:
function mapStateToProps(state) {
return {
loaded: !!state.books.book,
initialValues: state.books.book
};
}
Hope that helps.
In related to above question, Erik. I have following form and not sure why it is not Validating on submit. It loads the data into fields but when I hit submit the validation fails.
Form_Bayan.js
import React, {Component, PropTypes} from "react";
import {browserHistory} from "react-router";
import {reduxForm, Field} from "redux-form";
import {MyCustomInput, MySimpleInput, MyCustomSelect} from "./__form_field_components";
import {connect} from "react-redux";
import {bindActionCreators} from "redux";
import {
ADMIN_FETCH_AUTOSUGGESTS_Lbl,
adminFetchAutoSuggestCats_act,
ADMIN_GENERATESLUG_Lbl,
adminGenerateSlug_act,
ADMIN_GETCATID_BYNAME_Lbl,
adminGetCatIdByName_act,
ADMIN_ADDNEWBAYAAN_Lbl,
adminAddNewBayaan_act,
adminFetchArticlesByCat_act,
adminUpdateBayaan_act
} from "../../actions/adminActionCreators";
import _ from "lodash";
class NewBayanForm extends Component {
constructor(props) {
super(props); // this component inherits "toggleViewFunction" function through props for redirection
this.generateSlug = this.generateSlug.bind(this);
this.state = {
submitButtonMeta: {
btnTitle: "Save",
btnClass: "btn btn-default",
btnIcon: null,
disabled: false
},
globalMessage: { // set when an action is performed by ActionCreation+Reducer and a message is returned
message: "",
className: ""
},
tempData: {
//the_bayaansMainCat_id : 1, // '1' refers to the 'Bayaans' parent category in admin , this ID is used here for different sort of lookups i.e. fetch available subcats for autosuggest, fetch cat ID by name under parent catID
the_bayaansMainCat_id: this.props.associatedMainCatId, // being passed from parent component to avoide redundent declaration
the_autoSuggestCatList: [],
slug: "",
the_catId: null
}
};
}
resetMessageState() {
var noMsg = {message: "", className: ""};
this.setState({globalMessage: noMsg});
}
componentDidMount() {
console.log("<NewBayanForm> (componentDidMount)");
this.props.adminFetchAutoSuggestCats_act(this.state.tempData.the_bayaansMainCat_id);
}
doSubmit(props) {
//console.log("----- submitting form -----");
//console.log(props);
this.disableSubmitButton();
// prepare data for submit request
// item_title, item_slug, content, picture, attachment, media_path, reference, tag_keywords, author_name, cat_id, date_created
var newBayanObj = {
item_title: props.titleTxt,
item_slug: this.state.tempData.slug,
content: props.videoIdTxt,
picture: "",
attachment: "",
media_path: "https://www.youtube.com/watch?v=" + props.videoIdTxt,
reference: "",
tag_keywords: props.keywordsTxt,
author_name: props.authorTxt,
cat_id: this.state.tempData.the_catId
};
this.props.adminUpdateBayaan_act(newBayaanObj)
.then(() => {
console.log("%c <NewBayanForm> (doSubmit) Updated bayaan, refetching updated bayaans list...", "color:blue;font-weight:bold;");
this.props.adminFetchArticlesByCat_act(this.props.associatedMainCatId)
.then(() => {
console.log("%c <NewBayanForm> (doSubmit) Redirecting to Gallery after update...", "color:blue;font-weight:bold;");
this.props.toggleViewFunction(); // comming from Parent Class (bayaansPage)
});
});
}
disableSubmitButton() {
console.log("<NewBayanForm> (disableSubmitButton)");
// Ref: http://stackoverflow.com/questions/18933985/this-setstate-isnt-merging-states-as-i-would-expect
var newButtonState = {
btnTitle: "Please wait... ",
btnClass: "btn btn-disabled",
btnIcon: null,
disabled: true
};
this.setState({submitButtonMeta: newButtonState});
this.resetMessageState(); // Need to reset message state when retrying for form submit after 1st failure
}
enableSubmitButton() {
console.log("<NewBayanForm> (enableSubmitButton)");
// Ref: http://stackoverflow.com/questions/18933985/this-setstate-isnt-merging-states-as-i-would-expect
var newButtonState = {btnTitle: "Save", btnClass: "btn btn-default", btnIcon: null, disabled: false};
this.setState({submitButtonMeta: newButtonState});
}
fetchCategoryId(value) {
console.log('<NewBayanForm> (fetchCategoryId) input-Value:', value); // make API call to fetch / generate category ID for this post
this.props.adminGetCatIdByName_act(value, this.state.tempData.the_bayaansMainCat_id); // '1': refers to look up under 'Bayaans' parent category for the specified category name
}
// will always receive and triggers when there are 'new props' and not old/same props
componentWillReceiveProps(nextProps) { // required when props are passed/changed from parent source. And we want to do some operation as props are changed (Ref: http://stackoverflow.com/questions/32414308/updating-state-on-props-change-in-react-form)
console.log("<NewBayanForm> (componentWillReceiveProps) nextProps: ", nextProps); // OK
//console.log("this.props : ", this.props); // OK
//console.log("nextProps.siteEssentials.actionsResult : ", nextProps.siteEssentials.actionsResult); // OK
if (nextProps.hasOwnProperty("siteEssentials")) { // if action status appeared as Done!
if (nextProps.siteEssentials.hasOwnProperty("actionsResult")) { // if action status appeared as Done!
if (nextProps.siteEssentials.actionsResult[ADMIN_GETCATID_BYNAME_Lbl] !== "FAILED") {
var clonedState = this.state.tempData;
clonedState.the_catId = nextProps.siteEssentials.actionsResult[ADMIN_GETCATID_BYNAME_Lbl];
//var newTempState = {slug: this.state.tempData.slug, the_catId: nextProps.siteEssentials.actionsResult[ADMIN_GETCATID_BYNAME_Lbl] };
this.setState({tempData: clonedState});
}
if (nextProps.siteEssentials.actionsResult[ADMIN_FETCH_AUTOSUGGESTS_Lbl] !== "FAILED") {
var clonedState = this.state.tempData;
clonedState.the_autoSuggestCatList = nextProps.siteEssentials.actionsResult[ADMIN_FETCH_AUTOSUGGESTS_Lbl];
this.setState({tempData: clonedState});
}
console.log("<NewBayanForm> (componentWillReceiveProps) new-State:", this.state);
}
}
}
render() { // rendering Edit form
const {handleSubmit} = this.props;
console.log('<NewBayanForm> (render_editForm) this.props:', this.props);
return (
<div className="adminForm">
<form onSubmit={handleSubmit(this.doSubmit.bind(this))}>
<div className="col-sm-6">
<div className="row">
<div className="col-sm-5"><label>Title:</label></div>
<div className="col-sm-7"><Field name="titleTxt" component={MySimpleInput}
defaultValue={this.props.name} type="text"
placeholder="Enter Title"/></div>
</div>
<div className="row">
<div className="col-sm-5"><label>Slug:</label></div>
<div className="col-sm-7">{this.state.tempData.slug || this.props.slug} <input
type="hidden" name="slugTxt" value={this.state.tempData.slug}/></div>
</div>
<div className="row">
<div className="col-sm-5"><label>Select Category:</label></div>
<div className="col-sm-7"><Field name="catTxt" component={MyCustomSelect}
defaultValue={this.props.category_name} type="text"
placeholder="Select or Type a New"
selectableOptionsList={this.state.tempData.the_autoSuggestCatList}
onSelectionDone={ this.fetchCategoryId.bind(this) }/>
<input type="hidden" name="catIdTxt"
value={this.state.tempData.the_catId || this.props.category_id}/>
</div>
</div>
</div>
<div className="col-sm-6">
<div className="row">
<div className="col-sm-5"><label>Youtube Video ID:</label></div>
<div className="col-sm-7"><Field name="videoIdTxt" component={MySimpleInput}
defaultValue={this.props.content} type="text"
placeholder="TsQs9aDKwrw"/></div>
<div className="col-sm-12 hint"><b>Hint: </b> https://www.youtube.com/watch?v=<span
className="highlight">TsQs9aDKwrw</span></div>
</div>
<div className="row">
<div className="col-sm-5"><label>Author/Speaker:</label></div>
<div className="col-sm-7"><Field name="authorTxt" component={MySimpleInput}
defaultValue={this.props.author} type="text"/></div>
</div>
<div className="row">
<div className="col-sm-5"><label>Tags/Keywords:</label></div>
<div className="col-sm-7"><Field name="keywordsTxt" component={MySimpleInput}
defaultValue={this.props.tag_keywords} type="text"/>
</div>
</div>
</div>
<div className="row">
<div className={this.state.globalMessage.className}>{this.state.globalMessage.message}</div>
</div>
<div className="buttonControls">
<a className="cancelBtn" onClick={this.props.toggleViewFunction}>Cancel</a>
<button className={this.state.submitButtonMeta.btnClass}
disabled={this.state.submitButtonMeta.disabled}>
{this.state.submitButtonMeta.btnTitle}</button>
</div>
</form>
</div>
);
}
}
function validate(values) { // Validate function being called on Blur
const errors = {};
if (!values.titleTxt)
errors.titleTxt = "Enter Title";
if (!values.catTxt)
errors.catTxt = "Select/Enter a Category";
if (!values.videoIdTxt)
errors.videoIdTxt = "Enter youtube video ID (follow the provided hint)";
if (!values.keywordsTxt)
errors.keywordsTxt = "Enter keywords (will help in search)";
return errors;
}
// ReduxForm decorator
const newBayanFormAdmin_reduxformObj = reduxForm({
form: "newBayanFormAdmin", // any unique name of our form
validate // totally equivelent to--> validate: validate
});
function mapStateToProps({siteEssentials}, ownProps) {
console.log("<NewBayanForm> (mapStateToProps) siteEssentials:", siteEssentials);
// 1st param is related to our Redux State, 2nd param relates to our own component props
var initialValues = {
titleTxt: ownProps.name,
slugTxt: ownProps.slug,
catTxt: ownProps.category_name,
catIdTxt: ownProps.category_id,
videoIdTxt: ownProps.content,
authorTxt: ownProps.author,
keywordsTxt: ownProps.tag_keywords
};
console.log("<NewBayanForm> (mapStateToProps) initialValues: ", initialValues);
return ({siteEssentials}, initialValues);
};
function mapDispatchToProps(dispatch) {
return bindActionCreators({
adminFetchAutoSuggestCats_act,
adminGenerateSlug_act,
adminGetCatIdByName_act,
adminAddNewBayaan_act,
adminFetchArticlesByCat_act
}, dispatch);
};
NewBayanForm = connect(mapStateToProps, mapDispatchToProps) (newBayanFormAdmin_reduxformObj(NewBayanForm));
export default NewBayanForm;

Categories