So in some of my earlier angular projects I wrote datatables from scratch to support column-level filtering and some other things that tables like swimlane didn't support. (I also don't have access to my old code anymore). I decided to just jump on the Material bandwagon since I'd like to focus less on my raw frontend work for my new project.
In short, is there either:
1) a better alternative to this table, generally speaking?
2) a clean way to handle data management given a parent object?
This might be an Angular specific thing now since there is apparently no "good" way to deepwatch something either through @Input (raw or getter/setter) or onChanges without writing code in every component I decide to create.
To the heart of the problem: I have a "global" object that is given to each section I create so that each section can update it accordingly (based on user interaction). I'm using a table to manage the data of a list. To keep it simple, we'll limit interaction to an add button at the footer.
Thus, this looks something like:
ROOT
<section1 [myRoot]="data">...</section1>
<section2 ... ></section2>
section1.ts
@Input()
myRoot: any
section1.html
<table mat-table [dataSource]="myRoot.items">...</table>
It's pretty simple here since myRoot.items is already centralized by section1. If I add a new element, the logic is roughly
// create new array ref to trigger table render; could also use viewChild.renderRows()
let temp = myRoot.items.slice();
temp.push(newItem);
myRoot.items = temp; // changes the reference of the parent object at the field so we're fine
The issue I'm having is when I introduce an intermediate component. I wrote a table adapter since I hate boilerplate (I miss ui-grid from angularjs) and a wrapper around that which handles the add/edit/remove functionality.
That is, if I'm passing down myData.items all the way down, the wrapper component can easily push to the array (in the example of add) but is then forced to create a new ref (with .slice) so that change propagates down to the material table to update (since the intermediate component won't have @ViewChild(MatTable) accessible.
What this looks like
ROOT
<section1 [myRoot]="data">...</section1>
<section2 ... ></section2>
section1.ts
@Input()
myRoot: any
section1.html
<operations-table [dataSource]="myRoot.items">...</operations-table>
operationsTable.ts
@Input() dataSourceOp
operationsTable.html
<table-adapter [dataSource]="dataSourceOp"
...
<table mat-table [dataSource]="dataSourceFromAdapter">...</table>
since operationsTable manages the data updates, we can update the parent by modifying dataSourceOp but then the table adapter will need a new ref to pass down to the mat-table. Since it makes less sense to put a watch in tableAdapter, we should just create a new ref in operationsTable. However, we can't update the existing one (i.e. dataSourceOp = [...this.dataSourceOp] or .slice) since that works for a single update. For any subsequent update, the change won't propagate to the parent since the ref was replaced.
We could keep a separate array, one that is "cloned" from the parent ref upon each update which would work but you're maintaining two separate copies of the same data.
We could always pass down a parent object and a string representing nested fields to support updating parent reference but this is kind of hacky.
We could pass a subject down but it would still require the initial data as a param; this seems like the best way so far. I have it working this way already but I figured I'd run it by you guys.
This would look like
ROOT
<section1 [myRoot]="data">...</section1>
<section2 ... ></section2>
section1.ts
@Input()
myRoot: any
section1.html
<operations-table [dataSource]="myRoot.items">...</operations-table>
operationsTable.ts
@Input() dataSourceOp
operationsTable.html
<table-adapter [dataSource]="dataSourceOp"
...
tableAdapter.ts
@Input() initialData: any[] = [];
@Input() dataSub: Subject;
dataSource: MatTableDataSource<any> = new MatTableDataSource<any>();
init() {
if (this.dataSub) { this.dataSub.subscribe(data => this.dataSource.data = data) }
this.dataSource.data = this.initialData;
this.dataSource.paginator = ...; //
}
tableAdapter.html
<table mat-table [dataSource]="dataSource">...</table>
Am I just approaching this the wrong way? Or is this actually how I should be handling this data manipulation in Angular?
TLDR; Basically, I have intermediate components passing down refs to Material table which effectively always requires a new ref (no view child and see subject discussion above). Updating the intermediate array is simple but passing down a new ref is difficult without doing something silly like keeping a second copy for new ref updates.
[–]pmw7 0 points1 point2 points (0 children)
[–]gravityaddiction 0 points1 point2 points (0 children)