On a datatable that I am using, I am having mixed results when attempting to sort them by using the toggles in the table headers. It appears that columns that get populated by the response in my initial GET call populate and sort as expected. However, the columns with data that comes from the GET calls inside the outer subscription don't sort as expected. Ideally all columns would be able to sort properly.
When wrapping the this.dtTrigger.next() in a setTimeout() of 5000ms, all columns sorted as expected. So the question here is, how would I wait until the innermost GET call finishes to call the dtTrigger.next()so that all of the columns will be sortable once all of the data is finished loading in.
import { Component, OnInit, Input, OnChanges } from '#angular/core';
import { Subject } from 'rxjs';
import { BuildersService } from '#/services/builders/builders.service';
import { CommunitiesService } from '#/services/communities/communities.service';
import { ContactsService } from '#/services/contacts/contacts.service';
import { DataTableDirective } from 'angular-datatables';
#Component({
selector: 'datatable-builders',
templateUrl: './builder-datatable.component.html',
styleUrls: ['./builder-datatable.component.scss']
})
export class BuilderDatatableComponent implements OnInit {
#Input() title: string = '';
#Input() showFooter: boolean = true;
#Input() rowsPerPage: number = 10;
#Input() viewRoute: string = '';
#Input() viewText: string = 'View';
rows = [];
dtOptions: DataTables.Settings = {};
dtTrigger: Subject<any> = new Subject();
constructor(
private buildersService: BuildersService,
private communitiesService: CommunitiesService,
private contactsService: ContactsService
) { }
dataTablesInit() {
this.dtOptions = {
pagingType: 'simple_numbers',
lengthChange: false,
info: this.showFooter,
paging: this.showFooter,
columnDefs: [
{
targets: [4],
orderable: false,
searchable: false
}
],
initComplete: () => {
let searchLabels = document.querySelectorAll('.dataTables_filter > label');
searchLabels.forEach((label) => {
label.setAttribute('aria-label', 'Search/Filter Table');
});
}
};
}
ngOnInit() {
this.dataTablesInit();
// Get the table data
this.buildersService.getBuilders().subscribe((result: any) => {
// Get all Rows
let rows = result.body.map(row => {
let communities = [],
communitiesColumn = [],
managersColumn = [];
// Get the builders info and set up output
row.CommunityIDs.forEach((id) => {
this.communitiesService.getCommunity(id).subscribe((result: any) => {
communities.push(result.body);
communitiesColumn.push( result.body.Name );
});
});
row.Contacts.Managers.forEach((id) => {
this.contactsService.getContact(id).subscribe((result: any) => {
managersColumn.push(result.body);
});
});
console.log(row);
// Set additional row data
row.Communities = communities;
row.CommunitiesColumn = communitiesColumn;
row.ManagersColumn = managersColumn;
return row;
});
this.rows = result.body;
this.dtTrigger.next();
console.log(this.rows)
});
}
}
This is the synchronicity of JavaScript in general, you can use the keyword Async in the top level GET and Await keyword in the down level GET
e.g :
// Get the table data
async this.buildersService.getBuilders().subscribe((result: any) => {
...
//
// Get the builders info and set up output
row.CommunityIDs.forEach((id) => {
await this.communitiesService.getCommunity(id).subscribe((result: any) => {
...
//
communities.push(result.body);
communitiesColumn.push( result.body.Name );
});
});
row.Contacts.Managers.forEach((id) => {
this.contactsService.getContact(id).subscribe((result: any) => {
managersColumn.push(result.body);
});
});
console.log(row);
// Set additional row data
row.Communities = communities;
row.CommunitiesColumn = communitiesColumn;
row.ManagersColumn = managersColumn;
return row;
});
this.rows = result.body;
this.dtTrigger.next();
console.log(this.rows)
});
}
}
Related
I'm trying to send the selected data in my table row that I am selecting via a checkbox to the server but having questions about how it should be sent via a service. I have the basic skeleton but need help with getting the items to a delete REST API call. Using C# .Net Core JSON call as the server endpoint for this service call.
view.component.ts
#Component({
templateUrl: 'view.component.html'
})
export class ViewComponent implements OnInit, OnDestroy {
// User Fields
currentUser: User;
users: User[] = [];
currentUserSubscription: Subscription;
loading : boolean;
// Action Fields
viewData: any;
viewName: string;
refNumber: number;
currentActionSubscription: Subscription;
displayedColumns: string[] = [];
dataSource: any = new MatTableDataSource([]);
pageSizeOptions: number[] = [10, 20, 50];
#ViewChild(MatSort) sort: MatSort;
#ViewChild(MatPaginator) paginator: MatPaginator;
selection = new SelectionModel<TableRow>(true, []);
defaultSort: MatSortable = {
id: 'defColumnName',
start: 'asc',
disableClear: true
};
defaultPaginator: MatPaginator;
constructor(
private iconRegistry: MatIconRegistry,
private sanitizer: DomSanitizer,
private actionService: ActionService
) {
this.loading = false;
this.iconRegistry.addSvgIcon(
'thumbs-up',
this.sanitizer.bypassSecurityTrustResourceUrl(
'assets/img/examples/thumbup-icon.svg'
)
);
}
loadAction(action: any) {
this.loading = true;
// If there is already data loaded into the View, cache it in the service.
if (this.viewData) {
this.cacheAction();
}
if (this.sort) {
// If there is sorting cached, load it into the View.
if (action.sortable) {
// If the action was cached, we should hit this block.
this.sort.sort(action.sortable);
} else {
// Else apply the defaultSort.
this.sort.sort(this.defaultSort);
}
}
if (this.paginator) {
// If we've stored a pageIndex and/or pageSize, retrieve accordingly.
if (action.pageIndex) {
this.paginator.pageIndex = action.pageIndex;
} else { // Apply default pageIndex.
this.paginator.pageIndex = 0;
}
if (action.pageSize) {
this.paginator.pageSize = action.pageSize;
} else { // Apply default pageSize.
this.paginator.pageSize = 10;
}
}
// Apply the sort & paginator to the View data.
setTimeout(() => this.dataSource.sort = this.sort, 4000);
setTimeout(() => this.dataSource.paginator = this.paginator, 4000);
// Load the new action's data into the View:
this.viewData = action.action;
this.viewName = action.action.ActionName;
this.refNumber = action.refNumber;
// TODO: add uniquifiers/ids and use these as the sort for table
const displayedColumns = this.viewData.Columns.map((c: { Name: any; }) => c.Name);
displayedColumns[2] = 'Folder1';
this.displayedColumns = ['select'].concat(displayedColumns);
// tslint:disable-next-line: max-line-length
const fetchedData = this.viewData.DataRows.map((r: { slice: (arg0: number, arg1: number) => { forEach: (arg0: (d: any, i: string | number) => any) => void; }; }) => {
const row = {};
r.slice(0, 9).forEach((d: any, i: string | number) => (row[this.displayedColumns[i]] = d));
return row;
});
this.dataSource = new MatTableDataSource(fetchedData);
this.loading = false;
}
// Stores the current Action, sort, and paginator in an ActionState object to be held in the action service's stateMap.
cacheAction() {
let actionState = new ActionState(this.viewData);
// Determine the sort direction to store.
let cachedStart: SortDirection;
if (this.sort.direction == "desc") {
cachedStart = 'desc';
} else {
cachedStart = 'asc';
}
// Create a Sortable so that we can re-apply this sort.
actionState.sortable = {
id: this.sort.active,
start: cachedStart,
disableClear: this.sort.disableClear
};
// Store the current pageIndex and pageSize.
actionState.pageIndex = this.paginator.pageIndex;
actionState.pageSize = this.paginator.pageSize;
// Store the refNumber in the actionState for later retrieval.
actionState.refNumber = this.refNumber;
this.actionService.cacheAction(actionState);
}
ngOnInit() {
// Subscribes to the action service's currentAction, populating this component with View data.
this.actionService.currentAction.subscribe(action => this.loadAction(action));
}
/** Whether the number of selected elements matches the total number of rows. */
isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = this.dataSource.data.length;
return numSelected === numRows;
}
/** Selects all rows if they are not all selected; otherwise clear selection. */
masterToggle() {
this.isAllSelected()
? this.selection.clear()
: this.dataSource.data.forEach((row: TableRow) => this.selection.select(row));
}
// Delete row functionality
deleteRow() {
console.log(this.selection);
this.selection.selected.forEach(item => {
const index: number = this.dataSource.data.findIndex((d: TableRow) => d === item);
console.log(this.dataSource.data.findIndex((d: TableRow) => d === item));
this.dataSource.data.splice(index, 1);
this.dataSource = new MatTableDataSource<Element>(this.dataSource.data);
});
this.selection = new SelectionModel<TableRow>(true, []);
this.actionService.deleteRow(this.selection).subscribe((response) => {
console.log('Success!');
});
}
ngOnDestroy() {
}
}
view.service.ts
deleteRow(selection: any): Observable<{}> {
console.log('testing service');
return this.http.delete<any>(`http://localhost:15217/actions/deleteRow`);
}
There are 2 things that your code as it currently stands needs to do:
Pass the ids of the selected rows back to the server in some way (generally via the url in a DELETE request)
Subscribe to the observable to materialise it. Currently the http request won't run, because it's an observable without any subscribers. At the very least the call to the service in the component should look a little like this:
this.actionService.deleteRow(this.selection).subscribe((response) => {
console.log('Success!');
});
Edit:
With number 1, it depends on what your server method looks like. If it accepts an array of numeric ids, then view.service.ts would look something like:
deleteRow(selection: SelectionModel<TableRow>): Observable<{}> {
console.log('testing service');
// create an array of query params using the property that you use to identify a table row
const queryParams = selection.selected.map(row => `id=${row.id}`);
// add the query params to the url
const url = `http://localhost:15217/actions/deleteRow?${queryParams.join('&')}`;
return this.http.delete<any>(url);
}
I'm guessing here at how you pass information about table rows to your server. If you're still struggling with this, you will need to provide a bit of information about the DELETE endpoint.
Edit 2:
Now we know a bit more about what the objects look like...
deleteRow(selection: SelectionModel<TableRow>): Observable<{}> {
console.log('testing service');
// create an array of query params using the property that you use to identify a table row
const queryParams = [...selection._selection].map(row => `id=${row.id}`);
// add the query params to the url
const url = `http://localhost:15217/actions/deleteRow?${queryParams.join('&')}`;
return this.http.delete<any>(url);
}
My requirement to replace the all the columns when ever the changes/event is happening outside of the Data Table.
data table is displaying for the first time with selected columns(from event). if i select second one it's not displaying but columns in dtOptions getting changed but it's not displaying. I think clearing the view the problem but i tried using destroy it's not working out for me. some one please help me to achieve this.
HTML Code:
<div id="data-table-grid-slide">
<table datatable [dtOptions]="dtOptions" [dtTrigger]="dtTrigger" class="row-border hover"></table>
</div>
Angular Code for DataTable:
import {Component, ViewChild, OnInit, Input, AfterViewInit, OnDestroy, Output, EventEmitter} from '#angular/core';
import { DataTableDirective } from 'angular-datatables';
import { Subject } from 'rxjs';
import { ColumnObject } from '../data-tables-net/model/data-tables-model';
import { HttpClient } from '#angular/common/http';
import { DtServiceService} from '../data-tables-net/dt-service.service';
import { WindowRef} from '../services/WindowRef';
declare var $;
#Component({
selector: 'app-data-tables-net',
templateUrl: './data-tables-net.component.html',
styleUrls: ['./data-tables-net.component.css']
})
export class DataTablesNetComponent implements OnInit, AfterViewInit, OnDestroy {
#ViewChild('dataTable', {static: true}) table;
#ViewChild(DataTableDirective, {static: false}) dtElement: DataTableDirective;
dataTableColumn: Array<any> = [];
dtOptions: DataTables.Settings = {};
#Input() dataTableGrid: boolean;
#Input() tableShow: boolean;
#Output() tableShowChange = new EventEmitter<boolean>();
#Output() dataTableGridChange = new EventEmitter<boolean>();
dtTrigger: Subject<any> = new Subject();
// editor: any;
columnObject: ColumnObject = {
title: '',
data: ''
};
constructor(private http: HttpClient, private dtServiceService: DtServiceService, private winRef: WindowRef) { }
ngOnInit() {
this.dataTableGrid = true;
this.initDt();
}
ngAfterViewInit(): void {
// This method get called on pencil click of model in Data Model Visuvalizer
this.winRef.modelClick$.subscribe((modelObjAttributes) => {
this.dataTableGrid = true;
this.tableShow = false;
this.tableShowChange.emit(this.tableShow);
this.dataTableGridChange.emit(this.dataTableGrid);
console.log('modelObjAttributes', modelObjAttributes);
// tslint:disable-next-line: max-line-length
// this.dtOptions.columns = [{title: 'id', data: 'id'}, {title: 'name', data: 'name'}, {title: 'item code', data: 'item code'}, {title: 'addr', data: 'addr'}];
if (this.dtOptions.columns) {
// this.dtOptions.destroy = true;
// delete this.dtOptions.columns;
this.reRenderDataTable();
// console.log('columns', this.dtOptions.columns);
this.initDt();
this.dtOptions.columns = this.getModelDetails(modelObjAttributes);
// console.log(this.dtOptions.columns);
this.dtTrigger.next();
} else {
this.dtOptions.columns = this.getModelDetails(modelObjAttributes);
console.log(this.dtOptions.columns);
this.dtTrigger.next();
// this.dtOptions.destroy = true;
}
// delete this.dtOptions.columns;
});
}
initDt() {
this.dtOptions = {
// ajax: 'data/data.json',
// columns: [{title: 'Column1', data: 'column1'}],
paging: true,
searching: true,
ordering: true,
info: false,
responsive: true,
destroy: true
};
}
ngOnDestroy(): void {
// Do not forget to unsubscribe the event
this.dtTrigger.unsubscribe();
}
// This method used to get the details of model on clicking of pencil icon
getModelDetails(modelDetailsObj) {
return this.convertModelAttributesToDataTable(modelDetailsObj.options);
// this.getModelDetailsFromService(modelDetailsObj.id);
}
// This method is used to call the service to get the selected Models / Schema details from Database
getModelDetailsFromService(schemaId): void {
this.dtServiceService.getSelectedSchema(schemaId).subscribe(data => {
console.log(data);
},
error => {
console.log('Data is not getting');
});
}
// This method used to form the schema data for Data Table
convertModelAttributesToDataTable(attributesObject) {
this.dataTableColumn = [];
// delete this.dtOptions.columns;
for (const [index, obj] of attributesObject.entries()) {
if (obj) {
this.columnObject = { title: obj.text, data: obj.text};
console.log('columnObject', this.columnObject);
this.dataTableColumn.push(this.columnObject);
// console.log(this.dtOptions);
}
}
// this.dtTrigger.next();
return this.dataTableColumn;
}
// This method used re-render the data table with updated data's
reRenderDataTable(): void {
this.dtElement.dtInstance.then((dtInstance: DataTables.Api) => {
// Destroy the table first
// dtInstance.destroy();
// Call the dtTrigger to rerender again
this.dtTrigger.next();
});
}
}
I have created stackblitz for my requirement. In this example variables called columnsDataObj and dataUrl will change dynamically. it should get reflect in the data table. Please let me if you need more details:
https://stackblitz.com/edit/angular-datatables-gitter-4tavmk?file=app/app.component.ts
You need to also "destroy" dtOptions and the previous table from the template, also you have to make sure your template notices when the change is done, this are the main changes:
First destroy your previous DT completely, i added a flag called dtRendered:
dtRendered = true;
<table *ngIf="dtRendered" datatable [dtOptions]="dtOptions" class="row-border hover">
Also at your update method you have to make sure everything is destroyed and initialized again:
updateData() {
// destroy you current configuration
this.dtRendered = false
this.dtOptions = {
data: this.jsonData1.data,
columns: this.columnsDataObj1
};
// make sure your template notices it
this.cdr.detectChanges();
// initialize them again
this.dtRendered = true
this.cdr.detectChanges();
}
The this.cdr.detectChanges() call is needed so the lifecycle-hook notices about the change.
Here's your example working as expected:
https://stackblitz.com/edit/how-to-replace-all-the-columns-dynamically-in-data-table?file=app/app.component.ts
I'm new to this. I want to get data from Rest API. Loading data from the endpoint is ok, but I want to use it later, outside the method. For example I want to sum one of the attributes of the todos in another function. In funcion loadTodos() the first console log shows the data, but the second one shows only "undefined". How can I save the values what loadTodos() gives back and use it later?
import { Component, OnInit } from '#angular/core';
import { Router } from '#angular/router';
import { TodoDomainService } from '../services/todo-domain.service';
import { Todo } from 'app/model/todo';
#Component({
selector: 'app-todo-listing',
templateUrl: './todo-listing.component.html',
styleUrls: ['./todo-listing.component.scss']
})
export class TodoListingComponent implements OnInit {
todo: Todo;
constructor(private todoService: TodoDomainService, private router:Router) { }
public todos;
ngOnInit() {
this.loadTodos();
this.todo = new Todo();
}
private loadTodos() {
this.todoService.getTodos().subscribe(
data => { this.todos = data },
err => console.error(err),
() => console.log("todos loaded." +this.todos)
);
console.log(this.todos)
}
}
private getSum(todos) {
var sum = 0;
for(var i = 0; i < todos.length; i++){
sum += todos.price[i]}
return this.aggregatedSales;
}
console.log("todos loaded." +this.todos) will show a response because it is executed after the observable has completed.
console.log(this.todos) after your .subscribe(...) shows undefined because the observable hasn't yet finished, that is, the line data => { this.todos = data } hasn't been executed.
You are saving the data correctly for use. If you update your next called for the subscription to look like the following then the sum will execute:
// from
data => { this.todos = data }
// to
data => {
this.todos = data;
this.getSum(this.todos);
}
Here is a stackblitz example of fetching a todos array and adding up the userId values into a sum variable then displaying the value.
can any one please tell me why I can not loop through this array?
In ngOnInit, everything works fine. I got an array that I successfully display in the template.
But in ngAfterViewInit, console.log show the array but when looping through with "for of" or "forEach", nothing works.
import { JobsService } from '../jobs.service';
import {Job} from '../models/Job';
#Component({
selector: 'app-job',
templateUrl: 'job.component.html'
})
export class JobComponent implements OnInit, AfterViewInit {
title = 'Job';
jobs: Job[] = [];
InProcess = '';
CurrentPartner = '';
ShowProcess = false;
sended = '';
constructor(private jobsService: JobsService) {
}
ngOnInit() {
this.jobs = this.jobsService.getJobs();
}
ngAfterViewInit() {
console.log(this.jobs); // Show the array
// Nothing happened when looping through the array
this.jobs.forEach((oneJob) => {
console.log(oneJob);
});
}
}
Screenshot of the console in Google Chrome
The content of the service:
import { HttpClient, HttpErrorResponse } from '#angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import {Job} from './models/Job';
interface IJob {
message: string;
jobs: any[];
}
#Injectable({
providedIn: 'root'
})
export class JobsService {
constructor(private httpClient: HttpClient) { }
private REST_API_SERVER = 'http://localhost:8080/myband/api/getjobs.php';
private REST_API_SERVER_SEND = 'http://localhost:8080/myband/api/sendjob.php';
jobList: Job[] = [];
errorMessage: any;
message: string;
static handleError(err: HttpErrorResponse) {
let errorMessage = '';
if (err.error instanceof ErrorEvent) {
errorMessage = `An error occurred: ${err.error.message}`;
} else {
errorMessage = `Server returned code: ${err.status}, error message is: ${err.message}`;
}
console.error(errorMessage);
return throwError(errorMessage);
}
public getJobs() {
this.requestJobs().subscribe(
iJob => {
this.message = iJob.message;
for (const job of iJob.jobs) {
const oneJob: Job = new Job(job);
this.jobList.push(oneJob);
}
},
error => this.errorMessage = error as any
);
return this.jobList;
}
public requestJobs(): Observable<IJob> {
return this.httpClient.get<IJob>(this.REST_API_SERVER).pipe(
catchError(JobsService.handleError)
);
}
}
The first thing I want to say to you is about isolation of responsibilities.
Your service must have just one job: provider one way to access your data; It means your logic inside getJobs() method could be done in your component.
export class JobsService {
constructor(
private httpClient: HttpClient,
) {}
private REST_API_SERVER = 'http://localhost:8080/myband/api/getjobs.php';
public requestJobs(): Observable<IJob> {
return this.httpClient.get<IJob>(this.REST_API_SERVER);
}
}
Now, you can handler your data in your component.
import { JobsService } from '../jobs.service';
#Component({
selector: 'app-job',
templateUrl: 'job.component.html'
})
export class JobComponent implements OnInit, AfterViewInit {
title = 'Job';
jobs$;
InProcess = '';
CurrentPartner = '';
ShowProcess = false;
sended = '';
constructor(private jobsService: JobsService) {
}
ngOnInit() {
this.jobs$ = this.jobsService.requestJobs();
}
ngAfterViewInit() {
this.jobs$
.pipe(
map(() => {}), // change your data here
catchError(() => {}) // handler your error here;
)
.subscribe(
() => {} // have access to your final data here.
);
}
}
Things to know:
You can remove the subscribe() execution and use the async pipe in your template;
The use of the operator map in pipe() is optional, you can handler your final data directly from your first callback subscribe().
You can convert your Observable to Promise using toPromise() method in one observable. Don't forgot async / await in your ngAfterViewInit.
Let me know if there is something I can help.
Try:
Object.keys(this.jobs).forEach(job => {
console.log(this.jobs[job]);
});
Try to assign an iterator function with below part replacement by this code:
// Nothing happened when looping through the array
this.jobs.forEach(oneJob, function(value, key) {
console.log(key + ': ' + value);
});
Usage of forEach in AngularJS:
For documentation try to check AngularJS forEach Docs
Syntax:
someIterable.forEach(object, iterator, [context])
Please check below example
class Job {
id: any;
status: any;
constructor(obj: any) {
this.id = obj.id;
this.status = obj.status;
}
}
let arr = [
{
id: 1,
status: "job"
}, {
id: 2,
status: "job2"
}
];
let newArr: any = [];
arr.forEach(a => {
let obj: Job = new Job(a);
newArr.push(obj);
})
console.log(newArr);
newArr.forEach((a: any) => {
console.log(a);
})
Ang2 Component:
import { Component, OnInit } from '#angular/core';
import { AngularFire, FirebaseListObservable, FirebaseObjectObservable } from 'angularfire2';
import Chart from 'chart.js'
#Component({
selector: 'app-prchart',
templateUrl: './app-prchart.component.html',
styleUrls: ['./app-prchart.component.css']
})
export class AppPRChartComponent implements OnInit {
userEmail: any;
email: string;
uid: any;
records = [];
newRecords = [];
filteredRecords = [];
labels = [];
public barChartOptions:any = {
scaleShowVerticalLines: false,
responsive: true,
legend: {
display: false
}
};
public barChartColors: any [] =[
{
backgroundColor:'rgba(30, 136, 229, 1)'
}
];
public barChartType:string = 'bar';
constructor(public af: AngularFire) {
var auth = this.af.auth.subscribe( (user) => {
if (user) {
this.userEmail = this.af.auth.subscribe(auth => {
const queryObservable = af.database.list('/users/'+ auth.auth.uid +'/records/', {
});
queryObservable.subscribe(queriedItems => {
this.records.push(queriedItems);
});
// Filter records into PR's
this.newRecords =
this.records[0].sort((a, b) => {
if (a.movement === b.movement) {
return a.weight >= b.weight ? -1 : 1
}
return a.movement > b.movement ? 1 : -1
})
.filter((rec, i, arr) => {
if (i === 0) return true
return rec.movement !== arr[i - 1].movement
});
let recordString = JSON.stringify(this.newRecords);
let recordParse = JSON.parse(recordString);
this.filteredRecords.push(recordParse);
});
} else {
}
});
this.filteredRecords[0].forEach(function(snapshot) {
this.labels.push(snapshot.movement);
//barChartData.push(snapshot.weight);
});
//console.log(barChartLabels);
//console.log(barChartData);
}
ngOnInit() {
}
}
I'm trying to push items into an array but I keep getting the following error:
TypeError: Cannot read property 'labels' of undefined
The error occurs towards the bottom where this line of code is being run:
this.labels.push(snapshot.movement);
I've been playing with this for hours and can't figure out what I'm doing wrong, any help is appreciated.
The issues is that this changes inside callback functions. You can fix this by using arrow functions which will capture the correct this:
this.filteredRecords[0].forEach((snapshot) => {
this.labels.push(snapshot.movement);
//barChartData.push(snapshot.weight);
});
Or by capturing the this in another variable:
let that = this;
this.filteredRecords[0].forEach(function (snapshot) {
that.labels.push(snapshot.movement);
//barChartData.push(snapshot.weight);
});
This might be useful: What is 'this' in TypeScript