Binding data grid to form - reactjs

I have a simple table of data on the left side of my UI and a form on the right. As a brand new user to ReactJS I was able to accomplish this without much difficulty. However, I would like the user to be able to click the Name column from each row and have it populate the form on the right. I have a similar page using jQuery and a small routing library where I simply link each row to a route like #edit/:id and use jQuery to manually bind via the selectors. The reason I am trying out React is to avoid having to do this.
Anywhere here is my code so far. Just a main App component with the form and table in separate components.
var TplList = React.createClass({
getInitialState() {
return {
data: { rows: [] }
};
},
componentDidMount() {
$.ajax({
type: "GET",
dataType: "json",
url: this.props.url,
success: function(response) {
//console.log(response);
this.setState({
data: response
});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
render: function() {
var tblRows = this.state.data.rows.map(function(result, index) {
return <TplRow key={index} tpl={result} />
});
return (
<div>
<h3 className="formHeader">Workcell - Master Templates</h3>
<table id="tplDataTable" width="100%" className="form" cellpadding="2" cellspacing="0" border="0">
<thead>
<tr className="titleGreen">
<th>Name</th>
<th>By</th>
<th>Date</th>
<th className="th_right">R/C</th>
<th className="th_right">Copies</th>
<th className="th_right">Vol (10mM)</th>
</tr>
</thead>
<tbody>{tblRows}</tbody>
</table>
</div>
);
}
});
var TplRow = React.createClass({
handleNameClick: function() {
},
render: function() {
var tpl = this.props.tpl;
var cls = (tpl.index % 2 > 0) ? "" : "alt";
var volume = (tpl.volume > 0) ? tpl.volume + "\u00b5" + "l" : "\u00a0";
return (
<tr className={cls}>
<td><a href="#" onClick={handleNameClick}>{tpl.name}</a></td>
<td>{tpl.username}</td>
<td>{tpl.cre_date}</td>
<td>{tpl.compound_placement}</td>
<td>{tpl.copies}</td>
<td>{volume}</td>
</tr>
);
}
});
var TplForm = React.createClass({
getInitialState() {
return {
"copies": 10
};
},
render: function() {
return (
<div>
<h3 className="formHeader">Edit Master Template</h3>
<table className="form" width="100%" cellpadding="3" cellspacing="0">
<tbody>
<tr>
<td className="label"><label htmlFor="tplCopies">Copies</label></td>
<td className="field">
<input
type="number" min="0" max="500"
name="tplCopies"
id="tplCopies"
value={this.state.copies} />
</td>
</tr>
</tbody>
</table>
</div>
);
}
});
var MasterTemplateApp = React.createClass({
render: function() {
return (
<div id="container">
<div id="wc_tpl_left">
<TplList url="/cfc/com_admin_transfer.cfc?method=getWCTemplateData" />
</div>
<div id="wc_tpl_right">
<TplForm />
</div>
<div className="tpl_clear"></div>
</div>
);
}
});
ReactDOM.render(<MasterTemplateApp />, document.getElementById('app_wc_master_tpl'));

you should write your handleNameClick method like this
handleNameClick: function() {
ReactDOM.render(<TplForm />, document.getElementById('wc_tpl_right'))
}
don't forget to remove TplForm from
<div id="wc_tpl_right">
<TplForm />
</div>
hope this helps

Related

Reactjs do not re-render when children node changed

I contrusted a table-form with two children node buttons and rows using Reactjs.
After change the value of each child node, the parent node receive the new state,
but no re-render happened, why?
<!DOCTYPE html>
<html>
<head>
<title> w3du </title><meta charset="utf-8">
<!-- Bootstrap -->
<link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet">
<style>
.quarcol {
width:25%
}
.vmidcol {
vertical-align:middle
}
.cyan {
color:#fff;
background-color:#63aae7
}
.blue {
color:#fff;
background-color:#60a0e0
}
</style>
</head>
<body>
<div class="container">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title text-center">Panel</h3>
</div>
<div id="canvas"></div>
</div>
</div>
<script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.js"></script>
<script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.js"></script>
<script src="https://cdn.bootcss.com/react/15.6.1/react.js"></script>
<script src="https://cdn.bootcss.com/react/15.6.1/react-dom.js"></script>
<script src="https://cdn.bootcss.com/babel-standalone/6.26.0/babel.js"></script>
<script src="https://cdn.bootcss.com/js-signals/1.0.0/js-signals.js"></script>
<script type="text/babel">
The 'table-row' stuff, the 3rd column is number, after modified, the associated button doesn't changed its value, vice versa
var BudgetTableRow = React.createClass({
getInitialState : function(){
return {
n : this.props.n,
price : this.props.price
};
},
handleChangeNum : function(e){
this.setState({
n : e.target.value
});
this.props.callback(this.props.idx, e.target.value);
},
render : function(){
var styfull = {
width : '100%',
height : '30%',
};
var stycenter = {
verticalAlign : 'middle',
horizontalAlign : 'middle'
};
return (
<tr className={this.props.color}>
<td className="quarcol text-center monofont" style={stycenter}>
<a href="#" data-toggle="tooltip" title={"<h1>"+this.props.label+"</h1>"}>{this.props.name}</a>
</td>
<td className="quarcol"><input name={this.props.label+'-price'}
className="form-control text-center monofont"
type="text" defaultValue={this.state.price} style={styfull}
onChange={this.handleChangePrice}
/></td>
<td className="quarcol"><input name={this.props.label+'-n'}
className="form-control text-center monofont"
type="text" defaultValue={this.state.n} style={styfull}
onChange={this.handleChangeNum} onFocus={this.handleFocus} onBlur={this.handleBlur}
/></td>
<td className="quarcol text-center monofont" style={stycenter}>
{this.state.price * this.state.n}
</td>
</tr>
);
}
});
The 'button' stuff, after modified, the associated table-row doesn't changed its value
var BudgetTableButton = React.createClass({
getInitialState : function(){
return {
n : this.props.n
};
},
handelClick : function(e){
e.preventDefault();
var n = (this.state.n=="0" || this.state.n=="") ? "1" : "0";
this.setState({
n : n
});
this.props.callback(this.props.idx, n);
},
render : function(){
var cls = (this.state.n=="0" || this.state.n=="") ? null : 'btn-info';
return (
<button className={'btn ' + cls} onClick={this.handelClick}>{this.props.label} {this.state.n}</button>
);
}
});
The parent node, the console.log() does right, but the form doesn't re-render, i used unique-key, is it something wrong with this.state.items.map ?
var BudgetTable = React.createClass({
getInitialState : function(){
return {
items : this.props.items
};
},
handleCallback : function(idx, n){
var items = this.state.items;
items[idx].n = n;
this.setState({
items : items
});
},
render : function(){
console.log(this.state.items[0]);
return (
<table className="table monofont">
<thead>
<tr className="blue">
<td className="text-center" colSpan="4">Table</td>
</tr>
</thead>
<tbody>
<tr><td colSpan="4">
{this.state.items.map((it, idx) =>
<BudgetTableButton key={it.label+'r'} label={it.label} idx={idx} callback={this.handleCallback} n={it.n} />
)}
</td></tr>
{this.state.items.map((it, idx) =>
<BudgetTableRow key={it.label+'b'} label={it.label} name={it.name} price={it.price} idx={idx} callback={this.handleCallback} n={it.n} />
)}
</tbody>
</table>
);
}
});
function init(){
return [
{
"price": 1340,
"name": "shoe",
"label": "Q41",
"n" : 1
}, {
"price": 1290,
"name": "clothes",
"label": "Q42",
"n" : 1
}
];
}
ReactDOM.render(
<BudgetTable items={init()} />,
document.getElementById("canvas")
);
</script>
</body>
</html>
The problem in your example is that you are using two states.
(I will talk here about the button component, but both cases are the same)
As you probably already know, the local state can only trigger update on the component it belongs to.
You are trying to sync the button's state with the state of the main app. However getInitialState is only called on initial render and not on re-render (info here).
In order to do what you want, you will need to get the button/row information from the props and not from the state (you already have them in props).
Furthermore, I would delete the children's state at all, because you don't need it. It is better that you keep the state in the top level component and only pass props and functions (like the callback fn) down to the children.
So in this case, if you change the button to the following code, it works flawlessly:
var BudgetTableButton = React.createClass({
handelClick : function(e){
e.preventDefault();
var n = this.props.n === "0" || this.props.n === "" ? "1" : "0";
this.props.callback(this.props.idx, n);
},
render : function(){
var cls = (this.props.n=="0" || this.props.n=="") ? null : 'btn-info';
return (
<button className={'btn ' + cls} onClick={this.handelClick}>{this.props.label} {this.props.n}</button>
);
}
});
You are not mutating the state.
handleCallback : function(idx, n){
var items = [...this.state.items];
items[idx].n = n;
this.setState({
items : items
});
}

Selecting default value using ngModel ngRepeat, ngOptions

Hopefully someone can help.
I am developing an application using HTML AngularJs which uses ng-repeat,ng-options and ng-model and populates multiple rows based on the data in the database for a user. Each row has static data coming from DB (returned as object via restAPI) and dynamic select option for user selection. Select option is hardcoded in app.js and linked to model on HTML for DB update upon selection using update button. Each row has its own button and i can see update function is working at row level.
I want to set the default value of the drop down list dynamically as value of an element coming from database. Object is same as one being used to populate rows with static data .
Code is in the fiddle at https://jsfiddle.net/6j88L61y/4/
HTML below
<body>
<h1 align="center">User Tracker</h1>
<div ng-controller="MainController as main">
<div>
<p>Please Enter ID </p>
<p>
<input type="text" ng-model="main.model.userName"></input>
<button ng-click="main.model.getOrgList()">List State List</button>
</p>
</div>
<hr ng-show="main.model.formSubmitted"></hr>
<div>
<table class="table table-bordered" border="1">
<thead>
<tr>
<th>ID</th>
<th>User Name</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="org in main.model.orgList" id="{{org.id}}">
<td>{{org.id}}</td>
<td align="center">{{org.user}}</td>
<td align="center">
<select ng-model="main.model.selectedRecord.appCurrentStateSelected[$index]" ng-options="option.value as option.value for option in main.model.appCurrentStateList" ></select>
</td>
<td>
<button ng-click="main.model.updateAppDetailsList({id:org.id,userName:org.name,appCrntState:main.model.selectedRecord.appCurrentStateSelected})">Update</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</body>
JS
"use strict";
angular.module('myApp',[]);
angular.module('myApp').service('AppModel', function( $http) {
this.userId='';
this.userName ="";
this.formSubmitted="";
this. selectedRecord ={appCurrentStateSelected:''};
this.appCurrentStateList =[{name: 'New',value:'New',id:1}, {name: 'InUse',value:'InUse',id:2},{name: 'Inactive',value:'Inactive',id:3},{name: 'Deleted',value:'Deleted',id:4}];
this.submittedAppDetailsList=[];
console.log(' json sample:'+this.submittedAppDetailsList);
var path = 'home';
var currentProtocol = location.protocol;
var host =location.host;
var apiHost = currentProtocol+'//'+host+'/api/';
console.log('API url is : ' +apiHost);
// Get method
this.getOrgList = function() {
var path = 'home/userid';
console.log(this.userName);
console.log(this.selectedRecord);
$http.get(apiHost+path+'/'+this.userName+'/')
.then(function(response) {
this.orgList =response.data;
this.formSubmitted = true;
console.log(response.data);
}.bind(this),
function(response) {
console.log(response.data);
});
}
// Post method
this.updateAppDetailsList = function(appdetailsList) {
var path = 'home/update';
console.log(this.selectedRecord);
$http.post(apiHost+'home/update/',appdetailsList)
.then(function(response) {
this.submittedAppDetailsList.push(response.data);
this.formSubmitted = false;
console.log(response.data);
}.bind(this),
function(response) {
console.log('Error : '+response.data);
});
}
});
angular.module('myApp').controller('MainController', ['AppModel', function (AppModel) {
this.model = AppModel;
}]);

how to add row to a table using ReactJS on button click

I have a table and I want to add a row to it when ADD button is clicked. How can I do that using ReactJS instead of simple JavaScript?
code:
var RecordsComponent = React.createClass({
render : function() {
return (
<div>
<table>
<tr>
<td>row 1</td>
</tr>
<tr>
<td>row 2</td>
</tr>
<tr>
<td}>row 3</td>
</tr>
</table>
<button id="addBtn" onClick={addRow}>ADD</button>
</div>
);
},
addRow : function() {
//how to add row using ReactJS?
},
});
React.render(<RecordsComponent/>, document.getElementById('display'))
You need to make your React component have a state and render the component accordingly based on that data. Forget the old "DOM modification" paradigm where you are playing directly with HTML elements.
Untested but should carry the idea across:
var RecordsComponent = React.createClass({
getInitialState: {
return {
rows: ['row 1', 'row 2', 'row 3']
}
},
render : function() {
return (
<div>
<table>
{rows.map((r) => (
<tr>
<td>{r}</td>
</tr>
))}
</table>
<button id="addBtn" onClick={addRow}>ADD</button>
</div>
);
},
addRow : function() {
var rows = this.state.rows
rows.push('new row')
this.setState({rows: rows})
},
});
React.render(<RecordsComponent/>, document.getElementById('display'))
If you're just starting to learn React with your own test apps I would recommend using the most recent version of React, along with, among a lot of other things, the React ES6 class definitions.
Try something like this
var RecordsComponent = React.createClass({
getInitialState: function () {
return {
tablerows:[
{fname:"Tom",lname:"Moody",age:23}
]
};
},
addRow: function() {
// add new data from here
var newdata = {fname:"Tom",lname:"Moody",age:23}
//take the existing state and concat the new data and set the state again
this.setState({ tablerows: this.state.tablerows.concat(newdata ) });
},
rows:function(){
return this.state.tablerows.map(function(row,i){
return (<tr key={i}>
<td>{row.fname}</td>
<td>{row.lname}</td>
<td>{row.age}</td>
</tr>);
});
},
render : function() {
return (
<div>
<table>
<tr>
<td> row 1 </td>
</tr>
<tr>
<td> row 2 </td>
</tr>
<tr>
<td> row 3 </td>
</tr>
{this.rows()}
</table>
<button id= "addBtn" onClick={this.addRow}>ADD</button>
</div>
);
}
});
React.render(<RecordsComponent/>, document.getElementById('display'))

Angular in electron, slow rendering for large json

I'm currently learning Electron and using AngularJS.
I've my REST api in node and express and my database is MongoDB.
this is my route
router.get('/branch/:branch',function (req,res,next) {
var br = req.params.branch;
var final = [];
db.collection('users').find({"branch": br}, function (err, docs) {
docs.forEach(function (ele) {
console.log(ele['fir_id']);
final.push(ele['fir_id']);
})
db.punch_coll.find(function (err, docs) {
var d = [];
console.log(final);
docs.forEach(function (ele) {
console.log(ele['fir_id']);
if(final.includes(parseInt(ele['fir_id']))) {
ele['date'] = ele['date'].toDateString();
ele['punch_in'] = ele['punch_in'].substring(0, 8);
ele['punch_out'] = ele['punch_out'].substring(0, 8);
d.push(ele);
}
console.log(d);
})
console.log(d);
res.send(d);
})
});
});
punch_coll document
{
_id: 58e21075e0c6800ce8b08d92,
fir_id: '4',
date: 'Mon Apr 03 2017',
punch_in: '14:35:57',
punch_out: ''
}
user document
{
_id: 58e20ee0e0c6800ce8b08d82,
name: 'A B C',
fir_id: 1,
branch: 'CSE',
year: 'SE'
}
HTML and Angular Controller Script
<body ng-app="myApp" ng-controller="myCtrl">
<form class="pure-form">
<strong>Enter FIR-ID</strong> <input type="text" ng-model="fid" ng-
change="change()" class="pure-input-rounded">
</form>
</br>
<div class="pure-g">
<div class="pure-u-8-24" style="border-style: solid;border-
color:lightgrey;">
<header class="w3-container w3-light-grey">
<h2>Fir ID :- {{fid}}</h2>
<h3>Name :- {{user[0].name}} </h3>
</header>
<div class="w3-container">
<h2>Branch :- {{user[0].branch}} </h2>
<hr>
<h2>Academic Year :- {{user[0].year}} </h2>
</div>
</div>
</div>
<form class="pure-form">
<select id="state" ng-model="branch" ng-change="changeBranch()">
<option>CSE</option>
<option>MECH</option>
<option>CIVIL</option>
<option>ENTC</option>
</select>
</form>
<!-- <h2>Fir ID :- {{fid}}</h2>
<h2>Name :- {{user[0].name}} </h2>
<h2>Branch :- {{user[0].branch}} </h2>
<h2>Academic Year :- {{user[0].year}} </h2> -->
<div style="right:0;top:0;position:absolute;font-size:20px;">
<table class="pure-table pure-table-horizontal">
<thead>
<tr>
<th>Fir ID</th>
<th>Date</th>
<th>Punch In</th>
<th>Punch Out</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="x in name">
<td>{{ x.fir_id }}</td>
<td>{{ x.date }}</td>
<td>{{ x.punch_in }}</td>
<td>{{ x.punch_out }}</td>
</tr>
</tbody>
</table>
</div>
</body>
<script>
// You can also require other files to run in this process
var app = angular.module('myApp', []);
app.controller('myCtrl', function($scope,$http) {
$scope.name;
$scope.user;
$scope.fid;
$scope.branch='CSE';
$scope.change = function() {
//get punches for specific fir_id
$http.get('http://localhost:3000/users/'+$scope.fid)
.then(function(response) {
$scope.user=response.data;
})
//get punches for specific fir_id
$http.get('http://localhost:3000/punch/'+$scope.fid)
.then(function(response) {
console.log(response.status);
$scope.name=response.data;
}, function errorCallback(response) {
$scope.name=null;
});
};
$scope.changeBranch = function(){
//get record as per branch
$http.get('http://localhost:3000/branch/'+$scope.branch)
.then(function(response) {
console.log(response.status);
$scope.name=response.data;
}, function errorCallback(response) {
$scope.name=null;
});
};
});
The table is rendering slow for large json takes 1second it's like it's lagging.
I'm new to this so of course I'm doing something horrible. I think the way I'm using that async functions are bad also but dont know whats making it slow foreach or anything else.
So, after replacing foreach with simple for loop it solved the problem but I don't know whats the exact reason. Anyone had same problem?

How to get data from smart-table plugin?

I am trying to adapt the example of how to use a custom plugin with smart-table. See the "Create your own plugin" section of http://lorenzofox3.github.io/smart-table-website/
Here is a screen shot of my web app so far:
The problem I have is that I do not know how to get the items select from the smart-table. The selected items on the smart-table are "Beer" and "Mountain Biking". Here's my app.js file:
var app = angular.module("app", ['smart-table']);
app.directive('csSelect', function () {
return {
require: '^stTable',
template: '<input type="checkbox"/>',
scope: {
row: '=csSelect'
},
link: function (scope, element, attr, ctrl) {
element.bind('change', function (evt) {
scope.$apply(function () {
ctrl.select(scope.row, 'multiple');
});
});
scope.$watch('row.isSelected', function (newValue, oldValue) {
if (newValue === true) {
element.parent().addClass('st-selected');
} else {
element.parent().removeClass('st-selected');
}
});
}
};
});
Here is my controller:
app.controller("vendorCtrl", function($scope,$http) {
$scope.vendor_to_look_for = "";
$scope.newvendor = "";
$scope.newlogo = "";
$scope.vendors_types = [];
$scope.itemsByPage=5;
$http.get("http://192.168.1.115:8080/vendor").then(function(response) {
$scope.all_vendors = response.data;
});
$http.get("http://192.168.1.115:8080/type").then(function(response) {
$scope.all_types = response.data;
});
$scope.submit_vendor = function() {
// alert("add new vendor [" + $scope.newvendor + "]" );
var data = $.param({ vendor_name: $scope.newvendor, vendor_logo: $scope.newlogo, vendors_types: $scope.vendors_types });
$http.post("http://192.168.1.115:8080/vendor/add",
// [{'vendor': $scope.newvendor}],
data,
{headers: {'Content-Type': 'application/x-www-form-urlencoded'}}).then(function(response) {
console.log(response);
console.log(response.data);
});
$http.get("http://192.168.1.115:8080/vendor").then(function(response) {
console.log(response);
console.log(response.data);
$scope.all_vendors = response.data;
});
};
});
Update: Here's the pertinent HTML:
<form ng-submit="submit_vendor()">
<table>
<tr><td><label>Vendor name</label></td><td><input type="text" ng-model="newvendor" placeholder="Name"></td></tr>
<tr><td><label>Logourl</label></td><td><input type="text" ng-model="newlogo" placeholder="Url"></td></tr>
</table>
<table st-table="displayedCollection" st-safe-src="all_types" class="table table-striped">
<thead>
<tr>
<th st-sort="type">Select</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="x in displayedCollection">
<td cs-select="x"></td>
<td>{{x.type}}</td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5" class="text-center">
<div st-pagination="" st-items-by-page="itemsByPage" st-displayed-pages="7"></div>
</td>
</tr>
</tfoot>
</table>
<button type="submit" class="btn btn-success btn-lg btn-block"><span class="glyphicon glyphicon-flash"></span> Add </button>
</form>
When I click the add button my submit_vendor() function executes but my the vendor_types I pass in my form data is empty. How do I get the items selected in my table into my form data?
Thanks!
I found this https://github.com/vitalets/checklist-model
This makes things a lot easier. I do not need to declare my csSelect directive :)

Resources