React: Add Button to Component which Navigates to another page - react-router

I want to add Button to the Component which after click will navigate to another page.
Currently I am using react 18.0.
Bellow is the component:
import React from 'react';
import EmployeeService from '../Services/EmployeeService';
import { AddEmployee } from '../Functions/AddEmployee';
class ListEmployeeComponent extends React.Component {
constructor(props) {
super(props)
this.state={
employees: []
}
}
componentDidMount() {
EmployeeService.getEmployees().then((res) => {
this.setState({employees:res.data});
});
}
render() {
return (
<div>
<h2 className='text-center'>Employee List</h2>
<div className='row'>
<button className='btn btn-primary' onClick={AddEmployee.bind(this)}>Add Employee</button>
</div>
<div className='row'>
<table className='table table-striped table-bordered'>
<thead>
<tr>
<th>Employee First Name</th>
<th>Employee Last Name</th>
<th>Employee Email Name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{
this.state.employees.map(
employee =>
<tr key={employee.id}>
<td>{employee.firstName}</td>
<td>{employee.lastName}</td>
<td>{employee.email}</td>
</tr>
)
}
</tbody>
</table>
</div>
</div>
);
}
}
export default ListEmployeeComponent;
this is the button:
import { useNavigate } from 'react-router';
export function AddEmployee(a, b) {
let navigate = useNavigate();
return (
navigate('http://localhost:3000/add-employee')
);
}
And on the attached picture is an error which I am getting when I press button:
Erorr

As the error message says, React hooks need to be used from within a function component. The hook in question here is useNavigate, which you're calling from a regular function, hence the error. If you instead inject the navigate variable into the AddEmployee function, you shouldn't have a problem.
export function AddEmployee(navigate, a, b) {
navigate(...);
}
// then within the component, do something akin to
const navigate = useNavigate();
AddEmployee(navigate, a, b)

Okey after several hours of looking how to solve a problem I have made following
I removed Add Employee function complitely.
I imported Link from react-router-dom and change the Button to be Link to
import {React, Component} from 'react';
import {Link} from 'react-router-dom'
import EmployeeService from '../Services/EmployeeService';
class ListEmployeeComponent extends Component {
constructor(props) {
super(props)
this.state={
employees: []
}
}
componentDidMount() {
EmployeeService.getEmployees().then((res) => {
this.setState({employees:res.data});
});
}
render() {
return (
<div>
<h2 className='text-center'>Employee List</h2>
<div className='row'>
<Link to = '/add-employee' className='btn btn-primary' >Add Employee </Link>
</div>
<div className='row'>
<table className='table table-striped table-bordered'>
<thead>
<tr>
<th>Employee First Name</th>
<th>Employee Last Name</th>
<th>Employee Email Name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{
this.state.employees.map(
employee =>
<tr key={employee.id}>
<td>{employee.firstName}</td>
<td>{employee.lastName}</td>
<td>{employee.email}</td>
</tr>
)
}
</tbody>
</table>
</div>
</div>
);
}
}
export default ListEmployeeComponent;

Related

How can I make Angular model work in html page?

