Carry data from dynamicaly generated table to the next page - html

Probably a noob question but I couldn't find an answer anywhere I searched. So basically I'm generating a table made of data I download from Firebase when the page is accessed, and I'd like the user to be able to click on one row of the table to access the precise details of the clicked element. It's kinda confusing to explain but it'll be much clearer with the code below.
Here is the BooksComponent typescript:
import { Component, OnInit } from '#angular/core';
import {AngularFirestore} from '#angular/fire/firestore';
import {Book} from '../../services/book';
#Component({
selector: 'app-books',
templateUrl: './books.component.html',
styleUrls: ['./books.component.css']
})
export class BooksComponent implements OnInit {
private books: Array<Book> = [];
constructor(private db: AngularFirestore) { }
ngOnInit() {
this.db.collection('books').get().subscribe((querySnapshot) => {
querySnapshot.forEach((doc) => {
this.books.push({
id: doc.id,
title: doc.get('title'),
writer: doc.get('writer'),
reserved: doc.get('reserved')
});
// console.log(doc.data());
});
});
}
}
And here is the associated HTML:
<div class="container d-flex min-vh-100 justify-content-center mt-5">
<table class="table table-bordered table-hover table-active">
<thead class="thead-dark">
<tr>
<th scope="col">Title</th>
<th scope="col">Writer</th>
<th scope="col">Reserved</th>
<!--<th scope="col">ID</th>-->
</tr>
</thead>
<tbody>
<!-- Here I'm putting on screen the array I populated back in the typescript, the [routerLink]=" [book.id]" seems to be working fine since in the detail page the id does update, everything else stays at 'dummy'-->
<tr *ngFor="let book of books;" [routerLink]="[book.id]" style="cursor: pointer">
<td>{{book.title}}</td>
<td>{{book.writer}}</td>
<td>{{book.reserved}}</td>
<!--<td>{{book.id}}</td>-->
</tr>
</tbody>
</table>
</div>
Now here is the BookDetailComponent typescript:
import { Component, OnInit } from '#angular/core';
import {Book} from '../../services/book';
import {ActivatedRoute} from '#angular/router';
#Component({
selector: 'app-book-detail',
templateUrl: './book-detail.component.html',
styleUrls: ['./book-detail.component.css']
})
export class BookDetailComponent implements OnInit {
private book: Book = {
id: 'dummy',
title: 'dummy',
writer: 'dummy',
reserved: 'dummy'
};
constructor(private route: ActivatedRoute) { }
ngOnInit() {
this.book.id = this.route.snapshot.params.id;
}
}
I managed to update the 'id' field according to which row the user clicked on, it's also correctly updated in the Web Page URL, but that's about it. I'm thinking I could search and retrieve informations corresponding to the id in the Array that stores all the books but I'm not sure how to do that excatly. Any advice/help is greatly appreciated.

So technically for the list, you should have an API and for the individual id route also you should have another API which gives you more details. But since, you don't have such an API that gives details based on id, you could make use of a service.I would not recommend this, but it will work. When you click on a book, update a service function with the index and get the corresponding details in bookdetail component.
bookArr: Array<object>
setBookDetails(arr) {
this.bookArr = arr;
}
updateIndex(idx) {
this.detailIdx = idx;
}
getDetails(){
return this.bookArr[this.detailIdx]
}

Related

I'm trying to do a http get request to get data from my backend Larval, MYSQL I have created the API in Laravel but cant display the data in Angular

I'm new to programming and new advise please here is my code
employees component
import { DataService } from './../../service/data.service';
import { Component, OnInit } from '#angular/core';
#Component({
selector: 'app-employees',
templateUrl: './employees.component.html',
styleUrls: ['./employees.component.css'
]})
export class EmployeesComponent implements OnInit {
employees: any
constructor(private dataservice: DataService) { }
ngOnInit() {
}
getEmployeesData() {
this.dataservice.getData().subscribe((res: any) => {
this.employees = res;
});
}
}
data.service.ts
import { Injectable } from '#angular/core';
import { HttpClient } from '#angular/common/http';
#Injectable({
providedIn: 'root'
})
export class DataService {
getData: any;
constructor(private httpClient: HttpClient) {
this.httpClient.get('http://127.0.0.1:8000/api/employees')
};
}
HTML
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Name</th>
<th scope="col">Surname</th>
<th scope="col">Email</th>
<th scope="col">Salary</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let emp of employees">
<th scope="row">{{emp.id}}</th>
<td>{{emp.name}}</td>
<td>{{emp.surname}}</td>
<td>{{emp.email}}</td>
<td>{{emp.salary}}</td>
</tr>
</tbody>
I see a couple of things, that cause the Data not to show up.
data.service.ts:
Your fetching the Data in the Constructor but you do ignore the return Value of your call. I suggest you do the call in a separate function that will be called if the data is needed.
constructor(private httpClient: HttpClient) {}
loadAllEmployees(): Observable<any> {
return this.httpClient.get('http://127.0.0.1:8000/api/employees');
}
Employees Component:
It seems, that you do not call the getEmployeesData function. Therefore your employees never get any Data.
employees: any
constructor(private dataservice: DataService) { }
ngOnInit() {
this.getEmployeesData();
}
getEmployeesData() {
this.dataservice.loadAllEmployees().subscribe((res: any) => {
this.employees = res;
});
}
With this setup, your data should load and be displayed.
Besides that, I would suggest you define the Type of the Employee and do not work with any. It does make it easier to read and understand your code. You are also less likely to get any Errors regarding types.

