Search code examples
angularangular6angular-ngfor

Angular 6 ngFor list of tables grouped by key


My Angular 6 app needs to displays a list of tables, where a table is a group of chemical analyses of elements of its composition.

Lets say I have a metal alloy A. I perform different compound analyses on it to find its chemical composition: Fe: 0.001%, Cu: 0.042%, etc.

Here is my data source, which is only a typescript file with mocks

import { Certificate } from './certificate';

export const CERTIFICATES: Certificate[] = [
    { serie: '1050 AJ', ident: 'Fe', moy_certified: 0.297 },
    { serie: '1050 AJ', ident: 'Cu', moy_certified: 0.04 },
    { serie: '1050 AJ', ident: 'Mn', moy_certified: 0.0374 }, 
    { serie: 'X332.0 AC', ident: 'V', moy_certified: 0.019 },
    { serie: 'X4002 AA', ident: 'Mn', moy_certified: 0.037 }
];

I would like to display this data, in HTML using Angular 6, in a list of tables, where each analyses of a series are grouped like this:

Serie: 1050 AJ
-------------------------
| Element | Composition |
-------------------------
|    Fe   |    0.0297   |
-------------------------
|    Cu   |    0.04     |
-------------------------
|    Mn   |    0.0374   |

Serie: X332.0 AC
-------------------------
| Element | Composition |
-------------------------
|    V    |    0.019    |

Serie: X332.0 AC
-------------------------
| Element | Composition |
-------------------------
|    Mn   |    0.037    |

My HTML file for now looks like this

<ul class="cert-result">
    <li *ngFor="let certificate of certificates">
      <table>
        <tr>
          <th>Serie</th>
          <th>Element</th>
          <th>Composition</th>
        </tr>
        <tr>
          <td>{{certificate.serie}}</td>
          <td>{{certificate.ident}}</td>
          <td>{{certificate.moy_certifiee}}</td>
        </tr>
      </table>
    </li>
  </ul>

And obviously, this isn't the right way to do it since it makes a table for each elements of my data source.


Solution

  • You have to change the data structure.

    Solution.

    your data

    export const CERTIFICATES: Certificate[] = [
        { serie: '1050 AJ', ident: 'Fe', moy_certified: 0.297 },
        { serie: '1050 AJ', ident: 'Cu', moy_certified: 0.04 },
        { serie: '1050 AJ', ident: 'Mn', moy_certified: 0.0374 }, 
        { serie: 'X332.0 AC', ident: 'V', moy_certified: 0.019 },
        { serie: 'X4002 AA', ident: 'Mn', moy_certified: 0.037 }
    ];
    

    Create a method in your component. let say formatedData()

    import { CERTIFICATES } from './certificate';
    
    
    class AppComponent {
      //Todo...
    
      objectKey(obj) {
        return Object.keys(obj);
      }
    
      formatedCerts() {
          return CERTIFICATES.reduce((prev, now) => {
            if (!prev[now.serie]) {
              prev[now.serie] = [];
            }
    
            prev[now.serie].push(now);
            return prev;
          }, {});
    
        /*
           Now your data : { "1050 AJ": [ .... ], "X332.0 AC": [...], ... }
        */
    
      }
    
    }
    

    Now in template:

        <ul class="cert-result">
          <li *ngFor="let key of objectKey(formatedCerts())">
            <span>{{key}}</span>
            <table>
              <tr>
                <th>Élément</th>
                <th>Moy. Certifiée</th>
              </tr>
              <tr *ngFor="let certificate of formatedCerts()[key]">
                <td>{{certificate.ident}}</td>
                <td>{{certificate.moy_certifiee}}</td>
              </tr>
        </table>
          </li>
        </ul>
    

    If you want to optimize, store the data of formatedCerts() into a variable.