Create sparse Html table - html

I want to create HTML table like following
# Class Method A b c d
1 User get 10 20 30 40
set 40 30 20 10
find 40 30 20 10
2 Profile get 10 20 30 40
set 40 30 20 10
find 40 30 20 10
I have the following structure
export class Profiler {
constructor(
public classz: string,
public methodProfilers: {[key: string]: MethodProfiler;}
) {}
}
export class MethodProfiler {
constructor(
public count: number,
public totalTime: number,
public lastTotalTime: number,
public maxTime: number,
public avgTime: number,
public avgMemory: number
) {}
}
Is it possible to create such Html table using angular 4 *ngfor ? I am getting list of Profiler from back end.
getProfilerKeys(methodProfilers: Map<string, MethodProfiler>) {
return Object.keys(methodProfilers);
}
<div class="table-responsive">
<table class="table table-sm table-bordered">
<thead class="thead-default">
<tr>
<th class="text-center">Classz</th>
<th class="text-center">Method</th>
<th class="text-center">Count</th>
<th class="text-center">TotalTime</th>
<th class="text-center">LastTotalTime</th>
<th class="text-center">MaxTime</th>
<th class="text-center">AvgTime</th>
<th class="text-center">AvgMemory</th>
</tr>
</thead>
<tbody>
<tr class="text-center" *ngFor="let profiler of page.content;">
<td>{{profiler.classz}}</td>
<td>
<table>
<tr *ngFor="let key of getProfilerKeys(profiler.methodProfilers);">
<td>{{key}}</td>
</tr>
</table>
</td>
<td>
<table>
<tr *ngFor="let key of getProfilerKeys(profiler.methodProfilers);">
<td *ngFor="let subkey of getProfilerKeys(profiler.methodProfilers[key]);">{{profiler.methodProfilers[key][subkey]}}</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
</div>

It is more easy to achieve IMHO if you expand out the *ngFor into its <ng-template> equivalent.
Then create one td for each Profile and set the rowspan attribute on the td to be the number of "ProfilerKeys" in the array.
I use the function isNewIndex() to detect when the outer loop (Profiler objects) changes its index to draw another row-spanned td.
import { Component } from '#angular/core';
import { Profiler, MethodProfiler } from './profile';
#Component({
selector: 'my-app',
template: `
<table class="table table-sm table-bordered" width="100%">
<thead class="thead-default">
<tr>
<th class="text-center">#</th>
<th class="text-center">Class</th>
<th class="text-center">Method</th>
<th class="text-center">Count</th>
<th class="text-center">TotalTime</th>
<th class="text-center">LastTotalTime</th>
<th class="text-center">MaxTime</th>
<th class="text-center">AvgTime</th>
<th class="text-center">AvgMemory</th>
</tr>
</thead>
<tbody>
<ng-template ngFor let-profiler [ngForOf]="profilers" let-i="index">
<ng-template ngFor let-method [ngForOf]="getProfilerKeys(profiler.methodProfilers)">
<tr class="text-center">
<td *ngIf="isNewIndex(i)" [attr.rowspan]="getProfilerKeys(profiler.methodProfilers).length">{{i + 1}}</td>
<td *ngIf="isNewIndex(i, true)" [attr.rowspan]="getProfilerKeys(profiler.methodProfilers).length">{{profiler.classz}}</td>
<td>{{method}}</td>
<td>{{profiler.methodProfilers[method].count}}</td>
<td>{{profiler.methodProfilers[method].totalTime}}</td>
<td>{{profiler.methodProfilers[method].lastTotalTime}}</td>
<td>{{profiler.methodProfilers[method].maxTime}}</td>
<td>{{profiler.methodProfilers[method].avgTime}}</td>
<td>{{profiler.methodProfilers[method].avgMemory}}</td>
</tr>
</ng-template>
</ng-template>
</tbody>
</table>
`
})
export class AppComponent {
lastIndex = -1;
getProfilerKeys(methodProfilers: Map<string, MethodProfiler>) {
return Object.keys(methodProfilers);
}
// on last call to this function per row pass true for the updateIndex param
isNewIndex(thisIndex: number, updateIndex : boolean) {
if (thisIndex === this.lastIndex) return false;
if (updateIndex === true) this.lastIndex = thisIndex;
return true;
}
profilers = [
new Profiler('User',
{
'get' : new MethodProfiler(10,20,30,40,50,60),
'set' : new MethodProfiler(1,2,3,4,5,6)
}
),
new Profiler('Profile',
{
'get' : new MethodProfiler(60,50,40,30,20,10),
'set' : new MethodProfiler(6,5,4,3,2,1)
}
)
];
}
Demo: https://plnkr.co/edit/B5YYn9Jxx9nQCzxlr2VS?p=preview

