Custom Component injected into table row - html

I have a table that is created in one componentA that has several table-row elements in the table. I then have another componentB that just has several table-row elements that will be reused throughout the app in the template code. The issue is that when using componentB inside of the table of componentA the table-row elements are not spanning the entire width of the table and seem to be broken outside of the table.
I have found references to several other similar issues, but they are using dynamic rows based on data that is passed in for each row. My issue is that I have a fixed set of rows that will be used throughout the app. I tried using the following examples:
--- componentA
<table>
<tr><td>Name</td></tr>
<tr componentB></tr> --- does not work
</table>
--- componentB --- selector: '[componentB]'
<tr><td>Phone Number</td></tr>
<tr><td>Email</td></tr>
--- componentA
<table>
<tr><td>Name</td></tr>
<componentB></componentB> --- does not work
</table>
--- componentB --- selector: 'componentB'
<tr><td>Phone Number</td></tr>
<tr><td>Email</td></tr>
Here is an example stackblitz of exactly the problem: https://stackblitz.com/edit/angular-ydsgaz
The elements in componentB should span the width of the table just like a normal element used in componentA, but they are not. Any help would be great!
ANSWER: either one of the following examples will work for this scenario
content-projection: https://stackblitz.com/edit/angular-dr6fqs
template reference (my preferred way): https://stackblitz.com/edit/angular-qgxzhv

Use angular content projection.
You just have to add a directive selector to table body.
app.component.html
<table>
<tbody app-table-rows>
<tr>
<td>name</td>
<td>bob</td>
</tr>
<tbody>
</table>
and then in table-rows.component.html add ng-content element. Angular will replace this ng-content element with content inside directive host element.
<ng-content></ng-content>
<tr>
<td>email</td>
<td>bob#gmail.com</td>
</tr>
...
Working example https://stackblitz.com/edit/angular-dr6fqs

Related

How to make custom table in custom element?