I connect with my service without any problem, then I import it into a model, but when I try to print the data with a for loop in the html page, it comes up blank. Where do you think I am doing wrong?
this is component file
import { Component, OnInit, ViewChild } from '#angular/core';
import { ResultsModel } from 'src/app/core/models/results.model';
#Component({
selector: 'app-results',
templateUrl: './results.component.html',
styleUrls: ['./results.component.scss']
})
export class ResultsComponent implements OnInit {
clientResults : ResultsModel[] = [];
constructor(private httpRequestService : HttpRequestService) {}
ngOnInit(): void {
this.getAllResults();
}
getAllResults(){
let apiEndpoint = "results"
this.httpRequestService.getApi(apiEndpoint, false).subscribe(resultRequest => {
this.clientResults = resultRequest
return this.clientResults
})
}
this is model file
export interface ResultsModel {
"result": string,
"createddate": string,
"clientid": string,
"id": number,
"clientusername": string,
}
this is html file
<table class="table align-middle table-nowrap" id="invoiceTable">
<thead class="text-muted">
<tr>
<th class="sort text-uppercase" data-sort="invoice_id">User Id</th>
<th class="sort text-uppercase" data-sort="customer_name">Username</th>
<th class="sort text-uppercase" data-sort="email">Type</th>
<th class="sort text-uppercase" data-sort="date">Type</th>
<th class="sort text-uppercase" data-sort="invoice_amount">Type</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let results in clientResults">
<td>{{results.id}}</td>
<td class="customer_name">{{results.username}}</td>
<td>{{results.clientid}}</td>
<td>{{results.result}}</td>
<td>{{results.createddate}}</td>
</tr>
</tbody>
</table>
Where do you think I went wrong? Where could I be doing wrong?

Angular 6 DataTables - Table Items Appear/Disappear Upon Sort or Search

I am using Angular-DataTables 6.0 (found here https://l-lin.github.io/angular-datatables/#/welcome) and I have been running into a reoccurring problem. When a table item is added or deleted, the record either vanishes or reappears upon sorting or searching. Is this possibly because the adding/deleting is occurring from outside the dataTable?
I've tried adding the ' datatable="ng" ' fix many others suggested but that didn't change anything. I also attempted to add a re-render function, but in that case I ran into 'object unsubscribed' errors that I couldn't resolve. For reference, some similar problems can be found here: Similar examples include: (angular : lost data after sort datatable)
(Sorting of numbers within brackets in angular datatables)
(https://github.com/l-lin/angular-datatables/issues/852)
Here is my code:
HTML:
<table datatable="ng" [dtOptions]="dtOptions" [dtTrigger]="dtTrigger" class="table table-hover" id="t1">
<thead>
<tr>
<th>
<button id="b5">Asset ID</button>
</th>
<th>
<button id="b5">Employee ID</button>
</th>
<th>
<button id="b5">Asset Type</button>
</th>
<th>View</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let a of assets; let i = index">
<td>{{ a.assetID }}</td>
<td>{{ a.currentUser }}</td>
<td>{{ a.assetType }}</td>
<td><button id="button1" (click)="viewAsset(a)"><i class="fas fa-search"></i></button></td>
<td><button id="b2" class="btn" (click)="scrapAsset(a)" (mouseenter)="buttonHover(i)" (mouseleave)="buttonHoverLeave(i)"><i class="{{buttonIconArray[i].text}}"></i></button></td>
</tr>
</tbody>
</table>
-elsewhere in the code-
<button class="btn" id="b1" (click)="addAsset()">Add New Asset</button>
TS
dtOptions: DataTables.Settings = {};
dtTrigger = new Subject();
addAsset()
{
this.confirmChanges = false;
//Create a new asset:
let a: Asset = {
assetID: this.assetID,
currentUser: this.currentUser,
assetType: this.dropdownMessage,
}
//send a notification to the user that owns the new asset
let notify: Notice = {
emplyID: a.currentUser,
notificationSource: "Asset",
details: "A new " + this.dropdownMessage + " was added to your assets.",
timeStamp: new Date()
}
//Add the asset to the server
this.AssetsService.addAsset(a)
.subscribe(asset => { this.assets.unshift(asset);
//All of the inputs need to be emptied
this.clearFields();
})
}
scrapAsset(a: Asset)
{
console.log("The ID is " , a.assetID)
//this.AssetsService.deleteAsset(this.currentAsset).subscribe()
this.confirmChanges = false;
//This deletes the asset from the back-end.
this.AssetsService.deleteAsset(a).subscribe(() => {
console.log('test')
this.assets = this.assets.filter(t => t !== a);
this.NotificationsService.addNotice(notify).subscribe();
this.clearFields(); });
}
dtActivate()
{
this.dtOptions = {
pagingType: 'full_numbers',
pageLength: 7,
order: (this.assets.assetType),
orderClasses: false,
};
this.AssetsService.getAssetsIT().subscribe((assetsImported) => {
this.assets = assetsImported;
this.parseData();
// Calling the DT trigger to manually render the table
this.dtTrigger.next();
});
}
This is only a partial section of the code, so if you need to see more, just ask. Thank you for any help you might provide!
I've had the same problem for a long time. After a lot of stack overflow and documentation, I found a solution that fixed my problem. Maybe it helps you too.
Allow datatable to be destroyed
ngOnInit() {
this.dtOptions = {
destroy: true,
...
};
...
}
The method that received new items (called after edit, insert, add...).
onReceived(items){
let isFirst = this.items.length === 0;
this.items = <itemsModel[]>items;
if (isFirst)
this.dtTrigger.next();
else
this.rerender();
}
The rerender as in documentation
rerender(): void {
this.dtElement.dtInstance.then((dtInstance: DataTables.Api) => {
dtInstance.destroy();
this.dtTrigger.next();
});
}
https://l-lin.github.io/angular-datatables/#/advanced/rerender
I hope that this might help you.
this will work, but i am still looking for better solutions
setTimeout(function () {$(function () {$('#ng').DataTable();});}, 10);
You can remove the ng, Following code fixed my problem
<div class="container-fluid">
<div class="page-title">
<h4>Milk Types</h4>
</div>
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-block">
<a href="" class="btn btn-success" href='' [routerLink]="['/milk/create-milk']">
<i class="ti-zip pdd-right-5"></i>
<span>Add New Milk</span>
</a>
<div class="table-overflow">
<table class="table table-lg table-hover" datatable [dtTrigger]="dtTrigger" [dtOptions]="dtOptions">
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Status</th>
<th>Price</th>
<th></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let drink of data">
<td>
<div class="mrg-top-15">
<b class="text-dark font-size-16">{{ drink.id }}</b>
</div>
</td>
<td>
<div class="mrg-top-15">
<span class="text-info pdd-left-20"><b>{{ drink.name }}</b></span>
</div>
</td>
<td>
<div class="col-md-5">
<div class="toggle-checkbox toggle-success checkbox-inline">
<input type="checkbox" name="isEnabled" id="toggle4" [checked]="drink.isEnabled">
<label for="toggle4"></label>
</div>
</div>
</td>
<td>
<div class="mrg-top-15">
<span class="text-info"><b>KD {{ drink.price }}</b></span>
</div>
</td>
<td>
<div class="mrg-top-10 text-center">
<a href="" class="btn btn-warning">
<i class="ti-comment pdd-right-5"></i>
<span>Edit</span>
</a>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
import { Component, OnInit, Input, OnDestroy , ViewEncapsulation } from '#angular/core';
import { AngularFireDatabase } from 'angularfire2/database';
import { pipe, Subscription } from 'rxjs';
import { MilksItem, MilksDataSource } from './milks-datasource';
import { Subject } from 'rxjs';
#Component ({
templateUrl: 'milk.html'
})
export class MilkComponent implements OnInit {
dtOptions: DataTables.Settings = {};
subscription: Subscription;
data: MilksItem[] = [];
dtTrigger: Subject<MilksItem> = new Subject();
constructor(private db: AngularFireDatabase)
{
}
ngOnInit() {
this.dtOptions = {
pagingType: 'full_numbers'
};
this.subscription = this.db.list<MilksItem>('milkTypes').valueChanges().subscribe(d=>{
console.log('data streaming');
this.data = d;
this.dtTrigger.next();
});
}
ngOnDestroy(): void {
this.subscription.unsubscribe();
this.dtTrigger.unsubscribe();
}
}
Another Working Solution
Just move your data-table inside a separate component
AND
Provide your new options/data as an Input()
import { AfterViewInit, Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild } from '#angular/core';
import { DataTableDirective } from 'angular-datatables';
import { Subject } from 'rxjs';
#Component({
selector: 'app-datatable',
templateUrl: './datatable.component.html',
styleUrls: ['./datatable.component.scss']
})
export class DatatableComponent implements OnInit, OnDestroy, OnChanges, AfterViewInit {
#ViewChild(DataTableDirective, { static: false }) dt!: DataTableDirective;
#Input() dtOptions: DataTables.Settings = {};
dtTrigger: Subject<any> = new Subject<any>();
constructor() { }
private _initComponent() {
this.dtTrigger.next(this.dtOptions);
}
ngOnInit(): void {
}
ngAfterViewInit(): void {
this._initComponent();
}
ngOnChanges(changes: SimpleChanges): void {
}
ngOnDestroy(): void {
this.dtTrigger.unsubscribe();
}
}
<table #dtA datatable [dtOptions]="dtOptions" [dtTrigger]="dtTrigger"
class="custom-dt-1 table table-sm table-striped card-table w-full row-border hover">
</table>

