How To Transclude Table Row Into AngularJs Component - html

Iv been trying to create an angularJS component for constructing a dynamic html table from a provided array.
The components template includes some ng-transclude directives as 'placeholders'/slots for templates provided by the component consumer.
Is it possible to transclude a <tr> template (with nested <th>), to be used inside the table <thead>?
I have tried using all variations for transclution:
transclude: true, transclude: 'element'
and transclude: { header: 'header'}
(the third option seems as the most suitable, as I will eventually required multiple elements to be transcluded).
Component:
app.component("virtualTable", {
bindings: {
someArray: "<"
},
transclude: {
header: "header"
},
templateUrl: () => {
return "/app/components/virtual-table.component.html";
});
Component Template:
<div>
<table>
<thead>
<tr ng-transclude="header"></tr>
</thead>
<tbody>
<tr ng-repeat="item in vt.someArray">
...another place for transclution...
</tr>
</tbody>
</table>
</div>
Consuming HTML (component declaration):
<virtual-table some-array="vm.personList">
<header>
<th>First Column</th>
<th>Second Column<th>
</header>
</virtual-table>
I excpect the compiled DOM to look like this:
<table>
<thead>
<tr>
<th>First Column</th>
<th>Second Column</th>
</tr>
</thead>
...
But instead I get the innerHTML of the transcluded element (header) as Text:
<table>
<thead>
<tr ng-transclude="header">
<header class="ng-scope">
First Column
Second Column
</tr>
</header>
...
I Suspect the browser (chrome 76) to somehow strip the <tr> tags from the transcluded element before it is being provided to the component (as it illegal us to use <tr> outside of a <table>).
I would like to maintain an html table structure.
Is The Any Way to Solve This?

Related

How can i apply colspan inside my nested thead component?

I built the following stackblitz example
https://stackblitz.com/edit/angular-y454qb?file=src%2Fapp%2Ftable-header%2Ftable-header.component.css
As you can see I have parent table table which has one child component called table-header
If I write this code directly in my table component then my th colspan attribute will get applied
and will get the whole width of the table
<thead>
<th class="tableHeader" colspan="12">my table header</th>
</thead>
but if I put the same code inside nested component - in my casetable-header
then the code does not work and the colspan attribute does not get applied to the table and does not get the full width of the table.
How can I fix this?
The problem is how the app-table-header component is rendered by Angular. If you look at the result of the table being rendered you will see something like....
result of original table render
<table>
<thead>
<th class="tableHeader" colspan="12">my table header</th>
</thead>
<app-table-header>
<thead>
<th colspan="12">my table header</th>
</thead>
</app-table-header>
</table>
When app-table-header rendered there is an extra element between the <table> and the <thead> which breaks the table.
A possible solution is to make app-table-header an attribute selector, [app-table-header] and use a thead in your table with the attribute selector <thead app-table-header>.
table-header.compnonent.ts
#Component({
selector: 'thead[app-table-header]',
templateUrl: './table-header.component.html',
styleUrls: ['./table-header.component.css']
})
export class TableHeaderComponent implements {
#Input()
public heading: string = '';
}
table-header.component.html
<!-- Remove the thead from here -->
<th class="tableHeader" colspan="12">{{ heading }}</th>
table.component.html
<table>
<thead>
<th class="tableHeader" colspan="12">my table header</th>
</thead>
<thead app-table-header heading="Passing heading down with #Input"></thead>
</table>
This will result in the below html
<table>
<thead>
<th class="tableHeader" colspan="12">my table header</th>
</thead>
<thead app-table-header>
<th colspan="12">Passing heading down with #Input</th>
</thead>
</table>
See stackblitz below
https://stackblitz.com/edit/angular-bojuuf
EDIT
Updated the selector to thead[app-table-header] as suggested by #
Mazedul Islam.
Added #Input() to show you can pass values down with inputs.
Updated stackblitz

How to use CSS grid with a dynamic number of columns and rows?

I'm trying to create a table in Angular with a dynamic number of columns and rows. I've found this easy to accomplish using the HTML table element by doing something like this:
<table class="html-table">
<tr class="headers">
<th>Name</th>
<th>Age</th>
<th>Sex</th>
<th *ngIf="columns == 4">Species</th>
</tr>
<tr>
<td>Ryan</td>
<td>30</td>
<td>M</td>
<td *ngIf="columns == 4">Human</td>
</tr>
</table>
In this example there might be some button that toggles the value of columns between 3 and 4.
Every explanation I have looked at online involves changing CSS variables, which seems like a somewhat hacky way to accomplish something that should be simple. Is there some way I can specify that something should be a new column in CSS grid rather than that having to specify the number of columns in grid-template-columns?
In Angular you can use ng-repeat to have a dynamic table
A typical example could be
<table ng-table="tableParams" class="table table-striped">
<tr ng-repeat="data in datas">
<td title="Title_Of_First_Variable">{{data.variable1}}</td>
<td title="Title_Of_Second_Variable">{{data.variable2}}</td>
<td title="Title_Of_Third_Variable">{{data.variable3}}</td>
...
</tr>
</table>
Of course with your controller you should pass your dynamic data into the correct $scope, in this case should be $scope.datas (usually an object)...maybe something like this, using NodeJS:
$http.post('route_of_your_method')
.success(function (result) {
$scope.datas = result;
})
.error(function (err) {
...
});
I explained fastly but i hope this is enough