Related

Thymeleaf looping over list of objects with list of objects attribute problem

I'm working in Spring Boot and I have a problem rendering with Thymeleaf a table with different lines. First must be a String, and the subsequent lines must be the data saved in a list of objects.
situation of the problem:
I have a list of objects, this object has two attributes, one is a list of Strings, and the other one is a list of different objects. I don't know how to render in Thymeleaf in a table the first attribute of a string list in a line, and on the next lines of the table render the second list of attribute object.
details of the object:
public class objetosDeServiciosAD {
private String Servicio;
private LinkedList<usuarioAD> listaUsuariosAD;
public String getServicio() {
return Servicio;
}
public void setServicio(String servicio) {
Servicio = servicio;
}
public LinkedList<usuarioAD> getListaUsuariosAD() {
return listaUsuariosAD;
}
public void setListaUsuariosAD(LinkedList<usuarioAD> listaUsuariosAD) {
this.listaUsuariosAD = listaUsuariosAD;
}
#Override
public String toString() {
return "objetosDeServiciosAD [Servicio=" + Servicio + ", listaUsuariosAD=" + listaUsuariosAD + "]";
}
}
objetos_Servicios is a list of objects with two atributes, one is servicio
this object has a second attibute which is a list of objects, this is listaUsuariosAD.
This is my code in Thymeleaf:
<table class="table table-hover">
<thead class="thead-light">
<tr>
<th scope="col">Usuario</th>
<th scope="col">Teléfono</th>
<th scope="col">mail</th>
<th scope="col">Descripción</th>
</tr>
</thead>
<tbody>
<tr th:each="servicio : ${objetos_Servicios}">
<td th:text="${servicio.servicio}"></td>
<tr th:each=" listaeusuario : ${servicio.listaUsuariosAD}">
<tr th:each ="usuarios : ${listaeusuario}">
<td th:text = "${usuarios.usuario}"></td>
<td th:text = "${usuarios.telefono}"></td>
<td th:text = "${usuarios.mail}"></td>
<td th:text = "${usuarios.descripion}"></td>
</tr>
</tr>
</tbody>
</table>
The code will look something like this:
<table class="table table-hover">
<thead class="thead-light">
<tr>
<th scope="col">Usuario</th>
<th scope="col">Teléfono</th>
<th scope="col">mail</th>
<th scope="col">Descripción</th>
</tr>
</thead>
<tbody>
<th:block th:each="servicio : ${objetos_Servicios}">
<tr>
<td th:text="${servicio.servicio}" />
</tr>
<tr th:each = "lista : ${servicio.getListaUsuariosAD()}">
<td th:text="${lista.usuario}"></td>
<td th:text="${lista.telefono}"></td>
<td th:text="${lista.mail}"></td>
<td th:text="${lista.Descripcion}"></td>
</tr>
</th:block>
</tbody>
</table>
You can use a th:block tag to loop over a larger block of code (that contains the header <tr /> and the rows <tr />).
I recommend changing the naming standards you are using, so that all your class names begin with an upper-case letter - for example: ObjetosDeServiciosAD instead of objetosDeServiciosAD. This is standard in Java - and not doing this can be confusing for other people who read your code.
So, your class becomes:
import java.util.List;
public class ObjetosDeServiciosAD {
private String servicio;
private List<UsuarioAD> listaUsuariosAD;
public String getServicio() {
return servicio;
}
public void setServicio(String servicio) {
this.servicio = servicio;
}
public List<UsuarioAD> getListaUsuariosAD() {
return listaUsuariosAD;
}
public void setListaUsuariosAD(List<UsuarioAD> listaUsuariosAD) {
this.listaUsuariosAD = listaUsuariosAD;
}
}
I also replaced LinkedList with List, since you do not appear to need a linked list here (if you actually do, you can revert that change).
Then, for your Thymeleaf template, you can use Thymeleaf's <th:block> tag to structure your iteration loops:
<table class="table table-hover">
<thead class="thead-light">
<tr>
<th scope="col">Usuario</th>
<th scope="col">Teléfono</th>
<th scope="col">mail</th>
<th scope="col">Descripción</th>
</tr>
</thead>
<tbody>
<th:block th:each="servicio : ${objetos_Servicios}">
<tr>
<td th:text="${servicio.servicio}" />
<td></td>
<td></td>
<td></td>
</tr>
<tr th:each = "lista : ${servicio.listaUsuariosAD}">
<td th:text="${lista.usuario}"></td>
<td th:text="${lista.telefono}"></td>
<td th:text="${lista.mail}"></td>
<td th:text="${lista.descripcion}"></td>
</tr>
</th:block>
</tbody>
</table>
In the above code, I also replaced ${servicio.getListaUsuariosAD()} with the simpler ${servicio.listaUsuariosAD}, since you do not need to explicitly call the method, here.
I also added three empty <td></td> cells to ensure each row is complete, for the row displaying the servicio text.

