I have posted the full source code for a working solution on Github.
First, you need to declare an interface so TypeScript knows what data type your JSON is. For simplicity, you can match the interface property names to the JSON property names. You can do this in your component.ts
file directly, or an external .ts
file.
// Create an interface for your data, so TypeScript knows what datatype to work with when
// putting your data in the table.
export interface TableDataInterface {
Id: string;
ColumnIndex: number;
ColumnSpan: number;
RowIndex: number;
RowSpan: number;
Cell: string;
}
Now in your component.ts
file, declare three arrays: One for your input data (to be read in from the JSON response), one for the table data (what you are going to bind to in the HTML), and one for the table column definitions (something required by Angular for this technique):
export class YourComponent {
// ...
// Declare a property on your component to hold the data for your table,
// and initialize it to an empty array.
inputData: TableDataInterface[] = []
// Declare a property on your component to hold the data to be displayed.
// This is what you will bind to from the HTML.
tableData: string[][] = [];
// Declare a property to hold the list of columns to display.
tableColumns: string[] = [];
// ...
}
Inside of the appropriate function inside of your component class (I just wrote mine inside of ngOnInit()
), you now have 4 steps to complete.
First, get the data from whatever source you are getting it from. The code below uses a local JSON file. I have a post on how to do this here. This is good for testing, before you connect to live data from an API. Once you connect to the live data source, you will want to replace the content of the getData()
function with your API code.
import Table from './data/test-data.json'; // Added to support reading data from local json file.
import { TableDataInterface } from '../types/table-data.interface'; // import your interace.
// Added to support reading data from local json file.
let table_data = Table.Table;
export class YourComponent {
// ...
// Use the initialization (or whatever other appropriate function) to populate the
// data of your table.
ngOnInit(): void {
// 1. Get the data from whatever source you are getting it from.
this.inputData = this.getData();
// The next three steps go here ...
}
// ...
// Put your code to get the table data in this function. Here a hard-coded value is
// used as an example.
getData(): TableDataInterface[] {
// NOTE: if your interface properties do not match exactly the json, you will
// need to convert between the two here before returning. In this example
// the two match, so the data can be directly returned.
return table_data[0].TableData;
}
// ...
}
Second, create the 2D array to bind to based on the data in your JSON response:
// 2. Create a 2D array with a value for each cell initialized to empty string.
for (var row: number = 0; row < table_data[0].NumRows; row++) {
// Initialize the row.
this.tableData[row] = [];
for (var col: number = 0; col < table_data[0].NumCols; col++) {
// Initialize the cell in the row.
this.tableData[row][col] = "";
}
}
Third, create the columns array for Angular to draw from when rendering the table:
// 3. Create an entry in the table columns array for each column.
for (var col: number = 0; col < table_data[0].NumCols; col++) {
// Angular requires a unique name for each column, so we will just
// use the index of that column for the name.
this.tableColumns[col] = col.toString();
}
Fourth, replace the values of the initialized 2D array with the values from your JSON response, using the ColumnIndex and RowIndex in the JSON response.
// 4. Now replace the values in the table data with the ones you want displayed.
for (var inputIndex: number = 0; inputIndex < this.inputData.length; inputIndex++) {
// NOTE: the -1 for the column and row is to account for the fact that the input data
// is one-indexed, while the arrays are zero-indexed.
this.tableData[this.inputData[inputIndex].RowIndex - 1][this.inputData[inputIndex].ColumnIndex - 1] = this.inputData[inputIndex].Cell;
}
Finally, the HTML in your component.html
file will be rather short:
<!-- Bind the data source to the 2D data array in your corresponding .ts file. -->
<table mat-table [dataSource]="tableData">
<!-- Define the column templates. Use an ngFor to create a template for
each column in the array of columns. -->
<mat-text-column *ngFor="let data of tableColumns"
[name]="data"></mat-text-column>
<!-- Uncomment the following line if you want column headers. -->
<!--<tr mat-header-row *matHeaderRowDef="tableColumns"></tr>-->
<tr mat-row *matRowDef="let row; columns: tableColumns;"></tr>
</table>
The result, using the JSON given in the question, will look like this (screenshot from running the code posted on the Github on a localhost):

If things look misaligned, play with the table styling. In the above screenshot, I used the following CSS:
table {
width: 600px;
margin: 20px;
}
Important limitation: This solution currently assumes no cell data spans multiple rows or columns. Adding that in makes things considerably harder!