1

I'm trying to create dinamically the elements of a table and I have a doubt.

How can I create new <tr> for example each 4 <td> and then continue creating new <td>

 <table>
    <tr>
    <td class="category-card" *ngFor= "let category of categories">

             <img class="product-card-image" src="{{category.icon}}">
             <span class="barImage">{{category.title}}</span>
    </td>
    </table>

EDIT:

<table>     
<ng-template *ngFor="let category of categories; let i = index">
            <tr *ngIf="(i % 4) == 0">
                <td class="product-card">
                    <img class="product-card-image" src="/app/assets/img/{{category.icon}}.png">
                    <span class="barImage">{{category.titulo}}</span>
                </td>
            </tr>
        </ng-template>
    </table>

EDIT2: I've added

<table [outerHTML]="categorias | dynamicTablePipe"></table> on my html

This is a external class (I've declared on NGModule)

@Pipe({
    name: 'dynamicTablePipe'
})
export class DynamicTablePipe implements PipeTransform{
    transform(contents) {
        let template = ``;
        let index = 0;
        for (let content of contents) {
            let currentTemplate = ``;
            if (index === 0) {
                // check if first record
                currentTemplate = `
                    <tr>
                        <td class="product-card">
                            <img class="product-card-image" src="/app/assets/img/${content.icono}.png">
                            <span class="barImage">${content.titulo}</span>
                    </td>`;
            } else if ((index % 4) === 0) {
                // check if multiple of 4
                currentTemplate = `
                    </tr>
                    <tr>
                   <td class="product-card">
                            <img class="product-card-image" src="/app/assets/img/${content.icono}.png">
                            <span class="barImage">${content.titulo}</span>
                    </td>`;
            } else {
                // normal row
                currentTemplate = `
                    <td class="product-card">
                            <img class="product-card-image" src="/app/assets/img/${content.icono}.png">
                            <span class="barImage">${content.titulo}</span>
                    </td>`;
            }
            index++;
            template += currentTemplate;
        }
        return template;
    }
}

The code inside of the pipe works fine, this is the template string formed at the end:

 <tr>
                            <td class="product-card">
                                <img class="product-card-image" src="/app/assets/img/smartphone.png">
                                <span class="barImage">Telefonia</span>
                        </td>
                        <td class="product-card">
                                <img class="product-card-image" src="/app/assets/img/gamepad.png">
                                <span class="barImage">Videojuegos</span>
                        </td>
                        <td class="product-card">
                                <img class="product-card-image" src="/app/assets/img/laptop.png">
                                <span class="barImage">Portatiles</span>
                        </td>
                        <td class="product-card">
                                <img class="product-card-image" src="/app/assets/img/speaker.png">
                                <span class="barImage">Reproductores</span>
                        </td>
                        </tr>
                        <tr>
                       <td class="product-card">
                                <img class="product-card-image" src="/app/assets/img/link.png">
                                <span class="barImage">Redes</span>
                        </td></tr>

The problem is in the moment to return

thanks

Hanzo
  • 1,839
  • 4
  • 30
  • 51
  • 2
    "jump to a new file" ---> whaaaat ? – n00dl3 May 17 '17 at 11:00
  • LOL, sorry. I mean, how can I create new table row each 4 cells, and then continue adding cells into this new row – Hanzo May 17 '17 at 11:16
  • honestly, "have 4 elements on each row" sounds like something that you should do in your CSS, not inside the markup – PeterT May 17 '17 at 11:26
  • @PeterT How can I restrict the number os columns/cells of a table via CSS? – Hanzo May 17 '17 at 12:18
  • 2
    @Hanzo I'm suggesting that you don't use a `` but instead just a string of elements that you then layout with css. Something like this: [Fiddle link](https://jsfiddle.net/o0pcp0r7/) (it should be said that I'm a css noob, so there's a lot of room for improvement there)
    – PeterT May 17 '17 at 13:24
  • @Hanzo, I updated my answer. Check **Option 3**. Hope it helps, I think it's the better option/approach! – SrAxi May 17 '17 at 14:13

2 Answers2

1

Instead of using the *ngFor in the <tr> or <td>, use it in <template> and depending on the index or amount of records found you could chose to render <td> or <tr>.

Follow this tutorial/guide that explains this exact thing: https://toddmotto.com/angular-ngfor-template-element

Rememeber to use <ng-template> for Angular v4 or <template> for Angular v2.

At the end, you could have something like:

<ng-template ngFor let-i="index" let-c="count" let-contact [ngForOf]="contacts | async">
    <tr *ngIf="if((i % 4) == 0)">
        // Check if index is multiple of 4
    </tr>
</ng-template>

This final example is the final code of the tutorial/guide I linked you. I used it so when you get to this point you have a friendly code.

Option 2:

Using <template> you could build a directive to render the data.

So you would have something like:

<template [myDirective]="myData"></template>

And in your myDirective logic you chose how to render data using an index within the data loop.

Option 3 - @Pipe:

The @Pipe could be the right one! You could try something like this:

@Pipe({
    name: 'renderTable'
})
class RenderTable {
    transform(content) {
        let template = ``;
        let index = 0;
        for (let row in content) {
            let currentTemplate = ``;
            if (index === 0) {
                // check if first record
                currentTemplate = `
                    <tr>
                        <td>${row}</td>`;
            } else if ((index % 4) === 0) {
                // check if multiple of 4
                currentTemplate = `
                    </tr>
                    <tr>
                    <td>${row}</td>`;
            } else {
                // normal row
                currentTemplate = `
                    <td>${row}</td>`;
            }
            index++;
            template += currentTemplate;
        }
        return template;
    }
}

In your table, something like:

<table [outerHTML]="categories | renderTable"></table>

Update:

Try with innerHTML:

<table [innerHTML]="categories | renderTable"></table>

Update 2:

For the style losing issue, here is the solution! Found it!

In RC.1 some styles can't be added using binding syntax

Community
  • 1
  • 1
SrAxi
  • 19,787
  • 11
  • 46
  • 65
  • 1
    I'm gonna try it. Thanks – Hanzo May 17 '17 at 12:19
  • Hi @SrAxi, I've added to my original question a code updated with your solution. Doesn't works for me. It doesn't render anything. Also, I'm using Angular 4 and If I use – Hanzo May 17 '17 at 13:47
  • I miss typed. Angular4 uses ``. Sorry for that! Try with a custom directive. Check what I did here: http://stackoverflow.com/questions/43395808/angular2-rendering-reloading-a-components-template With a custom directive that I created for my menu. The content inside my `
  • ` elements I get it from the component that is selected as a directive. Example: `selector: [myComponent]`.
  • – SrAxi May 17 '17 at 13:52
  • Hi, First thanks for your responses. I've edited my original question adding my test(EDIT2). First if I leave on (for let content `IN` contents) I can't access to object properties of content It says that I need a string. However if I use the code of my edited question it returns the error `DOMException: Failed to set the 'outerHTML' property on 'Element': This element has no parent node. at EmulatedEncapsulationDomRenderer2.DefaultDomRenderer2.setProperty`` What would be doing wrong? – Hanzo May 17 '17 at 21:28
  • the code inside of the pipe works fine, is at the moment to return de value when it returns the error: DOMException: Failed to set the 'outerHTML' property on 'Element': This element has no parent node. at EmulatedEncapsulationDomRenderer2.DefaultDomRenderer2.setPro‌​perty` I've updated the question with the final result template – Hanzo May 17 '17 at 22:06
  • @Hanzo Hello friend. Check my Update. Sorry if I am not managing to answer you a lot, is because things at work are crazy. Anyway, I hope that my answer, in it's whole helps you or helped you solve your problem. I think `innerHTML` should solve it. Let me know! :D – SrAxi May 18 '17 at 07:24
  • Hi @SrAxi, With `innerHTML` loads the contet but Its possible that when inject html code with a pipe, the styles doesn't works?: If you see my code I add to a css classname but when is rendered it dissappears. Even inspecting the code on browser it not be present on html. With class="classname" or style="..." it doesn't work. Thanks for your help – Hanzo May 18 '17 at 08:01
  • @Hanzo Hi! I'm very glad it worked out! I think the main goal of your OP has been solved! Nice! Now, I would suggest you create a new question with this specific matter: *Rendering html through `@Pipe` is losing css classes*. Is just to keep things separate, each problem isolated and approachable separately. Good work and have a nice day mate! ;) – SrAxi May 18 '17 at 08:20
  • @Hanzo Nevermind friend. I found the solution for the css styles issue. You need to sanitize. Check my second update! ;=) – SrAxi May 18 '17 at 08:23
  • 1
    Thanks. Works with style tag but no with class. – Hanzo May 18 '17 at 14:19