GEt element By id angular 7

hello I need to retrieve the values entered by a form after the post method so I inserted at the service level this code :
list:Client[];
constructor(private http:HttpClient) { }
postClient(formData:Client){
return this.http.post(this.rootURL+'/Clients/',formData);
}
putClient(formData:Client){
return this.http.put(this.rootURL+'/Clients/'+formData.Engagement,formData);
}
getClient(formData:Client){
return this.http.get(this.rootURL+'/Clients/GetClientByName/'+formData.Engagement);
}
and at the component level like this :
getClient(form:NgForm){
this.clientservice.getClient(form.value).subscribe(
res =>{this.client = res as Client}
)
}
and in the HTML code this:
<table class="table table-hover">
<tr>
<th class="tname">Client</th>
<th class="tname">Enagement</th>
<th class="tname">ERP</th>
</tr>
<tr *ngFor="let clt of client">
<td >{{clt.Clientname}}</td>
<td >{{clt.Engagement}}</td>
<td >{{clt.ERP}}</td>
and I can’t get the values by ID with the get I don’t know what is the problem I have neither result or error message
I think your http service(this.rootURL+'/Clients/GetClientByName/) return Client[] not Client.
So, You should cast like this.
this.clientservice.getClient(form.value).subscribe(
res =>{this.client = res as Client[]}
)
But, your response is fixed
{"Engagement":"56789","Clientname":"ClLIENT","ERP":"ERP"}
at component level, no need to edit
getClient(form:NgForm){
this.clientservice.getClient(form.value).subscribe(
res =>{this.client = res as Client}
)
}
you should edit html file.
<table class="table table-hover">
<tr>
<th class="tname">Client</th>
<th class="tname">Enagement</th>
<th class="tname">ERP</th>
</tr>
<tr>
<td >{{client.Clientname}}</td>
<td >{{client.Engagement}}</td>
<td >{{client.ERP}}</td>
</tr>
---------------------------
or need to use ngFor loop, edit component level. and no need to edit component level.
client: Client[] = [];
getClient(form:NgForm){
this.clientservice.getClient(form.value).subscribe(
res =>{
const cli = res as Client;
this.client.length = 0;
Array.prototype.push.apply(this.client, cli)
}
)
}

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>

Array not getting updated in DOM in Angular 2

I am trying to update my array of contacts type on DOM. However I am getting every value when I am calling that array in a function but that value of array is not getting updated in DOM.
import {Component} from 'angular2/core';
#Component({
selector:'contacts',
templateUrl:'./dev/RouteComponents/contacts.component.html'
})
export class ContactsComponent{
checking=false;
contacts=[
{firstname:'vishu',lastname:'handa',contactno:'12654',gender:'male'},
{firstname:'lalit',lastname:'gupta',contactno:'56489',gender:'male'},
{firstname:'aman',lastname:'singh',contactno:'48984',gender:'male'},
{firstname:'gaurav',lastname:'gupta',contactno:'5485',gender:'male'}
];
addContactToContactList(firstname,lastname,contact,gender)
{
console.log(firstname.value+"--"+lastname.value+"--"+contact.value+"--"+gender.value);
this.contacts.push({firstname:firstname.value,lastname:lastname.value,contactno:contact.value,gender:gender.value});
this.run();
}
run()
{
console.log(this.contacts.length);
this.checking = true;
}
}
<p *ngIf="checking">checking</p>
<table class="table table-bordered table-hover table-condensed">
<thead>
<tr>
<th>First Name</th>
<th>Last Name</th>
<th>Contact Name</th>
<th>Gender</th>
</tr>
</thead>
<tbody>
<tr *ngFor="#contact of contacts">
<td>{{contact.firstname}}</td>
<td>{{contact.lastname}}</td>
<td>{{contact.contactno}}</td>
<td>{{contact.gender}}</td>
</tr>
</tbody>
</table>
Value is not getting updated in DOM
Change your table rows tr tag for *ngfor as below and check it again :
<tr *ngFor="let contact of contacts">
<td>{{contact.firstname}}</td>
<td>{{contact.lastname}}</td>
<td>{{contact.contactno}}</td>
<td>{{contact.gender}}</td>
</tr>