React js iteration of JSON array is not working

i am unable to repeat the row dynamically. when i am using .map method it is showing .map is not a function.
Component
import React, {Component} from 'react';
const pannelWidth = {
width: '90%'
};
const pannelHeader = {
color: 'white'
};
class ProjectList extends Component {
constructor(props) {
super(props);
this.state = {
projectList : ''
}
//this.deleteProjectMessage = this.deleteProjectMessage.bind(this);
}
componentDidMount() {
let d = '';
$.get("http://localhost:8008/api/navigation/all", function (data) {
d = data;
this.setState({
projectList: d
});
}.bind(this));
console.log(this.state.projectList);
}
render() {
var listItems = this.state.projectList.map(function (item, index) {
return <tr>
<td>{item.name}</td>
<td>{item.description}</td>
<td><i className="glyphicon glyphicon-trash"></i></td>
</tr>
});
return(
<div className="container" style={pannelWidth}>
<br/>
<div className="panel panel-primary">
<div className="panel-heading">
<div className="row">
<div className="col-md-2 col-lg-2">
<h4 style={pannelHeader}>Project List</h4>
</div>
<div className="col-md-offset-8 col-lg-offset-8 col-md-2 col-lg-2">
<button className="btn btn-sm btn-success">Create New Project</button>
</div>
</div>
</div>
<div className="panel-body">
<table className="table table-striped">
<thead>
<tr>
<th>Project Name</th>
<th>Description</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{listItems}
</tbody>
</table>
</div>
</div>
</div>
);
}
}
export default ProjectList;
JSON
[
{
"name": "pro",
"description": "Testing of pro"
},
{
"name": "Test",
"description": "Testing of Test"
}
]
i am calling api from my local system and getting above response and updating the state. then i am trying to render in side table row using map() but it is showing map is not a function in console.
you are defaulting projectList to a string, default it to an empty array.
this.state = {
projectList : '' // should be projectList: []
}
You are making an async request so the initial render of the component is trying to call map on the initial state which is
''.map(function (item, index) {
return <tr>
<td>{item.name}</td>
<td>{item.description}</td>
<td><i className="glyphicon glyphicon-trash"></i></td>
</tr>
});
If the data comes back as a string, you can't map it. You first need to parse the JSON text into a JavaScript array.
d = JSON.parse(data);