I am working on a custom element with lit-element. I want to make my own custom table element, but there are some problem.
First. It seemed browser allow tr, td tags for only table.
<!-- in html -->
<my-table>
<tr>
<td>A</td>
<td>B</td>
</tr>
</my-table>
<!-- chrome rendered -->
<my-table> A B </my-table>
Second. It could imitate with CSS, but CSS can't implement colspan.
<my-table> <!-- display: table -->
<my-tr> <!-- display: table-row -->
<my-td>A</my-td> <!-- display: table-cell -->
<my-td colspan="3">B</my-td> <!-- There is no way to implement colspan -->
</my-tr>
</my-table>
Third. I try to style with 'display: content' on host. It seemed to be work, but host has no data for box-sizing like clientHeight.
class MyTd extends LitElement {
render() {
return html`
<style>
:host { display: content; }
td {
all: inherit; /* It make to inherit host's style */
display: table-cell;
}
</style>
<td>
${html`<slot></slot>`} // I don't know why it work.
</td>
`;
}
}
// other js file.
const td = document.querySelector('my-td');
td.clientHeight; // 0
I can override clientHeight and something else (offsetHeight, getBoundingClientRect...) but I don't know it is proper and it is only way.
Is there any other way to create custom table or there is something wrong in my thinking?
This isn't really a good use case for a custom element - the question is why would I use <my-td> when <td> exists and works everywhere?
You can use column-count in CSS to imitate tables, but you have more severe issues - browsers (and related a11y tools) see the DOM for <table> as a structured table of data, while your <my-table> is just another custom element.
This breaks the DOM hierarchy for the table too - even though your elements wrap the related table elements they don't inherit or extend them.
So for your first example your DOM is something like this:
<my-table>
#shadow-root
<tr>
<td>A</td>
<td>B</td>
</tr>
</my-table>
Nothing the browser usually does to hook up cells to rows and rows to tables will work across those shadow DOM boundaries, and the <tr> is rendered exactly as if it was at the root of a new document fragment.
Likewise your DOM for the third attempt looks something like this:
<my-table>
#shadow-root
<table>
<my-tr>
#shadow-root
<tr>
<my-td>
#shadow-root
<td>
Each of those shadow roots is a new isolated fragment.
The question becomes what do you want to do with this structure?
Do you want something that looks like a table but uses custom elements for rows and cells consider using display: grid layouts instead. It's quite a bit more powerful than tables and in custom elements the CSS is much better supported than the rather inconsistent display: table-cell.
If you want something structured as a table consider putting the entire table in the shadow root and passing the data as a property
#customElement('my-table')
class MyTable
extends LitElement {
render() {
return html`
<table>
${this.items.map(i => html`
<tr>
<td>A</td>
<td>B</td>
</tr>`)}
</table>
`;
}
#property({attribute: false})
items: readonly RowRecordType[];
}
Then call this with a property prefix:
<my-table .items=${theListOfRows}></my-table>
Finally, if you want to extend <td> you can add to it with the is= syntax, but this was a bit of a dead end on the spec, isn't supported at all on Safari or Opera and is flakey on the rest.
You can extend a cell:
class MyCell
extends HTMLTableCellElement { ...
customElements.define('my-cell', MyCell, { extends: 'td' });
And then do:
<table>
<tr>
<td is="my-cell">A</td>
<td is="my-cell">B</td>
</tr>
</table>
However, this isn't supported by LitElement and you're better off implementing your custom web component directly.

Dynamically adding rows using components into HTML table with Angular

I'm encountering a strange problem and I cannot understand why it's happening.
I want to render an HTML table using Angular 4.
If I output the table using the following syntax everything works fine
<tbody>
<tr *ngFor="let athlete of athletes">
<td></td>
<td>{{athlete.name}}</td>
<td>{{athlete.country}}</td>
<td>{{athlete.time}}</td>
</tr>
</tbody>
And the table is rendered correctly:
But if do the same thing delegating the rending of the row to a component the table is not rendered correctly in Chrome (but is correct in Edge).
<tbody>
<app-athlete [athlete]="athlete" *ngFor="let athlete of athletes">
</app-athlete>
</tbody>
athlete.component.html
<tr>
<td></td>
<td>{{athlete.name}}</td>
<td>{{athlete.country}}</td>
<td>{{athlete.time}}</td>
</tr>
Could it be due to the fact that the "rendered" DOM is something like this when looking at Google Dev tools?
I also tried putting the <tr> with the *ngFor outside of the component but the problem is always the same.
I've reproduced the problem on Plunkr: https://plnkr.co/FZAFEP5S8KhzvdKwBtbH
Use attribute selector for your component
athlete.component.ts
#Component({
selector: 'tr[app-athlete]',
template: `
<td></td>
<td>{{athlete.name}}</td>
<td>{{athlete.country}}</td>
<td>{{athlete.time}}</td>
`,
})
export class AthleteComponent {
#Input() athlete: Athlete;
}
Now template of parent component should look like:
<tr app-athlete [athlete]="athlete" *ngFor="let athlete of athletes">
</tr>
Plunker Example

ng-repeat on td element

I have item collection and I want to display each item object inside it in a table.
I have an image to display for each item as well.
To do so, i decided to use < tbody ng-repeat>, with that,
each image can occupy 3 rows cell
The issue comes when i need to display calculations in another column, which is an array object reside for each item.
you can see it here http://jsbin.com/nifazanehe/3/edit?html,css,js,output
with background color red.
the first two index of calculations object is hard coded and the rest is using ng-repeat. It solves the problem yet it looks buttugly! i hate it!
any suggestion?
other solutions i can think of :
to nest a table so i can do ng-repeat for that individual row
or maybe just a simple
< ul>< li ng-repeat>
after empty out that section of rows and columns using rowspan and colspan
you can work with np-repeat-start and ng-repeat-end directive
<table>
<tbody>
<tr ng-repeat-start="demo in vm.demoArray">
<td >{{demo.attr1}}</td>
<td>{{demo.attr2}}</td>
</tr>
<tr ng-repeat-end>
<td>{{demo.attr1}}</td>
<td>{{demo.attr2}}</td>
</tr>
</tbody>
</table>

