I am learning ReactJS and trying to develop small CRUD form using it. At this point, I am trying to set input values from other sibling component to perform update.
So, if I click on edit button in first row on grid, Organzation Name and Description input boxes should get "abc com" and "company Abc" values respectively.
var OrganizationHandler = React.createClass(
render: function() {
return (
<div>
<OrganizationAPP onOrganizationSubmit=this.handleOrganizationSubmit} />
<OrganizationList external_DeleteOrganization={this.DeleteOrganizationFromServer} data= {this.state.data} />
</div>
);
}
});
var OrganizationList = React.createClass({
internal_DeleteOrganization: function(id) {
this.props.external_DeleteOrganization(id);
},
render: function() {
var results = this.props.data;
var parsed_results = results.objects;
var that = this;
var organizations = parsed_results.map(function(organization){
return <Organization onDeleteOrganization={that.internal_DeleteOrganization} id={organization.id} name={organization.name} description={organization.description} />
});
return(
<div>
{organizations}
</div>
);
}
});
var Organization = React.createClass({
handleDeleteClick: function() {
console.log(this.props);
this.props.onDeleteOrganization(this.props.id);
},
handleEditClick: function () {
alert(this.props.name);
},
render: function() {
return (
<div className="row">
<div className="small-2 large-2 columns">{this.props.id}</div>
<div className="small-4 large-4 columns">{this.props.name}</div>
<div className="small-4 large-4 columns"> this.props.description}</div>
<div className="small-2 large-2 columns">
<input type="button" onClick={this.handleDeleteClick} data-order={this.props.id} value="Delete" />
<input type="button" onClick={this.handleEditClick} data-order={this.props.id} value="Edit" />
</div>
</div>
);
}
});
var OrganizationAPP= React.createClass({
getInitialState: function() {
return {name: '', description:''};
},
onChangename: function(e) {
this.setState({name: e.target.value});
},
onChangedescription: function(e) {
this.setState({description: e.target.value});
},
handleSubmit: function() {
var name = this.refs.name.getDOMNode().value.trim();
var description = this.refs.description.getDOMNode().value.trim();
if (!description || !name) {
return false;
}
this.props.onOrganizationSubmit('{"name":"' + name +'", "description": "' + description +'"}');
this.refs.name.getDOMNode().value = '';
this.refs.name.getDOMNode().value = '';
this.setState({name: '', description: ''});
return false;
},
render: function() {
return (
<div>
<h1>Organization Setup:</h1>
<form onSubmit={this.handleSubmit}>
<div className="row">
<div className="small-12 large-3 columns">
<label>Organization Name:</label>
<input type="text" ref="name" required value={this.props.name} onChange={this.onChangename}/>
</div>
</div>
<div className="row">
<div className="small-12 large-7 columns">
<label>description:</label>
<input type="text" required ref="description" value={this.props.description} onChange={this.onChangedescription} />
</div>
</div>
<div className="row">
<div className="small-2 large-3 columns">
<button type="submit"> Add </button>
</div>
</div>
</form>
</div>
)
}
});
I have successfully performed addition and deletion operations but I don't know how can I set values to be edit in input boxes from sibling component. Please let me know if you don't understand anything as I just started learning reactjs and I know my code is not up to the mark.
Here is an example list editing app where editing an item content is done with a parent input (could easily be a sibling).
/** #jsx React.DOM */
//React is for rendering our app UI.
//Not for writing the app.
//So the first step is building out your logic in whatever you prefer.
//That could be Backbone, some other framework, or plain JS as we do here.
//our app
var app = {
//we'll tell this listener when we change
listener: null,
//a cheap way of creating IDs for our new data
nextId: 3,
//pre-populate our data
rows: [
{id: 1, name: "entry 1"},
{id: 2, name: "entry 2"}
],
//what data are we focused on
focusedId: 1,
//add a new row of data and set it to focused
addRow: function () {
var id = this.nextId++;
this.rows.push({id: id, name: ("entry " + id)});
this.setFocusedId(id);
},
//get the name property given the data id
getName: function(id){
return _.findWhere(this.rows, {id: id}).name;
},
//update the name property of the currently focused data
updateName: function (name) {
var id = this.focusedId;
_.findWhere(this.rows, {id: id}).name = name;
this.listener.changed();
},
//set the focused data
setFocusedId: function (id) {
this.focusedId = id;
this.listener.changed();
},
};
//a row component
var Row = React.createClass({
render: function () {
if (this.props.focused) {
return <span>Value: {this.props.name} [editing]</span>;
} else {
return <span>
Value: {this.props.name}
[<a href='#' onClick={this.props.focus}>edit</a>]
</span>;
}
}
});
//the main view
var View = React.createClass({
//our state is the app
getInitialState: function () {
return {
app: app
};
},
//before we render, start listening to the app for changes
componentWillMount: function () {
this.state.app.listener = this;
},
//update if the app tells us it changed
changed: function () {
this.forceUpdate();
},
//a handler we'll use for input fields
textChanged: function (event) {
this.state.app.updateName(event.target.value);
},
//let's render
render: function () {
var app = this.state.app;
//build an array of row components
var rows = _.map(app.rows, function (row) {
var focus = function () {
app.setFocusedId(row.id);
};
//the actual row component
//give the row a unique id
//give it a name, the focus handler function,
//and tell it if it has the current focus
return <li key={row.id}>
<Row
name={row.name}
focused={row.id == app.focusedId}
focus={focus}
/>
</li>;
});
//the main app view
return <div>
EDIT:
<input
type="text"
value={app.getName(app.focusedId)}
onChange={this.textChanged}
/>
<ul>{rows}</ul>
<a href="#"
onClick={function(){app.addRow()}}>
add row
</a>
</div>;
}
});
React.renderComponent(
<View />
, document.body);
Here is a working example:
http://jsbin.com/laxejufila/2/edit
Related
I'm trying to create a menu where the user should create a character details but I'm having an issue to update the Options states through an input of a child.
var Name = React.createClass({
render: function(){
return(
<input type="text"/>
)
}
});
var Options = React.createClass({
getInitialState: function(){
return{
name: ''
}
},
render: function(){
return (
<div>
Name: <Name onChange={this.updateName} value={this.state.name} />
</div>
)
},
updateName: function(evt){
this.setState({
name: evt.target.value
});
}
});
How can I go about updating the Option states using the input from Name?
you need onChange function on the Name component as well, that sends the value to the parent component
Try this:
var Name = React.createClass({
onUpdate: function(evt) {
this.props.onChange(evt);
}
render: function(){
return(
<input type="text" onChange={this.onUpdate} value={this.props.value}/>
)
}
});
var Options = React.createClass({
getInitialState: function(){
return{
name: ''
}
},
render: function(){
return (
<div>
Name: <Name onChange={this.updateName} value={this.state.name} />
</div>
)
},
updateName: function(evt){
this.setState({
name: evt.target.value
});
}
});
Component Communication in React
Refer this link to know what are the ways to communicate between React components.
I have a list in array like,
ReactDOM.render(<FilteredList labelInfo = { labelArr } />, document.getElementById('labels'));
and data in array is,
labelArr.push({ "Label": "abc", "id": "a.dfg", "src_tbl": "d_v" });
React function is as,
var FilteredList = React.createClass({
filterList: function(event) {
var updatedList = this.state.initialItems[0];
updatedList = updatedList.filter(function(item) {
return (item.Label.toLowerCase().search(event.target.value.toLowerCase()) !== -1 || item.id.toLowerCase().search(event.target.value.toLowerCase()) !== -1);
});
this.setState({items: updatedList});
LabelList = updatedList;
},
getInitialState: function() {
LabelList = this.props.labelInfo;
return {
initialItems: [ this.props.labelInfo ]
}
},
componentWillMount: function() {
this.setState({items: this.state.initialItems[0]})
},
newAddedLabel: function(e) {
//some code..
LabelList.push({ "Label": $('#lbl_txt').val(), "id": labelId , "type": "text"});
<List items={LabelList} />
//this.state.initialItems[0] = LabelList;
this.setState({items: LabelList});
},
changedLabel: function(e) {
//some code..
if( labelIndex > -1 )
{
LabelList[labelIndex].Label = $('#changelbl_txt').val();
LabelList[labelIndex].id = $('#changelbl_id').val();
}
<List items={LabelList} />
//this.state.initialItems[0] = LabelList;
this.setState({items: LabelList});
},
render: function() {
return (
<div className="filter-list">
<input
type="text"
id="searchBox"
placeholder="Search"
onChange={this.filterList}/>
<i
className="fa fa-plus addLabel"
title="Add a new label"
id="add_label"
onClick={this.addLabel}>
</i>
<List items={LabelList}/>
</div>
);
}
});
addLabel call newAddedLabel() and add new values in it.. The main problem is when I started searching in input box, it makes a list of suggestions list(which shows on typing in inputbox) and save in a LabelListand then if I add a new value by clicking in fa-plus it shows only suggested list only instead of whole list..how can I display whole list when new label is added?
You are calling the filterList function onChange
<input type="text" id="searchBox" placeholder="Search" onChange={this.filterList}/>
inside this function you are changing the state of initialItems
var updatedList = this.state.initialItems[0];
This why after you put some value inside the input your initialItems change, make a clone of this array like this:
var updatedList = this.state.initialItems[0].slice();
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
</head>
<body>
<h1>Example</h1>
<div id='root'></div>
<script type="text/babel">
var FilteredList = React.createClass({
filterList: function(event) {
if(event.target.value.trim() === ''){
this.setState({ suggest: [] })
return
}
var updatedList = this.state.initialItems.slice();
updatedList = updatedList.filter(function(item) {
return (item.Label.toLowerCase().search(event.target.value.toLowerCase()) !== -1 || item.id.toLowerCase().search(event.target.value.toLowerCase()) !== -1);
});
this.setState({suggest: updatedList || []});
},
getInitialState: function() {
return {
initialItems: [ this.props.labelInfo ],
items: [ this.props.labelInfo ],
suggest: [],
id: 1
}
},
newAddedLabel: function(e) {
var newId= ++this.state.id + ""
var item = { "Label": this.refs.keyword.value, "id": newId , "type": "text"}
var newItems = this.state.initialItems.slice()
newItems.push(item)
var newItems2 = this.state.items.slice()
newItems2.push(item)
this.setState({initialItems: newItems, items: newItems2 });
},
render: function() {
return (
<div className="filter-list">
<input
type="text"
ref="keyword"
id="searchBox"
placeholder="Search"
onChange={this.filterList}/>
<button
className="fa fa-plus addLabel"
title="Add a new label"
id="add_label"
onClick={this.newAddedLabel}>Add
</button>
<u>{ this.state.suggest.map(e => <li value={e.id}>{e.Label}</li>) }</u>
<h1>Saved List</h1>
<u>{ this.state.items.map(e => <li value={e.id}>{e.Label}</li>) }</u>
</div>
);
}
});
ReactDOM.render(<FilteredList labelInfo = { { "Label": "abc", "id": "a.dfg", "src_tbl": "d_v" } } />, document.getElementById('root'))
</script>
</body>
</html>
If you want to achieve that using the above code, you can have a boolean variable in your state which decides weather to render the suggested list or the initial list.
The in the render method, the List component should be implemented like this:
<List items={this.state.showInitialList?this.state.initialItems:LabelList} />
You can set the showInitialList variable in your state to true whenever fa-plus is clicked.
My project keeps crashing when I attempt to insert a new recipe element. I use the this.state.recipes.map... in RecipeList to be able to update the recipes as needed(e.g. delete,edit,etc.). The delete functionality works, but I am unable to add a new recipe element.
If I switch the statement to this.props.recipes.map..., I am able to insert elements without an issue, but am unable to delete since the delete triggers a state change, and needs the state change to reflect the update instead of the props. Anyone have any tips on this issue? Thanks!
Recipe List:
var RecipeList = React.createClass({
getInitialState: function(){
return {recipes: []};
},
deleteRecipe: function(recipe){
var curRecipes = this.state.recipes.slice('');
curRecipes.splice(recipe.recipeKey,1);
this.setState({recipes: curRecipes});
},
componentWillMount: function(){
this.setState({recipes: this.props.recipes});
},
render: function(){
var recipeNodes = this.state.recipes.map(function(recipe,index){
return <Recipe onDelete={this.deleteRecipe} recipeKey={index} key={index} recipeTitle={recipe.recipeTitle} ingredients={recipe.ingredients} instructions={recipe.instructions} />
},this);
return(
<div>
{recipeNodes}
</div>
);
}
});
Recipe Container:
var RecipeBox = React.createClass({
getInitialState: function(){
return {showForm: false,
recipes: []
};
},
openForm: function(){
this.setState({showForm: true});
},
handleRecipeSubmit: function(recipe){
var curRecipes = this.state.recipes.slice('');
curRecipes.push({recipeTitle: recipe.recipeTitle,ingredients: recipe.ingredients, instructions: recipe.instructions});
this.setState({recipes: curRecipes});
},
render: function(){
return(
<div id="recipeBox">
<RecipeList recipes={this.state.recipes} />
<div className="recipeButtons">
<button id="addRecipeButton" className="btn-style" onClick={this.openForm}>Add Recipe</button>
</div>
{this.state.showForm ? this.refs.dialogWithCallBacks.show() : null}
<SkyLight
dialogStyles={formDialog}
ref="dialogWithCallBacks"
title="Add Recipe">
<RecipeForm onRecipeSubmit={this.handleRecipeSubmit} skylightRef={this.refs.dialogWithCallBacks} />
</SkyLight>
</div>
);
}
});
Recipe Form:
var RecipeForm = React.createClass({
getInitialState: function(){
return {hideDialog: false};
},
getFormData: function(){
var ingredients= document.getElementsByClassName("ingredient"),
recipeName = document.getElementsByName('recipeName')[0].value,
instructions = document.querySelector('textarea').value,
data = [];
ingredients = [].slice.call(ingredients).map(function(ingredient,index){
return {
"quantity": ingredient.childNodes[0].value,
"ingredient": ingredient.childNodes[1].value,
"unit": ingredient.childNodes[2].value
};
});
// Combine results into output array
data.push(recipeName);
data.push(ingredients);
data.push(instructions);
return data;
},
submitRecipe: function(event){
event.preventDefault();
var data = this.getFormData();
// Hide the SkyLight modal container
this.setState({hideDialog: true});
// Submit form
this.props.onRecipeSubmit({recipeTitle: data[0], ingredients: data[1], instructions: data[2]});
},
render: function(){
return(
<form onSubmit={this.submitRecipe}>
<section className="recipe-main">
<h2 style={{'border-bottom': 'none'}}>Recipe Name</h2>
<RecipeFormName />
<h2 style={{'border-bottom': 'none'}}>Ingredients</h2>
<RecipeFormIngredients />
</section>
<RecipeFormInstructions />
<input type="submit" value="Add Recipe" />
{this.state.hideDialog ? this.props.skylightRef.hide() : null}
</form>
)
}
});
You should move the code in componentWillMount to getInitialState.
getInitialState: function(){
return {recipes: this.props.recipes};
},
Needed to change the RecipeList component to
<RecipeList recipes={this.state.recipes} onChange={this.handleChange}/>
and then handle the deletion change from the RecipeBox instead of directly in RecipeList. Have to use this.props.map... to display new recipes and also delete visible ones.
var RecipeList = React.createClass({
getInitialState: function(){
return {recipes: this.props.recipes};
},
deleteRecipe: function(recipe){
var curRecipes = this.props.recipes.slice('');
curRecipes.splice(recipe.recipeKey,1);
this.props.onChange({recipes: curRecipes});
},
render: function(){
var recipeNodes = this.props.recipes.map(function(recipe,index){
return <Recipe onDelete={this.deleteRecipe} recipeKey={index} key={index} recipeName={recipe.recipeName} ingredients={recipe.ingredients} instructions={recipe.instructions} />
},this);
return(
<div>
{recipeNodes}
</div>
);
}
});
I have a select all component rendering in my table header, I would like it to check all of the checkboxes on the page when checked, if one of the checkboxes is unchecked I would like the select all to unselect too:
var SelectAll = React.createClass({
handler: function(e) {
e.target.value;
e.preventDefault();
channel.publish({
channel: "contact",
topic: "selectAll",
data: {
contacts: this.props.data
}
});
},
render: function() {
console.log(this.props.data.children);
var id = this.props.contacts;
var isSelected = this.props.data.isSelected;
return (
<div className="contact-selector">
<input type="checkbox" checked={isSelected}
onChange={this.handler} />
</div>
);
},
});
My checkboxes are rendered in each table row:
var ContactSelector = React.createClass({
getInitialState: function() {
return {
selectedContacts:[]
};
},
handleChange: function(e) {
var id = e.target.attributes['data-ref'].value;
if (e.target.checked === true){
contactChannel.publish({
channel: "contact",
topic: "selectedContact",
data: {
id: id
}});
} else{
contactChannel.publish({
channel: "contact",
topic: "deselectedContact",
data: {
id: id
}});
}
},
render: function() {
var id = this.props.data.id;
var isSelected = this.props.data.IsSelected;
return (
<div className="contact-selector">
<input type="checkbox"
checked={isSelected} data-ref={id}
onClick={this.handleChange} />
</div>
);
}
});
Can anyone please tell me how to setup the check all? Using state or refs etc
I'm trying to create a todo list where after you finish one task,
only then will the next task be enabled (to tick as finished).
Here is what I have so far:
/** #jsx React.DOM */
$(function(){
var tasks = [
{title: "Wake up"},
{title: "Eat dinner"},
{title: "Go to sleep"}
];
var Task = React.createClass({
getInitialState: function(){
return {locked:true, done:false}
},
handleClick: function(e){
this.setState({done: !this.state.done});
var selector = '.toggle[data-order="'+(this.props.order+1)+'"]';
this.setState({locked: true})
console.log(selector)
console.log($(selector).removeAttr("disabled"))
},
render: function() {
var locked;
//Fix first task to not be disabled
if(this.props.order == 0 && this.state.done === false)
locked = false;
else
locked = this.state.locked;
var done = this.state.done ? "Done":"Not done";
var classView = "task" + (this.state.done ? " done":" not-done");
return (
<div class="todo well well-sm" class={classView}>
<span class="description">{this.props.title}</span>
<button type="button" onClick={this.handleClick} data-order={this.props.order} disabled={locked} class="toggle btn btn-default btn-xs pull-right">
<span class="glyphicon glyphicon-unchecked"></span> Done
</button>
</div>
);
}
});
var TaskList = React.createClass({
render: function(){
var i = -1;
var taskNodes = this.props.data.map(function (task) {
return <Task title={task.title} order={++i} />;
});
return (
<div class="task-list">
{taskNodes}
</div>
);
}
});
var Guider = React.createClass({
render: function(){
return (
<div>
<TaskList data={this.props.data} />
</div>
);
}
});
React.renderComponent(<Guider data={tasks} />, document.body);
});
The next buttons are still not disabled, and I feel that I'm doing something wrong in general (not in accordance with the react "zen").
Btw:
How can I change the state for a dom element without the user triggering it? is there any id I should use?
If you initiate the data into non-root component, it becomes hard to update other components. So I prefer keeping data into root component, Then pass a click handler as props. Now you'll have access to that handler inside non-root component. Calling that will update root component and so the other non-root components.
Here's working jsFiddle - http://jsfiddle.net/ammit/wBYHY/5/
Example -
var Task = React.createClass({
handleClick: function (e) {
// Passing order of task
this.props.clicked(order);
},
render: function () {
return ( <button type="button" onClick={this.handleClick}></button> );
}
});
var TaskList = React.createClass({
getInitialState: function(){
// initiate tasks here
},
whenClicked: function(order){
// Revise the tasks using `order`
// Finally do a setState( revised_tasks );
},
render: function(){
return ( <Task clicked={this.whenClicked} /> );
}
});