Angular 4 : Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays

I want to show the data from my backend (CodeIgniter) to my frontend (Angular 4),
but this error come up:
ERROR Error: Cannot find a differ supporting object '[object Object]' of type > 'object'. NgFor only supports binding to Iterables such as Arrays.
This is my component:
import { BookService } from './../book.service';
import { Component, OnInit } from '#angular/core';
import { Book } from "../book";
#Component({
selector: 'app-book',
templateUrl: './book.component.html',
styleUrls: ['./book.component.css']
})
export class BookComponent implements OnInit {
constructor(public bookService: BookService ) { }
ngOnInit() {
this.getBooks();
}
books:Book;
getBooks(){
this.bookService.getBooks()
.subscribe(books=>{
this.books = books;
})
}
}
This is my service:
import { Injectable } from '#angular/core';
import { Http } from '#angular/http';
import 'rxjs/add/operator/map' ;
#Injectable()
export class BookService {
constructor(private http: Http) {
}
books=[];
getBooks(){
return this.http.get("http://localhost/restserver/book/list_book.php")
.map(res => res.json());
}
}
My .json:
{"status":true,"data":
[{"id":"1","title":"SAINS","author":"ERLANGGA","isbn":"089928778"},
{"id":"2","title":"Geography","author":"ERLANGGA","isbn":"089182372"},
{"id":"3","title":"Math","author":"Srilangka","isbn":"091283181"},
{"id":"4","title":"test","author":"test","isbn":"1283798127"},
{"id":"5","title":"AAAA","author":"BBB","isbn":"91092301290"},
{"id":"6","title":"BBB","author":"CCC","isbn":"01920192"}]}
And this is my view:
<div class="container-fluid">
<div class="row">
<div class="card col-md-7">
<div class="card-body">
<table class="table table-responsive table-striped">
<caption>List of Books</caption>
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Title</th>
<th scope="col">Author</th>
<th scope="col">ISBN</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let book of books" >
<th></th>
<td>{{book.title}}</td>
<td>{{book.author}}</td>
<td>{{book.isbn}}</td>
<td>
<button class="btn btn-primary "> Detail </button>
<button class="btn btn-success " [routerLink]="['/book-edit']"> Edit </button>
<button class="btn btn-danger "> Delete </button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<app-book-add class="col-md-5"></app-book-add>
Issue :
You are assigning the whole response to books , but all you need is
data.
1) First way to solve it
Change this line from in component:
this.books = books;
to
this.books = books.data;
2) Or you can also do this in template:
Change this :
<tr *ngFor="let book of books" >
to
<tr *ngFor="let book of books?.data" >
books should be collection of Book & then set data property of returned response.
books:Book[]; //this should be change inside Component.
//service method should return data prop from it.
getBooks(){
return this.http.get("http://localhost/restserver/book/list_book.php")
.map(res => res.json().data);
}