VueJS: V-for not displaying as desired

How can I get v-for to display table data in the same form as the following html:
<tr>
<th scope="col">Name</th>
<th scope="col">Price</th>
<th scope="col">Product ID</th>
<th></th>
</tr>
Currently I am using the following vue v-for code(below) but it is adding the table header(th) one below another. I would like the table header to display side by side.
<tr v-for="(column, index) in schema" :key="index">
<th scope="col">{{column}}</th>
</tr>
You just need to place v-for on the element you want to repeat, not it's parent.
For loop should be put on <th></th>
<tr>
<th v-for="(column, index) in schema" :key="index">{{column}}</th>
</tr>
Here is the official vue doc for list rendering: https://v2.vuejs.org/v2/guide/list.html
bind grid header and data separately.
<template>
<table>
<thead>
<tr>
<th v-for="(header,index) in gridHeader" :key="index">
{{header.displayName}}
</th>
</tr>
</thead>
<tbody>
<tr v-for="(data, index) in gridData" :key="index" >
<td>
{{data.name}}
</td>
<td>{{data.age}}
</td>
<td>{{data.place}}
</td>
</tr>
</tbody>
</table>
</template>
<script lang="ts">
import Vue from 'vue';
export default class HelloWorld extends Vue {
private gridHeader: object[] = [
{name: 'Name', displayName: 'Name'},
{name: 'Age', displayName: 'Age'},
{name: 'Place', displayName: 'Place'}
];
private gridData: any =[{name:'Tony',age:'31',place:'India'},
{name:'Linju',age:'26',place:'India'},
{name:'Nysa',age:'12',place:'India'}];
};
</script>
<style scoped>
</style>

Convert a horizontal table to a vertical one in Ractivejs

I have a template like this, and I want ro make the table vertical(using partials is not an option in my environment, and I don't want to do it with inverted condition or duplicating html elements)
<table>
<tr>
<td>row1</td>
<td>row1</td>
<td>row1</td>
<tr>
</table>
The result should be:
<table>
<tr>
<td>row1</td>
</tr>
<tr>
<td>row1</td>
</tr>
<tr>
<td>row1</td>
<tr>
</table>
I already tried
var data = {
foo:true
}
<table>
<tr>
<td>row1</td>
{{#foo}}</tr><tr> {{/foo}}
<td>row1</td>
{{#foo}}</tr><tr> {{/foo}}
<td>row1</td>
<tr>
</table>
and
var data = {
foo:'</tr><tr>'
}
<table>
<tr>
<td>row1</td>
{{{foo}}}
<td>row1</td>
{{{foo}}}
<td>row1</td>
<tr>
</table>
A common misconception about Ractive is that because it uses the Mustache syntax, people think it acts like Mustache.
Mustache does string interpolation, where it replaces {{ }} to values that correspond in an object, and you get HTML. Ractive, on the otherhand, compiles the template into an AST. Then it uses that tree to create the DOM. It is similar to how Babel converts JSX into its object representation before feeding it to React, or how an HTML parser parses HTML, then constructing the DOM.
So for your scenario to work, you'll have to think in terms of properly-formed HTML:
Ractive.DEBUG = false;
const VerticalTableComponent = Ractive.extend({
template: `
<table border="1">
{{#tableItems}}
<tr>
<td>{{.}}</td>
</tr>
{{/}}
</table>
`
});
const app = VerticalTableComponent ({
el: '#app',
data: {
tableItems: ['foo', 'bar', 'baz', 'quz']
}
});
<script src="https://unpkg.com/ractive#0.8.12/ractive.min.js"></script>
<div id="app"></div>

Get the width of parent <td> from angularjs directive template

Im trying to figure out if its possible to get the width of the parent my directive is going to be placed in.
Keeping it simple I have some directive my-directive which Im placing inside of a table:
<table>
<tr>
<td><my-directive ..../></td>
<td><my-directive ..../></td>
<td><my-directive ..../></td>
<td><my-directive ..../></td>
</tr>
</table>
And inside my-directive Iwant to know the width of my parent <td> so I could display my content properly - with more or less data according to the width
Can it be done?
Update:
My HTML code:
<table class="table">
<thead>
<tr>
<td width="5%"></td>
<td width="5%"></td>
<td width="85%"></td>
</tr>
<tr>
<th>Col 5</th>
<th>Col 5</th>
<th>Col C 85</th>
</tr>
</thead>
<tbody>
<tr>
<td><trim value="Some value a"></trim></td>
<td>Some value b</td>
<td>Some value c</td>
</tr>
</tbody>
</table>
My directive:
angular.module('ui.directives', []).directive('trim', function() {
return {
restrict: 'E',
scope: {
value: '#'
},
template: "{{value | limitTo:6}}"
};
}
);
From within the template I can limit by a constant I define (or pass by, from outside. But I want to change that, to know myself, from within that directive if I should trim the text or not
Yes you can get the parent element using angular's jqlite, a subset of jquery in the link function. The link function is to provide the behavior to your directive, in this function you can get the directive element and modify it at run time according to your need.
angular.directive('myDirective', [function($document) {
return {
restrict:'E',
link: function(scope, element, attribute) {
var parent = element.parent();// will give an array
var parentWidth = parent[0].offsetWidth;
//modify/truncate your attribute.value depending upon the parentWidth.
}
}
}]);