How to bind Knockout model data to Multiple tables

I am using knockout binding to bind some data into html tables. My knockout view Model had multiple products and each product will have multiple chars. I want to display the products in one table and when i select the link "show chars" it should display the corresponding chars in below table.
This is my View Model
var ProductViewModel = function(items) {
this.items = ko.observableArray(items);
this.itemToAdd = ko.observable("");
this.addItem = function() {
if (this.itemToAdd() != "") {
this.items.push(this.itemToAdd());
this.itemToAdd("");
}
}.bind(this);
};
And this is my html tables
<div id="productTable">
<table class="ui-responsive table">
<thead>
<tr>
<th >Product Name</th>
<th >Description</th>
<th >Parent?</th>
</tr>
</thead>
<tbody id="pBody" data-bind="foreach: items">
<tr class="success" >
<td><span data-bind="text: name"></span>
</td>
<td><span data-bind="text: desc"></span>
</td>
<td>show chars</td>
</tr>
</tbody>
</table>
</div>
</div>
<div id="productChars">
<div id="productCharTable">
<table class="ui-responsive table">
<thead>
<tr>
<th >Char Name</th>
<th >Description</th>
<th >Length</th>
<th >Type</th>
</tr>
</thead>
<tbody id="pBody" data-bind="foreach: $data['chars']">
<tr class="success">
<td><span data-bind="text: name"></span>
</td>
<td>asdf asdfasdf</td>
<td>10</td>
<td>String</td>
</tr>
</tbody>
</table>
</div>
I am able to bind the products into first table. But for characteristics i am not sure how to achieve the same.
Could someone please help me in figuring out how to achieve the same.
Here is the jsfiddle
https://jsfiddle.net/sirisha_k/0Ln7h2bo/7/
As #supercool pointed out you can use "data-bind='with selectedItem'" to populate the second table with chars data. For that you need to add one more item into your model called selectedItem and every time you select or add a row, you point the selectedItem to that elementdata. And use "data-bind='with selecteItem'" for second table.
var ProductViewModel = function(items) {
this.items = ko.observableArray(items);
this.selectedItem = ko.observableArray();
this.itemToAdd = ko.observable("");
this.addItem = function() {
if (this.itemToAdd() != "") {
this.items.push(this.itemToAdd());
this.itemToAdd("");
}
}.bind(this);
};
and on row select call some function selectedItem($data) where $data refers to the current item.
then set that data to selectedItem in model.
function selectedItem(prod){
ProductViewModel.selectedItem(prod);
}