Why the function inside <td> did not run - reactjs

I want to render the table data like below.
However, the second line of "Available Channel" return value is number and I want to get the string. like 0--None, 1--Web, 2--Pos....
I try to use the paymentType function to switch it but it did not work.
It seems that this function did not run at all. But I do not know why
<table>
...
<tr>
<td> { this.paymentType(id) } </td>
<tr>
</table>
And the function I wrote likes:
paymentType(id){
if(id===0) { return "None"}
else if(id===1) { return "Web"}
else if(id===2) { return "Pos"}
}
I only got a blank line.
I changed it to arrow function, still a blank line.
<table>
...
<tr>
<td> { () => this.paymentType(id) } </td>
<tr>
</table>
I bind this in the constructor method, still a blank line.
constructor(props){
super(props)
this.paymentType = this.paymentType.bind(this)
}
I wonder how to solve this problem and all your replies would be appreciated.

Just call your paymentType function directly, don't wrap it an an arrow function declaration:
{ this.paymentType(payment.payment_type_id) }
As you had it you efectively had a function declaration in the curly braces, you were not calling your helper function.

Related

js map function index does not increment

probably missing something obvious but my map function's index param will not increment up its just stuck as 0 or whatever static index I set it to, maybe I'm missing something because my data structure im passing in is an array of objects like:
(20) -> [{},{},{}]
and each object has quite a few properties
I'll provide my map function below for greater context
also I can manually change my index and my code works fine grabbing an object at whatever index I specify i.e '5' or '13' and my depth is correct because its displaying the property values as it should, wondering if I need to nest something to make this work?
if I console log my state I have this structure here just an array of objects
//storing to state
componentDidMount(){
fetch(`https://api.nasa.gov/neo/rest/v1/feed?start_date=${this.state.time[0]}&end_date=${this.state.time[1]}&api_key=ipAxYzaENbqRKb7GgzFPcH6QUBsHXY3QKB7uXOf5`
)
.then(response => response.json())
.then((data) => {
let ast = []
Object.values(data.near_earth_objects).forEach((arr)=>{
//push the two arrays together
ast.push(...arr)
})
this.setState({asteroids:[ast]})
});
}
render() {
return (
<div>
<h4>Near earth objects</h4>
<table>
<tbody>
<tr>
<th>Name</th>
<th>ID</th>
<th>Magnitude</th>
<th>Hazardous</th>
<th>Sentry Object</th>
</tr>
{
this.state.asteroids.map((arr,index)=>(
//for each item (arr) there will be properties
<tr key={arr.toString()}>
<td >{arr[index].name}</td>
<td >{arr[index].id}</td>
<td >{arr[index].absolute_magnitude_h}</td>
<td >{arr[index].is_potentially_hazardous_asteroid==true? "true": "false"}</td>
<td >{arr[index].is_sentry_object==true? "true": "false"}</td>
<td > index {index}</td>
</tr>
))
}
</tbody>
</table>
</div>
)
}
The error is in componentDidMount when you setState of asteroids your taking the array and putting it inside another array
this.setState({asteroids:[ast]})
// instead it should be
this.setState({asteroids: ast })
Checkout this codesandbox with a working example of your code https://codesandbox.io/s/empty-forest-jvwin?file=/src/App.tsx
As for why the index is stuck, it's because the asteroids array in your example has only 1 element (the array that is holding all the asteroids data).

Copy the HTML table to clipboard in reactjs

I have an HTML table in my react project. I want to copy the table to clipboard.
<table id="sample">
<thead>
<th>Amount</th>
<th>Charges</th>
</thead>
<tbody>
<tr>{item.Amount}</tr>
<tr>{item.Charges}</tr>
</tbody>
</table>
This is the table structure . I haven't included any react code.
I referred this sample code https://stackblitz.com/edit/js-copy-element?file=index.js . But it's not in react.
Using react code how can I copy the table to clipboard?
might be too late to answer this, but here's how I did it.
I had element rendered in my component's render function.
render(){
return(
<table id="table">
.... your table content
</table>
)
}
and simply build a copy function by referring to the sample code shared above.
copyTable = () => {
const elTable = document.querySelector('table');
let range, sel;
// Ensure that range and selection are supported by the browsers
if (document.createRange && window.getSelection) {
range = document.createRange();
sel = window.getSelection();
// unselect any element in the page
sel.removeAllRanges();
try {
range.selectNodeContents(elTable);
sel.addRange(range);
} catch (e) {
range.selectNode(elTable);
sel.addRange(range);
}
document.execCommand('copy');
}
sel.removeAllRanges();
console.log('Element Copied! Paste it in a file')
}
then, call this function by building an element like this
<h1 onClick={()=>{this.copyTable()}}>Copy to table</h1>

