5

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.

Jim P.
  • 303
  • 1
  • 5
  • 15
  • You need to change the data structure – Ritwick Dey May 17 '18 at 15:44
  • If possible, I would like to keep the data structure the way it is and instead use Angular to solve the problem. – Jim P. May 17 '18 at 15:57
  • @JimP. you can keep the original DS and not change it but eventually you are going to have the take the values from the series property and set them to be the keys – Patricio Vargas May 17 '18 at 16:08
  • @Jim P. the answer with more botes should be the correct answer. Libraries exist for a reason. Being a better programmer also means being smart and use the resources that are out there... like the libraries/frameworks. you know in your case you are using Angular...if you don't want to use libraries at all, you should built your SPA using plan vanillaJS –  May 22 '18 at 14:13
  • I already know that, but I was looking for a solution without using a library since my current context does not allow me to do so. – Jim P. May 22 '18 at 15:25

3 Answers3

7

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.

Ritwick Dey
  • 18,464
  • 3
  • 24
  • 37
  • Object.keys(formatedData) won't work. template will give you an error – Patricio Vargas May 17 '18 at 16:01
  • 1
    Fixed. "For in" – Ritwick Dey May 17 '18 at 16:04
  • 1
    The for in won't work either it give you the error "Property binding ngForIn not used by any directive on an embedded template. " – Patricio Vargas May 17 '18 at 16:52
  • also if you change the "in" in your code to of you get the following error: " Cannot find a differ supporting object '[object Object]' of type 'object'. NgFor only supports binding to Iterables such as Arrays." I had to modify my answer too :( to get it with a working solution – Patricio Vargas May 17 '18 at 16:53
  • It works as wanted and does not involve an external library. I edited the HTML to repeat only the elements rows for each key. – Jim P. May 17 '18 at 17:40
3

You can easily archive this using underscore in your angular app.

how to use underscore.js library in angular 2

    groupedSeriesNames = []
        groupedSeries = []
            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 }
            ];

this.groupedSeries = _.groupBy(this.Certificate, certificate=>certificate.serie);

    this.groupedSeriesNames = Object.keys(this.groupedSeries)

The certificate.serie will become they key, you can change the certificate.serie to any other property like iden or whatever you need

your html

<ul class="cert-result">
    <li *ngFor="let key of groupedSeriesNames">
      <table *ngFor="let certificate of groupedSeries[key]">
        <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>
Patricio Vargas
  • 5,236
  • 11
  • 49
  • 100
-1
  <table >
    <tr>
      <th>Serie</th>
      <th>Element</th>
      <th>Composition</th>
    </tr>
    <ng-container *ngFor="let certificate of certs">
    <tr *ngIf="certificate.serie == '1050 AJ'">
      <td>{{certificate.serie}}</td>
      <td>{{certificate.ident}}</td>
      <td>{{certificate.moy_certified}}</td>
    </tr>
    <tr *ngIf="certificate.serie == 'X332.0 AC'">
      <td>{{certificate.serie}}</td>
      <td>{{certificate.ident}}</td>
      <td>{{certificate.moy_certified}}</td>
    </tr>
    <tr *ngIf="certificate.serie == 'X4002 AA'">
      <td>{{certificate.serie}}</td>
      <td>{{certificate.ident}}</td>
      <td>{{certificate.moy_certified}}</td>
    </tr>
    </ng-container>
</table>

Demo: https://stackblitz.com/edit/angular-3tvwgv

Not very concise. Hopefully other can give a better solution.

Ryan Huang
  • 728
  • 6
  • 7