Search code examples
arraysangularobservablezero

How can I populate Angular Mat-Table with Observable


I'm attempting to pull data from an AWS DynamoDB table and display it in an Angular Mat-Table. When I do this, the array created is returning a length of 0 even though there are 12 values in it. I believe this is preventing the Mat-Table from acting on the dataSource change and ultimately displaying the data.

Console Results

Observable {_isScalar: true, _subscribe: ƒ, value: Array(0)}
value: (12) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
_isScalar: true
_subscribe: ƒ (subscriber)
__proto__: Object

Component code

constructor(private dynamoService: DataDynamoService) {
    let dataSource: Observable<Record[]> = 
this.dynamoService.getAllRecords();
    console.log(dataSource);
}

Service function code

getAllRecords(): Observable<Record[]> {
  let dynDoc = {
  TableName: "records-table",
  };

  let retData: Record[] = [];
  Auth.currentCredentials()
    .then(credentials => {
      const dynamoDB = new DynamoDB.DocumentClient({
        credentials: Auth.essentialCredentials(credentials),
        region: 'us-east-1',
      });

      dynamoDB.scan(dynDoc).promise()
        .then(function(data){
          for(let record of data.Items){
            let rec = {
              value1: rec.value1,
              value2: rec.value2,
              value3: rec.value3,
              value4: rec.value4
            }
            retData.push(rec);
           }
        })
  })
  return of(retData);
}

I expect Array(12) for length, but am receiving 0

This is the service code that ultimately worked:

getAllRecords() {
  let dynDoc = {
    TableName: "my-table-name",
  };

  return Auth.currentCredentials()
    .then(credentials => {
      const dynamoDB = new DynamoDB.DocumentClient({
        credentials: Auth.essentialCredentials(credentials),
        region: 'us-east-1',
      });

      return dynamoDB.scan(dynDoc).promise()
        .then(data => {return of(data.Items);
        })
    })
}

Solution

  • retData is filled after the promises are completed, but the function doesn't wait for it. So it will always be []

    Component code

    _dataSource: any[] = []; <-- Data bound to mat-table
    
    constructor(private dynamoService: DataDynamoService) {
        this.dynamoService.getAllRecords().then(data => { // <-- value on promise completion
          this._dataSource = data; // <-- set MatTable source to promise values
          console.log(data, this._dataSource);
        });
    }
    
    

    Service code

    getAllRecords(): Promise<Record[]> { // <-- Return a promise, not observable
      let dynDoc = {
      TableName: "records-table",
      };
    
      let retData: Record[] = [];
      return Auth.currentCredentials() // <--- return outer promise
        .then(credentials => {
          const dynamoDB = new DynamoDB.DocumentClient({
            credentials: Auth.essentialCredentials(credentials),
            region: 'us-east-1',
          });
    
          return dynamoDB.scan(dynDoc).promise() // <-- return inner promise 
            .then(function(data){
              for(let record of data.Items){
                let rec = {
                  value1: rec.value1,
                  value2: rec.value2,
                  value3: rec.value3,
                  value4: rec.value4
                }
                retData.push(rec);
               }
    
              return retData; // <-- return the data outside the loop, still inside promise
            })
      })
    }
    

    Template code (for component)

    
    <mat-table [dataSource]="_dataSource">
    
    ...
    
    </mat-table>
    
    

    If DynamoDB has the option to use observables I would switch to that though.