Update a specific table row based on unique id - reactjs

I have two components, the parent is called Layout and the child Report. In Report I have a table that loops through and renders a list of car accidents. I have a handler - handleIncidentReport in Report that calls a function in Layout to update this.state.obds (car messages) which then obviously updates the child Report.
My question is what is the proper ReactJS way to have it so only the row clicked has its {this.props.incident_report} updated, and not the other dynamically created rows with the same yield statement (not sure of the correct terminology).
I'm less than a week into ReactJS and I know I could hack it together but I want to know the proper way.
Here is a chunk of the two files -
Report
handleIncidentReport: function(e) {
var accident_id = $(e.target).closest('tr').data('accident-id')
this.props.changeIncidentReport(e, accident_id)
},
render: function() {
var self = this;
var accidents = [];
for (var i = 0; i < this.props.accidents.length; i++) {
var incident = this.props.accidents[i];
accidents.push([
<tr key={i} onClick={self.handleIncidentReport} data-accident-id={incident.id} >
<td>{incident.owner.first_name} {incident.owner.last_name}</td>
<td>{incident.car.title}</td>
<td>{moment(incident.created_at).format("MMM D, YYYY - hh:mm A")}</td>
</tr>,
<tr className="incident-report">
<td colSpan="3">
<Obds obds={this.props.incident_report} />
</td>
</tr>
]);
};
return (
<div className="report">
<table className="table">
<thead>
<tr>
<th>Name</th>
<th>Car</th>
<th>Date</th>
</tr>
</thead>
<tbody>
{accidents}
</tbody>
</table>
</div>
);
}
Layout
changeIncidentReport: function(e, accident_id) {
var $tr = $(e.target).closest('tr');
$.ajax({
method: "GET",
data: {accident_id},
url: "/superadmin/emergency_analysis/get_incident",
datatype: 'jsonp'
}).success(function(incident){
this.setState({
incident_report: incident
});
}.bind(this));
},

You should make it so that handleIncidentReport returns a function that is configured (through closures) with the index of each of the rows, something like:
handleIncidentReport: function(accident_id) {
return function(e) {
this.props.changeIncidentReport(e, accident_id)
}
},
(If you aren't sure how closures work you should check out this excellent post: How do JavaScript closures work?)
Then update your render to use
<tr key={i} onClick={self.handleIncidentReport(i)} data-accident-id={incident.id} >
and each of the rows will have a unique handler all of their own that will be called when they're clicked on.
This should also get rid of some of that jQuery which figures out which accident_id was clicked on too.

Related

ServiceNow spUtil

I'm trying to make a slight improvement to an existing widget that our team created, but can't seem to get it to work correctly. We have a widget that does a RowCount of tasks and groups them by state. I want the RowCount to auto update once a task is complete without having the user press the refresh button. I've read some documentation on $rootscope, $broadcast, and $on, but can't seem to get it to work.
Below is snippet of our HTML:
<table class="table table-sm table-responsive">
<tbody>
<tr class="h3">
<td colspan=2>Complete</td>
</tr>
<tr class="h2 bg-success" ng-repeat="x in data.values track by $index">
<td><span class="glyphicon glyphicon-check"></span></td>
<td>{{x.completedCount}}</td>
</tr>
</tbody>
</table>
A snippet of our Server Script:
var values = [];
var _completedCount;
var gsCompleted = new GlideRecordSecure('sn_hr_core_task');
//CLOSED COMPLETE, CLOSED INCOMPLETE,
gsCompleted.addQuery('state', 'IN', '3,4,7');
gsCompleted.addQuery('assigned_to', gs.getUserID());
gsCompleted.addQuery("parent.state", 'NOT IN', '1,800,900');
gsCompleted.query();
if(gsCompleted){
_completedCount = gsCompleted.getRowCount();
}
else{
_completedCount = 0;
}
values.push(
{
completedCount: _completedCount
});
data.values = values;
How do I get this widget to auto update the Completed row count without refreshing the page? I've been playing around with spUtil recordWatch, but cannot get it to work correctly:
function($scope, $sce, spUtil) {
var c = this;
c.data.loading = true;
//After page initially loads re-call server script to load data
c.server.get({
action: 'retrieve_data'
}).then(function(response) {
c.data.loading = false;
console.log('Response');
console.log(response);
c.data.values = response.data.values;
spUtil.recordWatch($scope, 'sn_hr_core_task', "", function(name,data) {
spUtil.update($scope);
})
});
}
Take a look at the widget Simple List, it has an example of one that may help a bit.
You should be able to change your recordWatch to this
var filter = "stateIN3,4,7^parent.stateNOT IN1,800,900^assigned_to=" + window.NOW.user_id;
spUtil.recordWatch($scope, 'sn_hr_core_task', filter);
You generally won't need a callback function unless there is some specific action you're triggering.

React make table clickable and edit details

How do I make a table row clickable to edit and update details? I'm retrieving these details from pouchdb.
I'm pasting portion of my code below for your evaluation:
this.state = {docs: []}
this.db = this.props.db
componentDidMount () {
this.updateDocs()
this.db.changes({
since: 'now',
live: true
}).on('change', (change) => {
this.updateDocs()
}).on('error', (err) => {
console.error(err)
})
}
updateDocs () {
this.db.allDocs({include_docs: true}).then((res) => {
var docs = res.rows.map((row) => row.doc)
this.setState({docs})
})
}
And the table below:
<div className='table-list'>
<table>
<thead>
<tr>
<th>Registration Type</th>
<th>First Name</th>
<th>Middle Name</th>
</tr>
</thead>
<tbody>
{this.state.docs.map((doc) => <DataRow key={doc._id} doc={doc} {...this.props} />)}
</tbody>
</table>
</div>
class DataRow extends React.Component {
render () {
let {doc} = this.props
return (
<tr>
<td>{doc.RegistrationInfo['registrationtype']}</td>
<td>{doc.RegistrationInfo['firstname']}</td>
<td>{doc.RegistrationInfo['middlename']}</td>
</tr>
)
}
}
I want to be able to click and edit each of the rows.
My first suggestion - do not do this. Editable grids are quite tough components to implement on your own.
Therefore you have some options to choose from:
Use existing frameworks with editable grids: KendoUI, Wijmo, etc. Although they are quite pricely and most of them have quite pure support for react as for now.
There are some standalone grids with editing functionality: ag-grid, react data grid etc. Some of them are free, other paid.
You can develop your own editable grid based on powerfull components like fixed-data-table, react-virtualized, etc. This approach will still will require some coding to be done but will save you a lot of time.
Make your own components as you are trying now.
If you still would like to go with #4 you can do it this way:
4.1. In state store column of the currently edited cell: editingColumn.
4.2. Assign onClick handler on your <td> tags: <td onClick={(ev) => this.onCellClick(ev))}>. In the handler set editingColumn
4.3. In your render replace
<td>{doc.RegistrationInfo['registrationtype']}</td>
with
<td>{this.renderCell('columnName')}</td>.
And renderCell will look something like this:
private renderCell(colName)
{
if(this.state.editingColumn >= 0 && this.state.editingRow >= 0)
{
// Render your editing control here
// Assign to its 'onChange' like event and save changes to this.props.RegistrationInfo[colName];
}
else
{
return this.props.RegistrationInfo[colName];
}
}
This is very rough description but I hope it will help you get on going.