How to bind Json response to table

I have products.components.ts class, where I am getting Json data in this.Getdata
ngOnInit() {
this._service.getProducts(this.baseUrl)
.subscribe(data => {
this.Getdata=data
this.products=data
alert(JSON.stringify(this.Getdata));
});
This Josn data I want to bind in products.components.html class Table
<p>
Product List
</p>
<table>
<th>Id</th> <th>Name</th> <th> Country</th> <th>Actions</th>
<tr *ngFor="let lst of products; let i = index" border="1">
<td>{{i+1}}</td><td>{{lst.id}}</td><td>{{lst.employee_name}}</td> <td>Edit</td>
</tr>
</table>
The above code is not working. Only alert displaying. How can I bind data to table?
This is my Json data
[{"id":"1","employee_name":"amit","employee_salary":"0","employee_age":"0","profile_image":""},{"id":"247793","employee_name":"Ana","employee_salary":"123","employee_age":"123","profile_image":""},{"id":"247856","employee_name":"Joseph Beltran","employee_salary":"1000","employee_age":"23","profile_image":""},{"id":"247982","employee_name":"testyeyyeye1","employee_salary":"123","employee_age":"23","profile_image":""},{"id":"248007","employee_name":"test100","employee_salary":"123","employee_age":"23","profile_image":""},{"id":"248038","employee_name":"Hendry","employee_salary":"61888","employee_age":"26","profile_image":""}]
Model class
export class Productlist {
id: string;
employee_name: string;
employee_salary: string;
employee_age: string;
profile_image: string;
}
Instead of subscribing to the Observable, consider storing it in a property and then unwrapping it in the template using the async pipe.
If you consider that, then you can significantly reduce your Component to this:
import { Component } from '#angular/core';
import { Observable } from 'rxjs';
import { EmployeeService } from './employee.service';
#Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: [ './app.component.css' ]
})
export class AppComponent {
employees$: Observable<Array<any>> = this.employeeService.getEmployees();
constructor(private employeeService: EmployeeService) {}
}
And in your Template:
<p>
Employee List
</p>
<table border="1">
<thead>
<th>Id</th>
<th>Name</th>
<th> Country</th>
<th>Actions</th>
</thead>
<tbody>
<tr *ngFor="let employee of employees$ | async; let i = index">
<td>{{i+1}}</td>
<td>{{employee.id}}</td>
<td>{{employee.employee_name}}</td>
<td>Edit</td>
</tr>
</tbody>
</table>
Here's a Working Sample Demo Code for your ref.
PS: Consider naming your properties and methods appropriately. If you're working with employees, then it doesn't really make sense to name properties and methods as products.
You can also use
const myobject= JSON.parse(yourjsonvalue);
You will get the object in myobject and now you can loop through with simple ngFor.
I got it this way
export class ProductsComponent implements OnInit {
public Getdata;
products:Productlist[];
constructor(private employeeService: ProductService) {}
ngOnInit() {
this.employeeService.getProducts(this.baseUrl)
.subscribe((data:any) => {
this.products=data;
});
}
}

Data NOT refreshing onSubmit Form and route change, Angular 6

