Is there a Lifecycle Hook for components nested inside mat-tab? - javascript

New to Tabulator.
I have a mat-tab-group that I'm using for navigating through three Tabulator Tables.
The approach that I'm using is to create a div element inside component.ts, and the insert it into an existing div within the component.html.
The issue that I'm having is that only the selected tab has its html loaded into the DOM, so any div elements in an unselected tab aren't in the DOM, and cant be referenced by id into my component.ts. When I switch to a tab other than the one that was initially loaded, the tabulator table is not drawn.
What hoping is that Angular has a lifecycle hook that get's called on child-components of mat-tab that I can use to trigger a draw, when a user selects that tab.
I'm open to other/better approaches, of course.
mat-tab-group html
<mat-tab-group *ngIf="hasData" mat-align-tabs="start">
<mat-tab *ngFor="let tablename of tableNames">
<ng-template matTabLabel>
<span>{{tablename}}</span>
</ng-template>
<div>
<app-tabulator-table *ngIf="dataServiceSelectedDate" [integrationName]="integrationName" [tableName]="tablename" [processDate]=dataServiceSelectedDate>
</app-tabulator-table>
</div>
</mat-tab>
</mat-tab-group>
tablulator-table.component.ts
export class TabulatorTableComponent implements OnInit {
#Input() tableName: string;
#Input() processDate: string;
#Input() integrationName: string;
fields: RunDataTableField[];
dataContent: RunDataContent;
tab = document.createElement("div");
tabColumns: any[] = [];
tabRows: any[] = [];
constructor(private clientDataService: ClientDataService) {
}
ngOnInit() {
this.clientDataService.getRunDataContent(this.integrationName, this.processDate, `${this.tableName}.json`).toPromise().then(dataContent =>
{
console.log(this.dataContent)
this.tabColumns = this.buildHeaders(dataContent);
this.tabRows = this.buildRows(dataContent);
this.redraw();
});
}
calculationFormatter = function (cell, formatterParams) {
var value = cell.getValue();
cell.getElement().style.backgroundColor = "#fdd0ff";
return value;
}
buildHeaders(runData: RunDataContent): any[] {
console.log(`${this.tableName}: creating headers object`);
var headers: any[] = [];
runData.schema.fields.forEach(f => {
var c: any = {};
c.title = f.name;
c.field = f.name;
c.headerFilter = "input"
switch (f.validationType) {
case "calculation": {
c.formatter = this.calculationFormatter
break;
}
case "raw": {
}
case "table": {
}
default: {
break;
}
}
if (f.tip != null && f.tip.length > 0) {
c.tooltip = f.tip;
c.headerTooltip = f.tip;
}
headers = headers.concat(c);
});
console.log(`${this.tableName}: createad headers object`);
console.log(this.tabColumns);
return headers;
}
buildRows(runData: RunDataContent): any[] {
console.log(`${this.tableName}: creating rows object`);
var rows: any[] = [];
runData.rows.forEach(f => {
rows = this.tabRows.concat(f);
});
return rows;
}
private drawTable(): void {
new Tabulator(this.tab, {
layout: "fitDataStretch",
selectable: true,
selectableRangeMode: "click",
data: [],
columns: this.tabColumns,
height: "311px"
});
document.getElementById(`${this.tableName}`).appendChild(this.tab);
new Tabulator()
}
redraw() {
console.log(`${this.tableName}: drawing table`)
this.drawTable();
console.log(`${this.tableName}: completed drawing table`)
}
}

According to Angular Material Docs, By default, the tab contents are eagerly loaded. Eagerly loaded tabs will initalize the child components but not inject them into the DOM until the tab is activated.
If the tab contains several complex child components or the tab's contents rely on DOM calculations during initialization, it is advised to lazy load the tab's content.
Tab contents can be lazy loaded by declaring the body in a ng-template with the matTabContent attribute.
So, the correct approach would be to use ng-template with matTabContent. Please find below code for reference :
<mat-tab *ngFor="let table of tableNames" [label]="table">
<ng-template matTabContent>
<div>
<app-tabulator-table *ngIf="dataServiceSelectedDate"
[integrationName]="integrationName" [tableName]="tablename"
[processDate]=dataServiceSelectedDate>
</app-tabulator-table>
</div>
</ng-template>
</mat-tab>

Related

Ag-Grid cellRender with Button Click