Can't iterate React component to create table. Unexpected token

I'm trying to create a table filled by a REST API using ReactJS. The problem is I don't know what I'm doing wrong with the sintax... I'm using ES6.
This is the FooTable:
import React from 'react'
import Foo from './Foo'
export default class FooTable extends React.Component {
render() {
return(
<tr>
<td>id</td>
<td>name</td>
<td>surname</td>
</tr>
{ //<-- This is the marked error by webpack
this.props. Foos.map( foo=>{
return < Foo key={foo.id} name={foo.name} surname={foo.surname}/>
})
}
)
}//end-render
}
This is the Foo class:
import React from 'react'
export default class Foo extends React.Component {
render() {
return <tr>
<td>{foo.name}</td>
<td>{foo.surname}</td>
</tr>
}
}
This is the main render:
render(){
if (this.state.foos.length > 0) {
console.log('Foos size ' + this.state.foos.length);
return <table>
<tbody>
<FooTable foos={this.state.foos}/>
</tbody>
</table>
} else {
return <p className="text-center">Loading Foos...</p>
}
}
Webpack marks an error in FooTable (Unexpected Token). It's marked by a comment.
you need to return single node from your component's (FooTable in this case )render method.
render() {
return(
<tr>
<td>id</td>
<td>name</td>
<td>surname</td>
</tr>
{ //<-- This breaks the single root
this.props.Foos.map( foo=>{
return < Foo key={foo.id} name={foo.name} surname={foo.surname}/>
})
}
)
}
you need to do sth like this :
render() {
return (
<tbody>
<tr>
<td>id</td>
<td>name</td>
<td>surname</td>
</tr>
{this.props.Foos.map(foo => (<Foo key={foo.id} name={foo.name} surname={foo.surname}/>))}
</tbody>)
}
The documentation says (see here) :
Note:
One limitation: React components can only render a single root
node. If you want to return multiple nodes they must be wrapped in a
single root.
You need to wrap your <tr> and your <Foo>elements in a single node, for example a <div>:
render() {
return(
<div>
<tr>
<td>id</td>
<td>name</td>
<td>surname</td>
</tr>
{ //<-- No more multiple roots
this.props.Foos.map( foo => {
return < Foo key={foo.id} name={foo.name} surname={foo.surname}/>
}
</div>
)
}
Hope this helps