I can't figure out why the data does NOT update in my table, when I make a change (post, delete, etc.) to the SQL database. I have to refresh the browser every time to see the update in the table.
This is a simple list that gets updated when submitting a form with changes. At the end of the onSubmit function, it routes back to fleet-home, but the table shows the old data (until browser refresh). No errors.
I feel like I'm missing a major Angular step here (still new to Angular).
I've tried adding "...this.ngZone.run(...).." to the router navigate, but it's not refreshing the data.
I read that "...this.route.paramMap.subscribe(...)" can listen for data changes, but it's not working (perhaps I'm not using it correctly).
import { Component, OnInit } from '#angular/core';
import { ActivatedRoute, Params, Router } from '#angular/router';
import { FleetService } from 'src/app/fleet.service';
import { NgForm } from '#angular/forms';
#Component({
selector: 'app-fleet-home',
templateUrl: './fleet-home.component.html',
styleUrls: ['./fleet-home.component.css']
})
export class FleetHomeComponent implements OnInit {
public assignmentList: Array<any>;
public vehicleList: Array<any>;
id: number;
editMode = false;
constructor(
public route: ActivatedRoute,
public router: Router,
private fleetService: FleetService
) {
fleetService.getVehicleList().subscribe((importVehicleList: any) => this.vehicleList = importVehicleList);
fleetService.getAssignmentList().subscribe((importAssignmentList: any) => this.assignmentList = importAssignmentList);
}
ngOnInit() {
this.route.paramMap.subscribe(map => {
// adding this did not help, not sure if this is correct
this.fleetService.getAssignmentList().subscribe((importAssignmentList: any) => this.assignmentList = importAssignmentList);
});
}
}
Component with onSubmit...
onSubmit(form: NgForm) {
const value = form.value;
const assigmentAdd = form.value;
var newAssignmentObject = {
Id: assigmentAdd.Id,
CameraId: assigmentAdd.CameraId,
VehicleId: assigmentAdd.VehicleId,
DateCreated: assigmentAdd.DateCreated,
Deleted: assigmentAdd.Deleted
}
this.fleetService.addAssignment(newAssignmentObject).subscribe
(data => {
this.ngZone.run(() => this.router.navigate(['/fleet-home'])).then();
});
}
fleet-home HTML table...
<table class="table table-hover" id="searchTable">
<thead>
<tr>
<th>Date Created</th>
<th>Vehicle Assigned to</th>
<th>Camera</th>
<th>Edit and/or Delete Assignment</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let mat of assignmentList; let in = index" style='text-align:left;'>
<td *ngIf="mat.deleted === false">{{ mat.dateCreated | date: 'medium' }} </td>
<span *ngFor="let veh of vehicleList">
<td *ngIf="mat.vehicleId === veh.id && mat.deleted === false"> {{ veh.name }} </td>
</span>
<td *ngIf="mat.deleted === false">{{ mat.cameraId }} </td>
<td *ngIf="mat.deleted === false"> <a [routerLink]="['/fleet-home/fleet-edit/', mat.id]">EDIT or
DELETE</a> </td>
</tr>
</tbody>
</table>
You have to just call the function that fetch the records.
This is Not Possible in angular2, reason is once you load any component that component is only reload once you navigate from another route to that route again.
But you can reload forcefully
Basically there are two methods which are
you can call ngOnInit() method again and again as per need, which is not a recommended way
you can call one commonn method from ngOnInit() and later as per need call that function again like this
ngOnInit(){
this.callingFunction();
}
forLaterUse(){
this.callingFunction();
}

Angular 6 Datatable refresh data in table after outside filtering

i'm trying to render the rows in the table after request.
i can see the the response in the browser and i am pushing the new data to the
ROWS: object,
but the table is not refreshing the rows in table.
sample code:
import {Component, OnDestroy, OnInit} from '#angular/core';
import { Subject } from 'rxjs';
import { XXXXXXXService } from './NAME.service';
#Component({
selector: 'app-NAME',
templateUrl: './NAME.component.html',
styleUrls: ['./NAME.component.scss']
})
export class XXXXXXXComponent implements OnDestroy, OnInit {
dtOptions: DataTables.Settings = {};
rows = [];
dtTrigger: Subject<any> = new Subject();
constructor(private XXXXXXXService: XXXXXXXService) { }
ngOnInit(): void {
this.rows.length = 0;
this.dtOptions = {
pagingType: 'full_numbers',
pageLength: 25
};
this.class.getRows()
.subscribe(rows => {
console.log(rows);
this.rows = rows;
// Calling the DT trigger to manually render the table -- not working!!!
this.dtTrigger.next();
});
}
render_rows_filters(filter) {
this.class.getRowsFiltered(filter).subscribe(
rows => {
this.rows = rows;
this.dtTrigger.next();
}
);
}
ngOnDestroy(): void {
this.dtTrigger.unsubscribe();
}
}
html
<table datatable [dtOptions]="dtOptions" [dtTrigger]="dtTrigger" class="table-bordered">
<!--class="row-border table-bordered hover">-->
<thead>
<tr>
<th> NAME</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let row of rows">
<td>{{ row.Name }}</td>
</tr>
</tbody>
</table>
Ok,
the thing is that when you use angular with components and services, one component can't declare the DataTable Element to another component running in the same page.
the solution was to create a outside (Global) service and pass all the component variables.
then running all the functions in the Service with the component variables and child's.
Thank you all... =)
This is best way to refresh data on search .. or dynamic loading
angular-datatables/#/advanced/rerender
datatable refresh
Click on button press table grid refresh
$('#Clienttbl').dataTable().fnClearTable();