KendoUI table + AngularJS

I am trying to implement a generic table widget (using KendoUI) having the data binding done with AngularJS.
The table widget would look something like this in the HTML file (fiddle here: http://jsfiddle.net/mihaichiritescu/ULN36/35/):
<div ng:controller="Tester">
<gridview>
<div data-ng-repeat="man in people">
<gridviewcolumn datasource="name" man="man"></gridviewcolumn>
<gridviewcolumn datasource="age" man="man"></gridviewcolumn>
</div>
</gridview>
</div>
Basically, the table would have an ng-repeat that would repeat through the list of objects, and for each object, using the 'gridviewcolumn', I would add cells under each row.
This way, I am trying to replicate the structure of the KendoUI table, which is something like this:
​​<div id="grid">
<div class="k-grid-header"></div>
<div class="k-grid-content">
<table>
<colgroup></colgroup>
<tbody>
<tr>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</div>
<div class="k-pager-wrap k-grid-pager"></div>
<div>​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
So, using the ng-repeat, for each object I will dynamically add a new row, and for each column I will add a new cell on the last added row. Unfortunately, I am not able to manipulate the ng-repeat directive in such a way that I will properly replicate the internal structure of the KendoUI grid view. I am ending up with an internal table structure looking like this:
​<div id="grid">
<div data-ng-repeat="man in people" class="ng-scope">
<div datamember="name" man="man" class="ng-binding">name1</div>
<div datamember="age" man="man" class="ng-binding">21</div>
</div>
<div data-ng-repeat="man in people" class="ng-scope">
<div datamember="name" man="man" class="ng-binding">name2</div>
<div datamember="age" man="man" class="ng-binding">25</div>
</div>
<div class="k-grid-header"></div>
<div class="k-grid-content">
<table>
<colgroup></colgroup>
<tbody>
<tr>
<td></td>
<td></td>
</tr>
</tbody>
</table>
</div>
<div class="k-pager-wrap k-grid-pager"></div>
<div>​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​​
I would like to somehow place the content of the ng-repeat directive in the body of the table, not above it. Does anyone know how to accomplish this?
I could use jquery to place the content into the cells, but I would still have to remove/hide the ng-repeat directives and their content from above the table body, which I do not know how to do without some ugly hacks.
Also, I am not necessarily bound to KendoUI gridview, but it seems better looking than others, and it probably has similar internal structure to other table widgets, so I would encounter the same issue with other widgets too.
Do you guys have some ideas/advice on how to implement a generic table using AngularJS? I did search for some tables done with AngularJS, but I did not find something that would have good functionality and looks.
I have created two fiddles which would demonstrate how what you are trying to achieve could be done. The first fiddle uses ( http://jsfiddle.net/ganarajpr/FUv9e/2/ ) kendoui's grid ... So its style and display is complete. The only caveat being it wont update if the model changes. This is because kendoui takes the data first and then produces all the UI elements based on the model provided at the beginning.
The alternate is to use Kendo's UI (css) and leave out the grid producing code.
http://jsfiddle.net/ganarajpr/6kdvC/1/
This I believe is closer to what you were looking for. It also demonstrates the use of ng-repeat in a table.
Hope this helps.
Using contenteditable in html5 will easily help you.

GWT wrap a html table and add cels

I'm using gwt in my web app and I have a html panel which contains a <table>. I've chosen to do this instead of flextable due to some annoying issues when styling it as being unable to do <tbody valign="top">.
I wanted to know if it's possible to wrap a html table
<table>
<thead>
<tr>
<th>Name</th>
<th>Path</th>
<th>Type</th>
</tr>
</thead>
</table>
In a class like Flextable so I can easily control rows and columns through Java?
Something like
Label.wrap("id");
but for tables.
You can also format your data in FlexTable.
You can use flexTable.getRowFormatter(), flexTable.getColumnFormatter() or flexTable.getColumnFormatter().
You can apply alignment, style to rows, columns and cells using above FlexTable attributes.
Please provide a standalone test case if you are facing any problems with FlexTable.