Handling data streaming using ng-repeat

I got the following data which comes out of a EventSource interface. I'm using this data to build a simple table (i have specified the table structure below) using angular ng-repeat. The issue i have is since the data changes rapidly i.e. server would be sending the changes continuously for the requested symbol and then it would keep upon updating the values. I want to add a row when data's are requested for a symbol and then should update the same row for the new values. This is pretty easy and is working.
But the real challenge is (atleast for me) when i request another symbol i want that to be added to a new second row and from now on it should update the values in both rows. It goes on as we request new symbols. With the following data structure is this possible, if so please explain.
Data Structure:
data = [
AMZN: {
name: Amazon,
symbol: AMZN,
openPrice: $100,
latestPrice: $200,
percentage: 0.1
},
FB: {
name: Facebook,
symbol: FB,
openPrice: $150,
latestPrice: $250,
percentage: 0.2
}
]
Table Structure:
<table>
<thead>
<tr>
<th>Session ID</th>
<th>Company Name</th>
<th>Symbol</th>
<th>Open Price</th>
<th>Latest Price</th>
<th>Percentage</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="stockVal in data">
<td>[Empty]</td>
<td>{{stockVal.companyName}}</td>
<td>{{stockVal.companySymbol}}</td>
<td>{{stockVal.openPrice}}</td>
<td>{{stockVal.latestPrice}}</td>
<td>{{stockVal.percentage}}</td>
</tr>
</tbody>
</table>
What happens now is when i do a request for AMZN the tables populates the values pertaining to Amazon and then if i request for FB it replace the values in the same row... i.e. both FB and AMZN gets replaces in the same row. Bottom line is i want FB values to be populated in new row.
Controller code:
All it does is making a request for the end point with window.EventSource and grab the data and attach it to the scope object. To avoid populating n number of rows the data array is flushed each time like making the length to 0.
Adding controller code below,
$scope.generateData = _generateData;
function generateData() {
endPoint = '/getData'
$scope.symbols = window.document.getElementById('symbols').value;
var eventSource = new window.EventSource(endPoint + '?&s=' + $scope.symbols);
eventSource.onmessage = function(e) {
var data = null;
try {
data = JSON.parse(e.data);
} catch (err){
}
if(data) {
$scope.$apply(function () {
$scope.companyName = data.name;
$scope.companySymbol = data.symbol;
$scope.openPrice = data.openPrice;
$scope.latestPrice = data.latestPrice;
$scope.percentage = data.percentage;
});
var specData = {
[$scope.companySymbol]: {
name: $scope.companyName,
symbol: $scope.companySymbol,
openPrice: $scope.openPrice,
latestPrice: $scope.latestPrice,
percentage: $scope.percentage
}
}
$scope.data[0] = specData[$scope.companySymbol];
}
}
};

