I'm attempting to add CRUD functionality for a Rails resource through some ReactJS components. I am using webpacker.
At this stage, I get one error in the console:
Uncaught Error: Target container is not a DOM element.
My set up is:
React files are in app/javascript/Botanicals. I then have a app/javascript/packs where webpacker is compiling from into app/public/packs/manifest.json
My pack file is rendering the components:
app/javascript/packs/botanicalsCRUD.jsx
import React from 'react'
import ReactDOM from 'react-dom'
import Botanicals from 'Botanicals'
document.addEventListener('turbolinks:load', function() {
var element = document.getElementById("botanicals");
ReactDOM.render(<Botanicals />, element);
});
I then individual files:
app/javascript/Botanicals/index.jsx
var Botanicals = React.createClass({
render() {
return (
<div>
<Body />
</div>
);
}
});
export default Botanicals
app/javascript/Botanicals/body.js.jsx
var Body = React.createClass({
getInitialState(){
return { botanicals: [] };
},
componentDidMount(){
$.getJSON('/botanicals.json', (response) => {
this.setState({ botanicals: response });
});
},
handleSubmit(item) {
var newState = this.state.botanicals.concat(botanical);
this.setState({ botanicals: newState })
},
handleDelete(id) {
$.ajax({
url: `/botanicals/%{id}`,
type: 'DELETE',
success: () => {
this.removeItemClient(id);
}
});
},
removeItemClient(id) {
var newBotanicals = this.state.botanicals.filter((botanical) => {
return botanical.id != id;
});
this.setState({ botanicals: newBotanicals });
},
render() {
return (
<div>
<NewItem handleSubmit={this.handleSubmit} />
<AllItems botanicals={this.state.botanicals} />
</div>
);
}
});
export default Body
app/javascript/Botanicals/newitem.js.jsx
var NewItem = React.createClass({
handleClick() {
var name = this.refs.name.value;
var description = this.refs.description.value;
$.ajax({
url: "/botanicals",
type: "POST",
data: { botanical: { name: name, description: description } },
success: botanical => {
this.props.handleSubmit(botanical);
}
});
},
render() {
return (
<div>
<input ref='name' placeholder='Enter the name of the item' />
<input ref='description' placeholder='Enter a description' />
<button onClick={this.handleClick}>Submit</button>
</div>
);
}
});
export default NewItem
app/javascript/Botanicals/allitems.js.jsx
var AllItems = React.createClass ({
handleDelete(id) {
this.props.handleDelete(id);
},
render() {
var botanicals = this.props.botanicals.map((botanical) => {
return (
<div key={botanical.id}>
<h3>{botanical.name}</h3>
<p>{botanical.description}</p>
<button onClick={this.handleDelete.bind(this, botanical.id)}>Delete</button>
</div>
);
});
return <div>{botanicals}</div>;
}
});
export default AllItems
views/botanicals/index.html.erb
<% javascript_pack_tag 'botanicalsCRUD' %>
<h1>Botanicals</h1>
<div id="botanicals"></div>
BotanicalsCRUD is in the manifest.json file:
"botanicalsCRUD.js": "/packs/botanicalsCRUD-b8ea47dea15aa535c997.js",
"botanicalsCRUD.js.map": "/packs/botanicalsCRUD-b8ea47dea15aa535c997.js.map",
My main suspicion is that render file in Packs isn't correctly finding botanicals/index.jsx.
I'm struggeling with the fact there isn't more error messages.
you should put <% javascript_pack_tag %> inside a div with the same id as you wrote in getElementById of your first react file.
that's mean in views/botanicals/index.html.erb you should write
<div id="botanicals">
<% javascript_pack_tag 'botanicalsCRUD' %>
</div>
hope it will work for you
Related
I'm working in a personal project and I was trying to implement a string interpolation, but for some reason it isn't working and it is throwing an error.
I'm using visual studio code and the errors it's throwing are the following:
Property assignement expected
';' expected (2 times)
Declaration or statement expected
this is the line where I put the specific interpolation:
newsection = {`${newsectionnmb}`:"placeholder"}
this is my complete code:
import React, { Component } from "react";
import { NavLink, match } from "react-router-dom";
import { withRouter } from "react-router-dom";
import axios from "axios";
class AddContent extends Component{
constructor(props) {
super(props)
this.state = {
name: "",
content: "",
courseName:"",
actualContent: [],
section:"NEW",
video: ""
}
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
axios.get(`http://localhost:5000/course/id/${Number(this.props.match.params.id)}`)
.then( (response)=> {
this.setState({
courseName: response.data.name,
actualContent: response.data.content
});
if(this.props._id != response.data.adminId) {
this.props.history.push("/");
}
})
.catch(function (error) {
console.log(error);
alert("we couldn't fetch the course data, try again later by reloading the page")
})
}
handleChange(event) {
const target = event.target;
const value = target.value;
const name = target.name;
this.setState({
[name]: value
});
}
handleSubmit= (event)=> {
if (this.state.name !== "" ) {
if (this.state.section === "NEW") {
newsectionnmb = this.state.actualContent.length +1;
newsection = {`${newsectionnmb}`:"placeholder"}
}
event.preventDefault();
axios({
method: 'put',
url: `http://localhost:5000/course/add-content/${Number(this.props.match.params.id)}`,
data: {
content: this.state.content
}
})
}
}
render() {
let sectionlist = this.state.actualContent.length > 0
&& this.state.actualContent.map((item, i) => {
return (
<option key={i} value={item.name}>{item.name}</option>
)
}, this);
return (
<div className="courses">
{this.props.registered === true?
<div>
<form onSubmit={this.handleSubmit} className="Register">
<h1>Add guide</h1>
<h2>{this.state.courseName}</h2>
<p>quotation marks aren't allowed</p>
<div>
<input
name="name"
type="text"
onChange={this.handleChange}
placeholder="name"/>
</div>
<div>
<input
name="video"
type="text"
onChange={this.handleChange}
placeholder="URL for video"/>
</div>
<div>
<textarea
name="content"
type="text"
onChange={this.handleChange}
placeholder="content"/>
</div>
<label>select section:</label>
<select name="section" onChange={this.handleChange} className="select">
<option value="NEW">New Section</option>
{sectionlist}
</select>
<button type="submit" className="coursesub">Submit</button>
<NavLink exact to="/courses">Go back to course</NavLink>
</form>
</div>
:
<div>
<h1>Welcome to SSG courses app, to start using please login.</h1>
<NavLink exact to="/login">Login</NavLink>
</div>
}
</div>
)
}
}
export default withRouter(AddContent);
If you want to make newsection an object, it should be done like this
newsection = {newsectionnmb:"placeholder"}
If you are trying to make it a string, this should work for you
newsection = `${newsectionnmb}:"placeholder"`
I have this simple react app, where I fetch the Flickr public feed. So, I can scroll to the end of the page and new content is going to show. So I would like to scroll until there is nothing else new, and the app stops trying to load more content, because it has reached the last item of the list, which is not happening if I try to scroll (you can see that on the loading message). How can I fix this?
Check the code below:
import React, { Component } from "react";
import ReactDOM from "react-dom";
import $ from "jquery";
import PhotoListItem from "./photoListItem";
import "./photoApp.css";
export default class PhotoApp extends Component {
constructor(props) {
super(props);
this.state = {
photoList: [],
searchTerm: "cyanotype",
items: 8,
loadingState: false,
loadingMessage: ""
};
}
componentDidMount() {
this.getPhotoList();
this.onInfiniteScroll();
}
/* get data from Flickr public feed */
getPhotoList = () => {
const flickrApiPoint =
"https://api.flickr.com/services/feeds/photos_public.gne?jsoncallback=?&tags=" +
this.state.searchTerm;
try {
$.ajax({
url: flickrApiPoint,
dataType: "jsonp",
data: { format: "json" },
success: function(data) {
this.setState({ photoList: data.items });
}.bind(this)
});
} catch (err) {
console.log(err);
}
};
/* code for infinite scroll */
onInfiniteScroll = () => {
this.refs.iScroll.addEventListener("scroll", () => {
if (
this.refs.iScroll.scrollTop + this.refs.iScroll.clientHeight >=
this.refs.iScroll.scrollHeight - 20
) {
this.loadMoreItems();
}
});
};
/* code for infinite scroll */
loadMoreItems = () => {
if (this.state.loadingState) {
return;
}
this.setState({
loadingState: true,
loadingMessage: "Loading photos..."
});
setTimeout(() => {
this.setState(prevState => ({
items: prevState.items + 8,
loadingState: false,
loadingMessage: "No more photos to show."
}));
}, 1000);
};
render() {
console.log(this.state.photoList)
return (
<div className="appContainer" ref="iScroll">
<div className="appHeader">
<h1 className="headerTitle">
Welcome to Flickr Alternative Photography Feed!
</h1>
</div>
<div className="gridContainer">
{this.state.photoList
.slice(0, this.state.items)
.map((photo, index) => {
const author = photo.author.split(/"/)[1];
const authorLink = photo.description.split(/"/)[1];
const description = photo.description.split(/"/)[13];
return (
<PhotoListItem
key={index}
url={photo.media.m}
photoLink={photo.link}
title={photo.title}
author={author}
authorLink={authorLink}
description={description}
tags={photo.tags}
/>
);
})}
</div>
<React.Fragment>
{this.state.loadingState ? (
<p className="loading">{this.state.loadingMessage}</p>
) : (
<p className="loading">{this.state.loadingMessage}</p>
)}
</React.Fragment>
</div>
);
}
}
LIVE EXAMPLE HERE
Thank you!
You could check if the item that you've loaded exceeds your items in your ajax request
/* code for infinite scroll */
loadMoreItems = () => {
// hasMore = data.items.length (you may want to rename this more appropriately)
if (this.state.loadingState || (this.state.items > this.state.hasMore)) {
// Do not load if there's no more items
return;
}
...
Your onInfiniteScroll doesn't have any code right now that checks whether it should load more items, it just blindly does. So: at the end of getPhotoList you probably want to check whether that's the last page of results and if so, do a setState({ exhausted: true }) or something similar, so you can check that value in your onInfiniteScroll and not do anything if you see this.state.exhausted === true.
I saw some questions speaking about similar issues but somehow I still do not manage to solve my issue so here I am asking for your kind help. I am pretty new to React and would like to send a function from a Parent to a child and then use it from the Child but somehow when I want to use it it says
Uncaught TypeError: Cannot read property 'props' of undefined"
Edited Code after first answers were helping:
var Menu = React.createClass({
links : [
{key : 1, name : "help", click : this.props.changePageHelp}
],
render : function() {
var menuItem = this.links.map(function(link){
return (
<li key={link.key} className="menu-help menu-link" onClick={link.click}>{link.name}</li>
)
});
return (
<ul>
{menuItem}
</ul>
)
}
});
var Admin = React.createClass ({
_changePageHelp : function() {
console.log('help');
},
render : function () {
return (
<div>
<div id="menu-admin"><Menu changePageHelp={this._changePageHelp.bind(this)} /></div>
</div>
)
}
});
ReactDOM.render(<Admin />, document.getElementById('admin'));
Pass a value from Menu function and recieve it in the changePageHelp function and it works.
var Menu = React.createClass({
render : function() {
return (
<div>
{this.props.changePageHelp('Hello')}
</div>
)
}
});
var Admin = React.createClass ({
_changePageHelp : function(help) {
return help;
},
render : function () {
return (
<div>
<div id="menu-admin"><Menu changePageHelp={this._changePageHelp.bind(this)} /></div>
</div>
)
}
});
ReactDOM.render(<Admin />, document.getElementById('admin'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="admin"></div>
For performance reasons, you should avoid using bind or arrow functions in JSX props. This is because a copy of the event handling function is created for every instance generated by the map() function. This is explained here: https://github.com/yannickcr/eslint-plugin-react/blob/master/docs/rules/jsx-no-bind.md
To avoid this you can pull the repeated section into its own component. Here is a demo: http://codepen.io/PiotrBerebecki/pen/EgvjmZ The console.log() call in your parent component receives now the name of the link. You could use it for example in React Router.
var Admin = React.createClass ({
_changePageHelp : function(name) {
console.log(name);
},
render : function () {
return (
<div>
<div id="menu-admin">
<Menu changePageHelp={this._changePageHelp} />
</div>
</div>
);
}
});
var Menu = React.createClass({
getDefaultProps: function() {
return {
links: [
{key: 1, name: 'help'},
{key: 2, name: 'about'},
{key: 3, name: 'contact'}
]
};
},
render: function() {
var menuItem = this.props.links.map((link) => {
return (
<MenuItem key={link.key}
name={link.name}
changePageHelp={this.props.changePageHelp}
className="menu-help menu-link" />
);
});
return (
<ul>
{menuItem}
</ul>
);
}
});
var MenuItem = React.createClass ({
handleClick: function() {
this.props.changePageHelp(this.props.name);
},
render : function () {
return (
<li onClick={this.handleClick}>
Click me to console log in Admin component <b>{this.props.name}</b>
</li>
);
}
});
ReactDOM.render(<Admin />, document.getElementById('admin'));
I'm doing a react app and id I'd like to know how to think multipages websites. Actually i'im I'm doing a course searcher,i searcher; I use routie to render the different components that renders render the page. The problem is that they arent aren't related by hierarchy, so the ajax data isn't accessible to the component that renders the result.I've I've tried vainly to use a js var data but doesnt var data but that doesn't work too either.Ive read https://facebook.github.io/react/tips/communicate-between-components.html
but i don't see what to do with own event system. If someone could illustrate the last paragraph of this doc it is great for all the people that are in this case.
var data = {};
var CourseSearcher = React.createClass({
getInitialState: function(){
return {
return { places: '',
branch: 0,
dayOfMonth: '',
timeStart: '',
timeEnd: '',
data: []};
},;
},
handlePlacesChange: function(e){
this.setState({places: e.target.value});
},
handleBranchChange: function(e){
this.setState({branch: e.target.value});
},
handleDayOfMonthChange: function(e){
this.setState({dayOfMonth: e.target.value});
},
handleTimeStartChange: function(e){
this.setState({timeStart: e.target.value});
},
handleTimeEndChange: function(e){
this.setState({timeEnd: e.target.value});
},
handleSubmit: function(e){
// stop the default browser action
e.preventDefault();
// Do an ajax post
$.ajax({
url:'php/results.php',
dataType: 'json',
type: 'GET',
data: {
data: {places: this.state.places,
branch:this.state.branch,
dayOfMonth:this.state.dayOfMonth,
timeStart:this.state.timeStart,
timeEnd:this.state.timeEnd},
},
success: function(data){
this.setState({data: data});
data = this.state.data;
routie('results');
}.bind(this),
error: function (xhr,status,err){
console.error('php/results.php',status,err.toString());
}.bind(this)
});
},
render: function(){
return(
<div>
<form method="get" onSubmit={this.handleSubmit}>
<label>Où?</label>
<input
type="text"
placeholder="Lieux"
value={this.state.places}
onChange={this.handlePlacesChange}
/>
<label>Quoi?</label>
<select value={this.state.branch} onChange={this.handleBranchChange}>
<option>Matière</option>
<option>Français</option>
<option>Anglais</option>
</select>
<label>Quand ?</label>
<input
type="date"
value={this.state.dayOfMonth}
onChange={this.handleDayOfMonthChange}
/>
<input
type="time"
value={this.state.timeStart}
onChange={this.handleTimeStartChange}
/> -
<input
type="time"
value={this.state.timeEnd}
onChange={this.handleTimeEndChange}/>
<button type="submit">Go!</button>
</form>
</div>
);
}
});
console.log(data);
var ResultList = React.createClass({
render: function() {
console.log(data);
return(
<h1>Hello</h1>);
}
);
}
});
var ResultBox = React.createClass({
render: function() {
return (
<div>
<h4>{}</h4>
</div>
);
}
});
routie({
'':function() {
React.render(<CourseSearcher />,
document.getElementById('content'));
},
'results': function() {
React.render(
React.render(<ResultList results={data} />,
document.getElementById('content'));
}
});
Done well with react router ;)
I've done it with react router where components are related to some dedicated urls
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;