Iterate with ngFor on objects obtained from Firebase

I have a list of objects stored in firebase database:
I am getting this list using angular http get request. After getting it I want to iterate on a li in html template using ngFor="let subject of subjects" which gives Error:
ERROR Error: Cannot find a differ supporting object '[object Object]'
of type 'object'. NgFor only supports binding to Iterables such as
Arrays.
As I searched online, I came to know that ngFor can only be used on arrays while I am getting nested objects. Can anyone please suggest me what should I do iterate these objects.
So far I have tried Array.of which converts whole list into someArray[0]. I also tried to manually change the unique ids of objects into array indexes [0, 1, 2] which worked but when I add new subject using http post firebase automatically assigns unique id to new subject making it uniteratable.
In simple words tell me to convert nested objects into an arrayList or how can I change the firebase default behavior of assigning unique id in angular (I found something like firebase push function which I couldn't understand).
Update:
(Code from ExaminerService)
getBatchSubjects(batch){
return this.http.get(firebaseLinkGoesHere);
}
(Code from Component)
onBatchSelected(event){
this.selectedBatch = event.target.value; //getting value of a html select
this.examinerService.getBatchSubjects(this.selectedBatch)
.subscribe(
(response : Response) => {
this.selectedBatchData = response.json();
}
),(error) => {
console.log(error);
}
}
(Code from HTML Template)
<div class="batchDetails">
<table class="table table-striped">
<thead>
<tr>
<th class="text-center">S.No</th>
<th>Subject Name</th>
<th>Facilitator</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let subject of selectedBatchData.subjects; let i = index">
<td class="text-center"> {{i + 1}} </td>
<td> {{subject.name}} </td>
<td> {{subject.facilitator}} </td>
</tr>
</tbody>
</table>
</div>
You can use Object.values to turn the properties into an array. This can be done in several ways.
Method 1 - By applying the map operator to the observable data (see this stackblitz):
import { Observable } from "rxjs/Rx";
public subjects: Array<any>;
this.examinerService.getBatchSubjects(this.selectedBatch)
.map(x => (Object as any).values(x.json().subjects))
.subscribe(
(values) => { this.subjects = values; },
(error) => { console.log(error); }
);
Method 2 - With a property getter or a method of the component class (see this stackblitz):
get subjects(): Array<any> {
return (Object as any).values(this.selectedBatchData.subjects);
}
Method 3 - By converting the values in the subscribe callback (see this stackblitz):
public subjects: Array<any>;
...
this.selectedBatchData = response.json();
this.subjects = (Object as any).values(this.selectedBatchData.subjects);
Template
In the template, the ngFor directive would iterate over the subjects array:
<tr *ngFor="let subject of subjects; let i = index">
<td class="text-center"> {{i + 1}} </td>
<td> {{subject.name}} </td>
<td> {{subject.facilitator}} </td>
</tr>
try following code snippet.
ngFor="let subject of subjects|async
Update
In your ExaminerService you should import FirebaseListObservable in order to define return type FirebaseListObservable<any[]>
import { AngularFireDatabase, FirebaseListObservable } from 'angularfire2/database';
export class ExaminerService{
constructor(private db: AngularFireDatabase) {}
getBatchSubjects(batch){
return this.db.list('/subjects');
}
}
In your Component should look like this
export class ExaminerComponent implements OnInit {
movies: any[];
constructor(private examinerDb: ExaminerService) { }
ngOnInit() {
this.examinerDb.get().subscribe((snaps) => {
this.selectedBatchData = snaps;
});
}
}
Angular has pipe "keyvalue" which will let you parse your object into pairs like subject.key and subject.value

this.props.stats.map is not a function

In the console i am receiving back data from an API: https://github.com/SunDwarf/OWAPI/blob/master/api.md
but in the file where i am trying to render it to the page i am getting back an error : this.props.stats.map is not a function
previously i did it like this for a weather app and didn't have a problem so i am not sure where to look to figure this out.
*EDIT: added curly brackets to function mapStateToProps({ stats }) which now changes the error to "TypeError: Cannot read property 'achievements' of undefined"
does this mean the path to the info is incorrect?
Code:
import React, { Component } from 'react';
import { connect } from 'react-redux';
class OverwatchStats extends Component {
renderStats(achievementData) {
return (
<tr>
<td>{achievementData.data.eu.achievements.support}</td>
</tr>
);
}
render() {
return (
<table className="table table-hover">
<thead>
<tr>
<th>Hero Achievements</th>
<th>Got</th>
</tr>
</thead>
<tbody>
{this.props.stats.map(this.renderStats)}
</tbody>
</table>
);
}
}
function mapStateToProps({ stats }) {
return { stats };
}
export default connect(mapStateToProps)(OverwatchStats);
I have my code here: My Github
According to the api, stats is the object, not the array, so you can't map over it
Eldar G hit the nail right on the head.
Because stats is an object that outputs the following
var stats = {"stats": {
"typeofstat" : {},
"othertypeofstat": {}
...
}
You can do the following in order to iterate all keys of an object.
Object.keys(this.props.stats).map((key) => {
var currentStat = stats[key];
//do stuff with currentStat
});
Read https://github.com/SunDwarf/OWAPI/blob/master/api.md#get-apiv3ubattletagstats if you want more information on the type of keys that each stat contains. It seems your response will be a deeply nested object. Interpret and manipulate such an object according to your preferences and requirements.
Hope this helps. Remember, all iterator methods like map, forEach, reduce, etc... work only for arrays!

Is it possible to turn a filter on and off conditionally in AngularJS?

For example I have a table in which I am showing students marks of Math, Science, English and other subjects. And I have a checkbox which if checked will only list the students that have {{student.mathMarks + student.scienceMarks > 150}} (i.e. sum of student's math and science marks to be more than 150). And when the checkbox is unchecked it will again show all the students. Is there a way I could associate a conditional filter with the given students with ng-repeat to achieve this?
Following is the code relating to the case I tried explaining above:
<tr ng-repeat="student in students">
<td >
{{student.name}}
</td>
<td >
{{student.mathMarks}}
</td>
<td >
{{student.scienceMarks}}
</td>
<td >
{{student.englishMarks}}
</td>
</tr>
<input type="checkbox" ng-model="onlyFooStudents" />
You can pass a function as the expression for you filter. So in that case all you got to do is to declare a function in your scope that checks for the flag, something like:
$scope.yourCustomFilter = function(student) {
// if flag is false, bring everybody. if not, bring only the ones that match.
return $scope.onlyFooStudents ||
student && student.mathMarks + student.scienceMarks > 150;
};
And in your binding you will have ng-repeat="student in student | filter:yourCustomFilter".
Another way to implement it in case you're using the same filter somewhere else, is to create a custom filter and you can than pass in parameters, something in these lines:
angular.module('appFilters', []).filter('filterStudents', function() {
function isApproved(student) {
return student && student.mathMarks + student.scienceMarks > 150;
}
return function(students, showApprovedOnly) {
// if it is to bring everybody, we just return the original array,
// if not, we go on and filter the students in the same way.
return !showApprovedOnly ? students : students.filter(isApproved);
// COMPATIBLITY: please notice that Array.prototype.filter is only available IE9+.
};
})
Your binding you then be ng-repeat="student in students | studentFilter:onlyFooStudents". Notice that we pass in the onlyFooStudents as an argument to your filter, bound directly from the scope.
Create a filter that will receive an extra argument (that of the checkbox) to know when to conditionally enable/disable the filter.
function FooFilter() {
return function(data, isEnabled) {
var result = [];
if (angular.isArray(data) && isEnabled) {
angular.forEach(data, function(student) {
if (student.mathMark + student.scienceMark > 150) {
result.push(student);
}
});
return result;
} else {
return data;
}
}
}
<tr ng-repeat="student in students | fooFilter : filterState">
<td>...</td>
</tr>
...
<input type="checkbox" ng-model="filterState" /> Enable filter
Here is a working plunker.

Resources