Angular JS - Display only the field with the highest value from JSON data

I have a JSON retrieve from database
[{"id":1,"firstname":"Alan Cayetano","numbers":2},{"id":2,"firstname":"Bong Marcos","numbers":0},{"id":3,"firstname":"I Dont Care","numbers":3},{"id":4,"firstname":"toto tata","numbers":0},{"id":5,"firstname":"titi terter","numbers":0},{"id":6,"firstname":"Ian Go","numbers":0}]
this is the result when displayed in table result
firstname lastname numbers
Alan Cayetano 10
Bong Marcos 4
Ian Go 3
What Ever 0
I only want the data with the highest number value
In this case
firstname lastname numbers
Alan Cayetano 10
This data is dynamically fetch from database
My angular.js
<script src="//code.angularjs.org/1.4.8/angular.js"></script>
<script>
var app = angular.module('myApp', []);
app.config(function($interpolateProvider) {
$interpolateProvider.startSymbol('//');
$interpolateProvider.endSymbol('//');
});
app.controller('customersCtrl',['$scope','$http',function($scope, $http) {
//$http.get("http://localhost:8093/voters/voters_angular")
$http.get("{{ path('vp_president') }}")
.success(function (response) {
$scope.names= JSON.parse(response);
});
}]);
//console.log(names);
</script>
Table
<div ng-app="myApp" ng-controller="customersCtrl">
<table class="table">
//names//
<thead>
<tr>
<th>Firsname</th>
<th>Lastname</th>
<th>NUm</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="x in names">
<td>//x.firstname//</td>
<td>//x.lastname//</td>
<td>//x.numbers//</td>
</tr>
</tbody>
</table>
</div>
How to achieve this? I am still learning Angular Js
I wonder if Angular's $last filter will work on this
This should work as describe
Link
<tr ng-repeat="x in names| orderBy:'-numbers' | limitTo:1">
<td>//x.id//</td>
<td>//x.firstname//</td>
<td>//x.numbers//</td>
</tr>
It's better to find your max in the controller, not in the view.
function findMax(names) {
var result = null;
for (var i = 0; i < names.length; i++) {
var name = names[i];
if (result == null || name.numbers > result.numbers) {
result = name;
}
}
return result;
}
$scope.name = findMax($scope.names);
And in html
<tr>
<td>{{name.firstname}}</td>
<td>{{name.lastname}}</td>
<td>{{name.numbers}}</td>
</tr>
In AngularJS it's better to pre-sort your data before showing in the view, the ng-repeat creates a watcher for each object it repeat's, in AngularJS the number of watchers is associated with performance if you have many watchers your performance it's worst.
If you don't need the other values to appear, there is no need to create watchers for that values, so it's better to pre-sort the array.
you can do this :
<tr ng-repeat="x in names | orderBy:numbers:reverse | limitTo:1">
You can use:
angular.forEach($scope.names, function (value, key) { //code });
With angular foreach you can read row by row in your $scope.names, and use your logic for get the msx value.
When:
key -> is the position (use an alert(key) to see the info)
value -> is a the row of the json. For get data use value.id, value.firstname, etc.

how to fix ngtable pagination?

I have developed a ngtable with a filter. The pagination on the table does not work anymore though? When I select the show 10 rows on the bottom of the table it still shows 14 rows? How can i fix the pagination/indexing?
This is my table definition:
<table ng-table="tableParams" class="table">
<tr ng-repeat="account in $parent.filtered =(data | filter:search.accountName | filter:search.id)">
<td data-title="'id'">
{{account.account.accountId.id}}
</td>
<td data-title="'name'">
{{account.account.accountName}}
</td>
</tr>
</table>
plunkr:http://plnkr.co/edit/Rqt6px?p=preview
You need to figure pagination function by yourself. You may see ng-table's example in here.
var Api = $resource("/data");
this.tableParams = new NgTableParams({}, {
getData: function(params) {
// ajax request to api
return Api.get(params.url()).$promise.then(function(data) {
params.total(data.inlineCount); // recal. page nav controls
return data.results;
});
}
});
It first load all the data into Api. The params.url() contains current page and page count. It then use these two variable to return part of dataset. So you may need to figure out how to return this part of data.

Resources