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();
}
Related
I have a question about a get and put request using angular.
I know how I can do a get or put request when I for example click on a button. For that, I just have to bind my request to the button. But now I want to be able to make a get request for example without some button. that means ich enters for example a barcode and just clicks on my keyboard on entering and releases my get request.
A normal code for a get look for example like:
this.codeService.getBarcode(barcode).subscribe(_=>
...
)
my question is what do I have to do in the subscribe to be able to do this get request if I don't have some button to click
If you want to get data when you load the app without clicking but in OnInit time.
You can write direct in your component but that is not a clean code.
You better write a service which will hold get and put.
Here is a little example.
Here you have get, post, put and delete
export class EmployeeService {
private baseUrl = "http://localhost:8081/rest/user";
constructor(private http: HttpClient) { }
getEmployee(id: number): Observable<any> {
return this.http.get(`${this.baseUrl}/${id}`);
}
createEmployee(employee: object): Observable<object> {
return this.http.post(`${this.baseUrl}`, employee);
}
updateEmployee(id: number, value: any): Observable<object> {
return this.http.put(`${this.baseUrl}/${id}`, value);
}
deleteEmployee(id: number): Observable<any> {
return this.http.delete(`${this.baseUrl}/${id}`);
}
getEmployeesList(): Observable<any> {
return this.http.get(`${this.baseUrl}`);
}
}
You need then to add HttpClientModule in the app.module.ts
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
HttpClientModule <-- This here
],
You can have an interface like this.
export class Employee {
id: number;
name: string;
age: number;
}
Your component ts
export class EmployeeListComponent implements OnInit {
employees: Observable<Employee[]>;
constructor(private employeeService: EmployeeService,
private router: Router) {}
ngOnInit() {
this.reloadData();
}
reloadData() {
this.employees = this.employeeService.getEmployeesList();
}
deleteEmployee(id: number) {
this.employeeService.deleteEmployee(id)
.subscribe(data => console.log(data, id));
this.reloadData();
}
employeeDetails(id: number) {
this.router.navigate(['details', id]);
}
}
Your component HTML
<div class="panel panel-primary">
<div class="panel-heading">
<h2>Employee List</h2>
</div>
<div class="panel-body">
<table class="table table-striped">
<thead>
<tr>
<th>Firstname</th>
<th>Lastname</th>
<th>Email</th>
<th>Description</th>
<th>City</th>
<th>Extra</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let employee of employees | async">
<td>{{employee.name}}</td>
<td>{{employee.age}}</td>
<td><button (click)="deleteEmployee(employee.id)" class="btn btn-danger">Delete</button>
<button (click)="employeeDetails(employee.id)" class="btn btn-info" style="margin-left: 10px">Details</button>
</td>
</tr>
</tbody>
</table>
</div>
</div>
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]
}
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();
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();
}
In my angular 2 application Headers are dynamically setting up. I arranged my JSON data as shown in attached image. I want to filter entire data using the Table Heading as a search term (Ex: Name or Position or Extn).
I am separating my result data to keys and values like:
this.keys= Object.keys(this.ResultData_search[0])` // my table heading false here
in my template :
<table>
<tr>
<th *ngFor=" let key of keys,let i=index"></th>
</tr>
<tr *ngFor=" let res of ResultData_search ,let i = index" style="height: 35px;">
<td>{{i+1}}</td>
<td *ngFor=" let key of keys ">
{{res[key]}}
</td>
</tr>
</table
Why I am doing this is because my JSON data is not static. I have to set table headings and data dynamically.
You have to use a Pipe with an Array parameter containing the filters for each header.
In the Pipe you can dynamically filter your data for each header.
Let's assume you have the following component with a similar data structure as yours. It has a filters Array of type IFilter :
import { Component, OnInit, ChangeDetectionStrategy } from '#angular/core';
#Component({
selector: 'app-filter',
templateUrl: './filter.component.html',
styleUrls: ['./filter.component.css'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class FilterComponent implements OnInit {
data: Array<Map<string, string>>;
headers: Array<string>;
filters: Array<Ifilter>;
constructor() {
}
ngOnInit() {
this.data = new Array(
new Map([["header1", "value11"], ["header2", "value12"], ["header3", "value13"], ["header4", "value14"], ["header5", "value15"]]),
new Map([["header1", "value21"], ["header2", "value22"], ["header3", "value23"], ["header4", "value24"], ["header5", "value25"]]),
new Map([["header1", "value31"], ["header2", "value32"], ["header3", "value33"], ["header4", "value34"], ["header5", "value35"]])
);
this.headers = Array.from((this.data[0]).keys());
this.filters = new Array({header:"header1",filter:""},{header:"header2",filter:""},{header:"header3",filter:""},{header:"header4",filter:""},{header:"header5",filter:""})
}
}
export interface Ifilter{
header : string;
filter : string;
}
Then define the following Pipe and filter for each header.
import { Pipe, PipeTransform } from '#angular/core';
import { Ifilter } from "app/filter/filter.component";
#Pipe({
name: 'pipeFilter',
pure: false
})
export class FilterPipe implements PipeTransform {
transform(data: Array<Map<string, string>>, filters: Array<Ifilter>): Array<Map<string, string>> {
let filteredData = Array<Map<string, string>>();
for (let row of data) {
let exclude: boolean = false;
for (let filter of filters) {
if (filter.filter != '' && row.get(filter.header).indexOf(filter.filter) == -1) {
exclude = true;
break;
}
}
if (!exclude)
filteredData.push(row);
}
return filteredData;
}
}
Finally, display the table with this template.
Get the filters with a two-way binding [(NgModel)] and pass them as parameter to the Pipe on the *ngFor : *ngFor="let rows of data | pipeFilter:filters". :
<table *ngIf="data && data.length">
<th *ngFor="let header of headers">
{{header}}
</th>
<tr>
<td *ngFor="let filter of filters;let i=index">
<input type='text' [(ngModel)]='(filters[i]).filter' />
</td>
</tr>
<tr *ngFor="let rows of data | pipeFilter:filters">
<td *ngFor="let header of headers ">
{{rows.get(header)}}
</td>
</tr>
</table>
Both Template and Pipe are completely dynamic and can handle any header.
Regard,
Philippe