How to share the http response between two components in angular 6?

I have a component named modal. In this model ts file, Im making a http request to get json data from node js. After retrieving Im just displaying it in a table format. My table structre is like below.
modalId modalName subModal AddSubmodal
111 modal1 add
112 modal2 add
The problem here is after clicking the add button, one pop up box will come(Another component) asking us to enter sub model name. There we should display the modal name for which we have to enter sub model details.
So After clicking the add button, I need to pass the modalId to fetch the model details. So here I need to pass the modalID dynamically to the addmodel(second) component. Can anybody tell me how to do this?
My modal.component.ts:
#Component({
selector: 'app-modal',
templateUrl: './modal.component.html',
styleUrls: ['./modal.component.css']
})
export class ModalComponent extends Lifecycle {
public d: any;
constructor(
private $modal: $ModalManagerService,
private http: HttpClient,
) {
super();
}
_initialize(): void {
this.http.get('/getModel',{responseType:"json"}).subscribe(
response => {
this.data = response;
var sample=JSON.stringify(response);
});
}
addSubmodal(modalId){
let obj = this.$modal.show(AddSubModalComponent)
.subscribe(r => {
obj.unsubscribe();
});
};
My modal html:
<table class="table table-bordered" >
<thead>
<tr>
<th>modal ID</th>
<th>modal Name</th>
<th>SubModal</th>
<th>AddSubmodal</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let i of d">
<td>{{ i.Record.modalId }}</td>
<td>{{ i.Record.modalName }}</td>
<td></td>
<td>
<img src="./../Add_Icon.svg" (click)="addSubmodal(i.Record.modalId);">
</td>
</tr>
</tbody>
</table>
As I'm new to angular, Im just browsing angular answers in stackoverflow & doing it. Please tell me how to achieve this in my second component html file?
Use Input & Output Decorators
Basic concept ---> DEMO
app.component.html:
<app-component1 (elm)="catch1Data($event)">
</app-component1>
<app-component2 [elm]="datatocomp2" *ngIf="datatocomp2"></app-component2>
parent component : {{datatocomp2 | json}}
app.component.ts:
datatocomp2: any;
catch1Data(data) {
console.log(data)
this.datatocomp2 = data;
}
component1.ts:
#Output () elm : EventEmitter<any> = new EventEmitter<any>();
objectData: any;
constructor() { }
ngOnInit() {
let objectData = {
comp: 'component 1',
data: 'anything'
}
this.objectData = objectData;
this.elm.emit(objectData)
}
component2.ts:
#Input() elm: any;
constructor() { }
ngOnInit() {
console.log(this.elm);
}
Use a shared service to share data between components, event emitter method isn't the best way for components in routing
Simple example , create a datashare service
//DataShare Service
import { ReplaySubject } from 'rxjs/Rx';
export class DatashareService {
private dataObs$ = new ReplaySubject<any>(1);
getData() {
return this.dataObs$.asObservable();
}
updateData(data) {
this.dataObs$.next(data);
}
constructor() { }
}
In the component where you fetch data,
import { DatashareService } from '../datashare.service';
update the value of the observable
this.dataService.updateData(this.modal_id);
Subscribe to the observable from the sub components
import { Component, OnInit,OnDestroy,NgZone } from '#angular/core';
import { DatashareService } from '../datashare.service';
import { Subscription } from 'rxjs/Subscription';
In the constructor,
constructor(private dataService: DatashareService
,private zone:NgZone){}
Now, access data on init,
ngOnInit() {
this.subscription.add(this.dataService.getData().subscribe(data => {
this.zone.run(()=>{
console.log(data);
this.modal_selected=data;
})
}))
}
Don't forget to unsubscribe on destroy
ngOnDestroy() {
// unsubscribe to ensure no memory leaks
this.subscription.unsubscribe();
}