I am using an angular 5 with ag-grid data table
i cant able to trigger a click event from cell using cellRenderer here how am using my ag-grid --> colDefs
this.columnDefs = [
{headerName: '#', rowDrag: true, width: 75},
{headerName: 'One', field: 'fieldName',
cellRenderer : function(params){
return '<div><button (click)="drop()">Click</button></div>'
}
}
];
drop() {
alert("BUTTON CLICKEFD")
}
if am using onClick="alert("123")" --> it works,
but i cant able to use onClick="drop()" it throws drop of undefined,
i tried this too inside of cellRenderer --> params = params.$scope.drop = this.drop;
if am using gridOptions with angularCompileRows : true it throws an error Cannot read property '$apply' of undefined.
Do i need to install ag-grid enterprise ??
You can use cellRenderer with a button component.
If you want to get the click event on the button from the user on the table, just declare the callback function you want to cellRendererParams.
// app.component.ts
columnDefs = [
{
headerName: 'Button Col 1',
cellRenderer: 'buttonRenderer',
cellRendererParams: {
onClick: this.onBtnClick.bind(this),
label: 'Click'
}
},
...
]
The above code is just a small part, check out full example on Stackblitz
Angular.
Here we create the button cell renderer as an Angular component that implements the ICellRendererAngularComp interface. Access to the params object can be found on the agInit hook.
// app/button-cell-renderer.component.ts
#Component({
selector: 'btn-cell-renderer',
template: `
<button (click)="btnClickedHandler($event)">Click me!</button>
`,
})
export class BtnCellRenderer implements ICellRendererAngularComp, OnDestroy {
private params: any;
agInit(params: any): void {
this.params = params;
}
btnClickedHandler() {
this.params.clicked(this.params.value);
}
ngOnDestroy() {
// no need to remove the button click handler as angular does this under the hood
}
}
The renderer is registered to ag-Grid via gridOptions.frameworkComponents. Note that we’re passing the button click handler dynamically to our renderer via cellRendererParams - allowing for a more flexible and reusable renderer.
// app/app.component.ts
this.columnDefs = [
{
field: 'athlete',
cellRenderer: 'btnCellRenderer',
cellRendererParams: {
clicked: function(field: any) {
alert(`${field} was clicked`);
}
},
minWidth: 150,
}
// [...]
];
this.frameworkComponents = {
btnCellRenderer: BtnCellRenderer
};
It is also necessary to pass our renderer to our #NgModule decorator to allow for dependency injection.
// app/app.modules.ts
#NgModule({
imports: [
BrowserModule,
FormsModule,
HttpClientModule,
AgGridModule.withComponents([BtnCellRenderer]),
],
declarations: [AppComponent, BtnCellRenderer],
bootstrap: [AppComponent],
})
See demo.
Learn more about Angular Cell Renderer.
Vanilla JavaScript.
A DOM element is created in the init method, which is then returned in the getGui method. The optional destroy hook has also included to do some cleanup (removing the click listener from our component).
// btn-cell-renderer.js
function BtnCellRenderer() {}
BtnCellRenderer.prototype.init = function(params) {
this.params = params;
this.eGui = document.createElement('button');
this.eGui.innerHTML = 'Click me!';
this.btnClickedHandler = this.btnClickedHandler.bind(this);
this.eGui.addEventListener('click', this.btnClickedHandler);
}
BtnCellRenderer.prototype.getGui = function() {
return this.eGui;
}
BtnCellRenderer.prototype.destroy = function() {
this.eGui.removeEventListener('click', this.btnClickedHandler);
}
BtnCellRenderer.prototype.btnClickedHandler = function(event) {
this.params.clicked(this.params.value);
}
The renderer is registered to ag-Grid in gridOptions.components and is used on the athlete column. Note that we’re passing the button click handler dynamically to our renderer via cellRendererParams - this makes for a more flexible and reusable renderer.
// main.js
var gridOptions = {
columnDefs: [
{
field: 'athlete',
cellRenderer: 'btnCellRenderer',
cellRendererParams: {
clicked: function(field) {
alert(`${field} was clicked`);
}
},
minWidth: 150
},
// [...]
components: {
btnCellRenderer: BtnCellRenderer
}
};
See demo.
Learn more about JavaScript Cell Renderers.
React.
Here our button cell renderer is constructed as a React component. The only thing to take note of here is that cell params will be available on the component via props.
// BtnCellRenderer.jsx
class BtnCellRenderer extends Component {
constructor(props) {
super(props);
this.btnClickedHandler = this.btnClickedHandler.bind(this);
}
btnClickedHandler() {
this.props.clicked(this.props.value);
}
render() {
return (
<button onClick={this.btnClickedHandler}>Click Me!</button>
)
}
}
The renderer is registered to ag-Grid via gridOptions.frameworkComponents. The button click handler is passed to our renderer at run time via cellRendererParams - allowing for a more flexible and reusable renderer.
// index.jsx
columnDefs: [
{
field: 'athlete',
cellRenderer: 'btnCellRenderer',
cellRendererParams: {
clicked: function(field) {
alert(`${field} was clicked`);
},
},
// [...]
}
];
frameworkComponents: {
btnCellRenderer: BtnCellRenderer,
}
See demo.
Learn more about React Cell Renderers.
Vue.js.
Configuring the renderer in Vue.js is simple:
// btn-cell-renderer.js
export default Vue.extend({
template: `
<span>
<button #click="btnClickedHandler()">Click me!</button>
</span>
`,
methods: {
btnClickedHandler() {
this.params.clicked(this.params.value);
}
},
});
As with the other frameworks, the renderer is registered to ag-Grid via gridOptions.frameworkComponents and the button click handler is passed to our renderer at run time via cellRendererParams - allowing for a more flexible and reusable renderer.
// main.js
this.columnDefs = [
{
field: 'athlete',
cellRenderer: 'btnCellRenderer',
cellRendererParams: {
clicked: function(field) {
alert(`${field} was clicked`);
}
},
// [...]
],
this.frameworkComponents = {
btnCellRenderer: BtnCellRenderer
}
See demo.
Learn more about Vue.js Cell Renderers.
Read the full blog post on our website or check out our documentation for a great variety of scenarios you can implement with ag-Grid.
Ahmed Gadir | Developer # ag-Grid
To expand on the answer from #T4professor, I will post some code to also have a dynamic label on that Click button.
// Author: T4professor
import { Component, OnInit, AfterContentInit } from '#angular/core';
import { ICellRendererAngularComp } from 'ag-grid-angular';
#Component({
selector: 'app-button-renderer',
template: `
<button class="{{btnClass}}" type="button" (click)="onClick($event)">{{label}}</button>
`
})
export class ButtonRendererComponent implements ICellRendererAngularComp {
//https://stackblitz.com/edit/angular-ag-grid-button-renderer?file=src%2Fapp%2Fapp.component.ts
params: any;
label: string;
getLabelFunction: any;
btnClass: string;
agInit(params: any): void {
this.params = params;
this.label = this.params.label || null;
this.btnClass = this.params.btnClass || 'btn btn-primary';
this.getLabelFunction = this.params.getLabelFunction;
if(this.getLabelFunction && this.getLabelFunction instanceof Function)
{
console.log(this.params);
this.label = this.getLabelFunction(params.data);
}
}
refresh(params?: any): boolean {
return true;
}
onClick($event) {
if (this.params.onClick instanceof Function) {
// put anything into params u want pass into parents component
const params = {
event: $event,
rowData: this.params.node.data
// ...something
}
this.params.onClick(params);
}
}
}
Then, in the component with the grid you do the following:
columnDefs = [
{
headerName: 'Publish',
cellRenderer: 'buttonRenderer',
cellRendererParams: {
onClick: this.onRowPublishBtnClick.bind(this),
label: 'Publish',
getLabelFunction: this.getLabel.bind(this),
btnClass: 'btn btn-primary btn-sm'
}
}
]
onRowPublishBtnClick(e) {
this.rowDataClicked = e.rowData;
}
getLabel(rowData)
{
console.log(rowData);
if(rowData && rowData.hasIndicator)
return 'Republish';
else return 'Publish';
}
You have this issue because you invoke drop() incorrectly you should change it to this.drop()
In general you should use cellRenderer property with simple logic. More convenient way for complex logic renderer you should use cellRendererFramework: YourCustomRendererAngularComponent.
columnDefs = [
{
headerName: 'Col Name',
cellRendererFramwork: MyAngularRendererComponent, // RendererComponent suffix it is naming convention
cellRendererParams: {
onClick: (params) => this.click(params);
}
},
...
]
MyAngularRendererComponent should implements AgRendererComponent.
Also in angular module where you use MyAngualrRendererComponent don`t forget put this code:
#NgModule({
imports: [
AgGridModule.withCompoennts([
MyAngualrRendererComponent
])
]
})
I was looking for a solution to this but for multiple buttons in the same column. I couldn't find an answer anywhere so I wrote up this Plain Javascript solution. I hope it helps other people looking for the solution I was looking for. Also open to suggestions on how to make the javascript less hacky.
// multi-btn-cell-renderer.js
function multiBtnCellRenderer() {}
multiBtnCellRenderer.prototype.init = function(params) {
var self = this;
self.params = params;
self.num_buttons = parseInt(this.params.num_buttons);
self.btnClickedHandlers = {};
let outerDiv = document.createElement('div')
for(let i = 0; i < self.num_buttons; i++) {
let button = document.createElement('button');
button.innerHTML = self.params.button_html[i];
outerDiv.appendChild(button);
self.btnClickedHandlers[i] = function(event) {
self.params.clicked[i](self.params.get_data_id());
}.bind(i, self);
button.addEventListener('click', self.btnClickedHandlers[i]);
}
self.eGui = outerDiv;
};
multiBtnCellRenderer.prototype.getGui = function() {
return this.eGui;
};
multiBtnCellRenderer.prototype.destroy = function() {
for(let i = 0; i < this.num_buttons; i++) {
this.eGui.removeEventListener('click', this.btnClickedHandlers[i]);
}
};
// main.js
var columnDefs = [
{
headerName: "Action",
maxWidth: 60,
filter: false,
floatingFilter: false,
suppressMenu: true,
sortable: false,
cellRenderer: multiBtnCellRenderer,
cellRendererParams: {
num_buttons: 2,
button_html: ["<i class='fa fa-pencil'></i>","<i class='fa fa-trash'></i>"],
get_data_id: function() {
return this.data.id;
},
clicked: {
0: function(data_id) {
$.get(`/employee/${data_id}/edit`)
},
1: function(data_id) {
$.delete(`/employee/${data_id}`)
}
}
}
}
]

Angular create Dynamic Component recursively

I am trying to build a dynamic component based on a Config. The component would read the config recursively and create the component. It is found that the method ngAfterViewInit() would only be called twice.
#Component({
selector: "dynamic-container-component",
template: `
<div #container
draggable="true"
(dragstart)="dragstart($event)"
(drop)="drop($event)"
(dragover)="dragover($event)"
style="border: 1px solid; min-height: 30px"></div>
`
})
export default class DynamicContainerComponent {
#Input()
dynamicConfig: DynamicConfig;
#ViewChild("container", {read: ElementRef})
private elementRef: ElementRef;
private isContainer: boolean;
private componentRef: ComponentRef<any>;
private componentRefs: ComponentRef<any>[] = [];
constructor(
private componentFactoryResolver: ComponentFactoryResolver,
private injector: Injector,
private viewContainer: ViewContainerRef,
private render: Renderer2
){
console.log("running");
}
ngAfterViewInit(){
if (this.dynamicConfig){
console.log(this.dynamicConfig)
if (this.dynamicConfig.getType() == ComponentType.INPUT){
this.isContainer = false;
let componetFactory: ComponentFactory<InputComponent> =
this.componentFactoryResolver.resolveComponentFactory(InputComponent);
this.componentRef = this.viewContainer.createComponent(componetFactory);
this.render.appendChild(this.elementRef.nativeElement, this.componentRef.location.nativeElement);
}else {
this.isContainer = true;
let items: DynamicConfig[] = this.dynamicConfig.getItems();
if (items){
for (var i=0; i<items.length; i++){
let item: DynamicConfig = items[i];
let componetFactory: ComponentFactory<DynamicContainerComponent> =
this.componentFactoryResolver.resolveComponentFactory(DynamicContainerComponent);
let componentRef: ComponentRef<DynamicContainerComponent> =
this.viewContainer.createComponent(componetFactory);
componentRef.instance.dynamicConfig = item;
this.componentRefs.push(componentRef);
this.render.appendChild(this.elementRef.nativeElement, componentRef.location.nativeElement);
}
}
}
}else {
console.log("config does not exist");
}
}
dragstart(event){
debugger;
}
drop(event){
debugger;
}
dragover(event){
debugger;
event.preventDefault();
}
}
The Component would be created by other component by the following code. If The Dynamic Component would create another Dynamic Component by componentFactoryResolver.
var configJson = {
type: ComponentType.CONTAINER,
items: [
{
type: ComponentType.CONTAINER,
items: [{
type: ComponentType.CONTAINER,
items: [{
type: ComponentType.CONTAINER,
items: [{
type: ComponentType.INPUT
}]
}]
}]
}
]
}
this.config = new DynamicConfig();
this.config.assign(configJson);
console.log(this.config);
Update
I found a similar issue in github: https://github.com/angular/angular/issues/10762
I have done something suggested by other people. but I think it is just a dirty fix.
ngAfterViewInit(){
setTimeout(function(){
if (this.dynamicConfig){
console.log(this.dynamicConfig)
if (this.dynamicConfig.getType() == ComponentType.INPUT){
this.isContainer = false;
let componetFactory: ComponentFactory<InputComponent> =
this.componentFactoryResolver.resolveComponentFactory(InputComponent);
this.componentRef = this.viewContainer.createComponent(componetFactory);
this.render.appendChild(this.elementRef.nativeElement, this.componentRef.location.nativeElement);
}else {
this.isContainer = true;
let items: DynamicConfig[] = this.dynamicConfig.getItems();
if (items){
for (var i=0; i<items.length; i++){
let item: DynamicConfig = items[i];
let componetFactory: ComponentFactory<DynamicContainerComponent> =
this.componentFactoryResolver.resolveComponentFactory(DynamicContainerComponent);
let componentRef: ComponentRef<DynamicContainerComponent> =
this.viewContainer.createComponent(componetFactory);
componentRef.instance.dynamicConfig = item;
this.componentRefs.push(componentRef);
this.render.appendChild(this.elementRef.nativeElement, componentRef.location.nativeElement);
}
}
}
}else {
console.log("config does not exist");
}
}.bind(this))
}
By the time you create your dynamic component angular has almost finished change detection cycle.
This way you can either run:
componentRef.changeDetectorRef.detectChanges()
Note: setTimeout has similar effect but fires change detection cycle on the whole app
or rename lifecycle hook to ngOnInit
Also you're passing wrong input to dynamic component:
let item: DynamicConfig = items[i];
^^^^^^^^^^^^^
but it is not DynamicConfig instance but rather plain object
...
componentRef.instance.dynamicConfig = item;
it should be:
let item: any = items[i];
const config = new DynamicConfig();
config.assign(item);
componentRef.instance.dynamicConfig = config;
Ng-run Example

Display datas on modal dialog in angular4 application

I have a angular 4 application and I want to display datas in dialog. So, I use #Output to pass data from child to parent component.
So, in the parent component I have :
export class DashboardComponent {
myTask;
public returnTask(task: any):void {
console.log("returnTask");
this.myTask = task;
console.log(this.myTask);
}
openDialogEditTask() {
console.log(this.myTask);
let dialogRef = this.dialogEditTask.open(DialogEditTask, {
//task
data: {
start: this.myTask.start,
end: this.myTask.end,
status: this.myTask.status,
user: this.myTask.user,
content: this.myTask.content,
id: this.myTask.id,
rate: this.myTask.rate
}
});
dialogRef.afterClosed().subscribe(result => {
this.selectedOption = result;
});
}
}
In the parent html, I have :
<visTimeline (myTask)="returnTask($event)"></visTimeline>
In the child component, I have :
export class VisTimelineComponent {
#Output() myTask: EventEmitter<any> = new EventEmitter<any>();
}
And I emit the task with self.myTask.emit(task);
So, I get the datas in the parent component (I can see them in the console) but I can't use them in openDialogEditTask because it's undefined.
So, do you know how can I get the datas before calling the function to have the datas in the dialog ?
EDIT :
This is my code to emit datas in child component :
ngOnInit() {
Timeline.prototype.onTaskDoubleClick = function(task) {
console.log("Double click on task " + task.id);
console.log(task);
$('#editTask').click();
self.myTask.emit(task);
};
}
Timeline.prototype.onTaskDoubleClick is a function from a library.
I think you are not able to pass data into you modal component. try with componentInstance method.
openDialogEditTask() {
console.log(this.myTask);
let dialogRef = this.dialogEditTask.open(DialogEditTask, {
height: '90%',
width: '80%'
});
dialogRef.componentInstance.myTaskValue = this.myTask; //<- passing data into DialogEditTask component
dialogRef.afterClosed().subscribe(result => {
this.selectedOption = result;
});
}
in your DialogEditTask declare a variable myTaskValue: any;
you will get all your value you pass into DialogEditTask component in this myTaskValue variable

can use service inside a foreach()

import { Component, Input, Output, OnInit, OnChanges } from '#angular/core';
import { ViewComponent } from '../view/view.component';
import { HitoService } from '../../services/hito.service';
#Component({
selector: 'app-time-line',
templateUrl: './time-line.component.html',
styleUrls: ['./time-line.component.css'],
providers: [HitoService]
})
export class TimeLineComponent implements OnChanges, OnInit {
#Input() calbuscador: String;
#Input() idcalbuscador: String;
public pepe: String
nom_cal1: any;
hito1: any = {};
hito2: any = {};
constructor(public _hitoService: HitoService) {
}
ngOnInit() {
}
///we retrieve the value sento to this component in OnChanges
ngOnChanges() {
this.nom_cal1 = this.calbuscador;
this.drawtimeline(this.nom_cal1, this._hitoService, this.idcalbuscador);
}
result: any[] = [];
drawtimeline(nom_cal, _hitoService, idcalbuscador) {
var container = document.getElementById('timeLine');
//alert("id cal sele es :" + idcalbuscador);
var k = 0;
var j = 0;
var master = new vis.DataSet();
var items = new vis.DataSet();
this.result.push({ "_id": this.idcalbuscador,
"title": nom_cal });
this.result.forEach(function (ev) {
master.add([{ id: ev._id, content: ev.title, cal_id: ev._id }]);
var g = ev._id;
for (var i= 0; i<this.result.length; i++){
console.log("hola");
console.log(this.result[i]._id);
this._hitoService.getHitos(this.result[i]._id)
.subscribe(hito2 => {
this.hito2 = hito2
var items: any;
items = hito2;
items.forEach(function (item) {
items.add([{ id: k, group: g, start: item.start_datetime, end: item.end_datetime, style: itemStyle(item.design), className: "pepe" }]);
k++;
});
j++;
});
}
});
I am trying to implement a timeline using the vis.js, I retrieve the name and id of the timeline want in this class component then in the ngOnChanges I call the function to draw the timeline passing to it the name of the timeline, it's id and the services in other to get the observables item of the this specific timeline. I have an array that will store the timelines (result) I want to view and then an observable I subscribed to, to add the items of the timelines. The first foreach() will remove the first element in the result array, get the observables of items for that result and the second foreach() will go through the observables items and print the items, then it start over and move to the next. But all I get in the browsers console is : TypeError: this is undefined. Probably not making use of the service in the forach()
You can use an arrow function inside your forEach to keep using the enclosing scope.
this.result.forEach((ev) => {
// your code here
}
Assign current this to another variable (Say, that) then, use that into your callback.
Note: Inside your callback function this is changed to the JavaScript context by which the function is called.
const that = this; // save the current 'this' to 'that' variable
this.result.forEach(function (ev) {
master.add([{ id: ev._id, content: ev.title, cal_id: ev._id }]);
var g = ev._id;
for (var i= 0; i< that.result.length; i++){
console.log("hola");
console.log(that.result[i]._id);
that._hitoService.getHitos(that.result[i]._id)
.subscribe(hito2 => {
that.hito2 = hito2
var items: any;
items = hito2;
....
....
....

Angular2 : Bind view with callback variable

In my angular2 project, I read a csv file with FileReader. After the onloadend callback I have a variable which contain the content of my csv file.
Here my component.ts :
items: Array<any> = []
...
readCSV (event) {
let csvFileParseLog = this.csvFileParseLog;
r.onloadend = function(loadedEvt) {
devicesFile = files[0];
let csvFileParseLog = [];
parseDevicesCsvFile(contents) // One of my function which is an observable
.subscribe(newItems=> {
csvFileParseLog.push(newItems); // My result
},
exception => { ... }
);
};
}
I tried to bindcsvFileParseLog to my view by passing my value into items ... whithout success.
Here my componenet.html :
<div *ngFor="let c of csvFileParseLog">
{{ c.value }}
</div>
How can I display this content into my view component and loop on it with ngFor ?
r.onloadend = function(loadedEvt) {
should be
r.onloadend = (loadedEvt) => {
otherwise this won't work within that function.
and then just use
this.csvFileParseLog.push(newItems);
and just drop let csvFileParseLog = this.csvFileParseLog;
You also might need to inject
constructor(private cdRef:ChangeDetectorRef) {}
and call it in subscribe()
.subscribe(newItems=> {
this.csvFileParseLog.push(newItems);
this.cdRef.detectChanges();
},

Categories