In Vue, I have created multiple tables in the same component's template. Those tables all have the same html template, except they use different property of data (). Somehow the column width is inconsistent between them.
<template>
<v-container>
<h2>Security</h2>
<hr>
<v-simple-table>
<template v-slot:default>
<tbody>
<tr v-for="(a, index) in user_info.security_titles"
:key="a">
<td><strong>{{ a }}</strong></td>
<td v-show="user_info.security_data">{{user_info.security_data[index]}}</td>
</tr>
</tbody>
</template>
</v-simple-table>
<br>
<h2>Connection</h2>
<hr>
<v-simple-table>
<template v-slot:default>
<tbody>
<tr v-for="(b, index) in user_info.connection_titles"
:key="b">
<td> <strong>{{ b }}</strong> </td>
<td v-show="user_info.connection_data">{{user_info.connection_data[index]}}</td>
</tr>
</tbody>
</template>
</v-simple-table>
<br>
<h2>Language</h2>
<hr>
<v-simple-table>
<template v-slot:default>
<tbody>
<tr v-for="(c, index) in user_info.language_titles"
:key="c">
<td> <strong>{{ c }}</strong> </td>
<td v-show="user_info.language_data">{{user_info.language_data[index]}}</td>
</tr>
</tbody>
</template>
</v-simple-table>
</v-container>
</template>
I have used any style
A table always tries to fit in the content the most nicely, so if your column contains a row with long text, that column will take up more space.
You can use the width property in your headers to have a fixed width as shown in the docs
{
text: string
value: string
align?: 'start' | 'center' | 'end'
sortable?: boolean
filterable?: boolean
divider?: boolean
class?: string | string[]
width?: string | number
filter?: (value: any, search: string, item: any) => boolean
sort?: (a: any, b: any) => number
}
Related
Can I use an ngFor instead of repeating <table> two times?
NB: I thought to combine all the items into objects as items of a single array of mapping(each object contains a variable, label and value) but it does not work for me)
....
this.maxValueTable.push(selectedData.res.maxValue);
this.minValueTable.push(selectedData.res.minValue);
...
<div style="display: flex;">
<table style="width:100%;">
<thead>
<tr>
<th>Max</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let maxValue of maxValueTable">
<td> {{ maxValue | numberFormatter: (getUnit() | async)}}</td>
</tr>
</tbody>
</table>
<table style="width:100%;">
<thead>
<tr>
<th>Min</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let maxValue of minValueTable">
<td> {{ MinValue| numberFormatter: (getUnit() | async)}}</td>
</tr>
</tbody>
</table>
</div>
Another way:
<!--create an array directly in the .html: you can also use
a variable in your .ts-->
<table *ngFor="let table of [{head:'Max',data:maxValueTable},
{head:'Max',data:maxValueTable}]
style="width:100%;">
<thead>
<tr>
<!--use table.head-->
<th>{{table.head}}</th>
</tr>
</thead>
<tbody>
<!--see how iterate over table.data-->
<tr *ngFor="let maxValue of table.data">
<td> {{ maxValue | numberFormatter: (getUnit() | async)}}</td>
</tr>
</tbody>
</table>
If your arrays has the same length and only want a table, iterate over one array ans use the index to get the value of the another one
<table style="width:100%;">
<thead>
<tr>
<th>Max</th>
<th>Min</th>
</tr>
</thead>
<tbody>
<!--see the "let i=index"-->
<tr *ngFor="let maxValue of maxValueTable;let i=index">
<td> {{ maxValue | numberFormatter: (getUnit() | async)}}</td>
<!--use the "index" "i" to get the element of the another array-->
<td>
{{ minValueTable[i] | numberFormatter: (getUnit() | async)}}
</td>
</tr>
</tbody>
</table>
in this case you can also use map to create a new Array
minMax=this.minValueTable.map((x,index)=>({
min:x,
max:this.maxValueTable[index]
}))
And use {{value.min}} and {{value.max}}
You can create a function that will combine min and max values into an array like that:
mergeIntoArray(maxValue: Array<number>, minValue: Array<number>): IMergedObj[] {
let mergedArray = [];
maxValue.forEach((value, index) => {
let tempObj = {};
tempObj['maxValue'] = value;
tempObj['minValue'] = minValue[index];
mergedArray.push(tempObj);
});
return mergedArray;
}
and call this function like that:
let minAndMax = [];
this.minAndMax = this.mergeIntoArray(this.maxValueTable, this.minValueTable);
after that, use this variable (minAndMax) in your HTML template. This way you do not need to use ngFor twice.
<table style="width:100%;">
<thead>
<tr>
<th>Max</th>
<th>Min</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of minAndMax">
<td>{{ item.maxValue }}</td>
<td>{{ item.minValue }}</td>
</tr>
</tbody>
</table>
Here is the stackblitz link created for you. Hope that might help you.
You can use *ngTemplateOutlet for this case:
<div style="display: flex;">
<ng-container *ngTemplateOutlet="tableTemplate; context: {$implicit: maxValueTable, header: 'Max'}"></ng-container>
<ng-container *ngTemplateOutlet="tableTemplate; context: {$implicit: minValueTable, header: 'Min'}"></ng-container>
</div>
<ng-template #tableTemplate let-values, let-header="header">
<table style="width:100%;">
<thead>
<tr>
<th>{{ header }}</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let value of values">
<td> {{ value | numberFormatter: (getUnit() | async) }}</td>
</tr>
</tbody>
</table>
</ng-template>
And you will get the same table rendered twice - for Max and Min values.
As you can see, the arguments are passed as a second argument inside ngTemplateOutlet:
context: {$implicit: valuesArgument, header: headerArgument}
Later you could use this template to create infinite amount of tables:
<div *ngFor="let table of tables">
<ng-container *ngTemplateOutlet="tableTemplate; context: {$implicit: table.values, header: table.header}"></ng-container>
</div>
Assuming your tables property will look like
export class C {
tables: Table[] = [{header: 'Min', values: [1, 2, 3]}, {header: 'Max', values: [4, 5, 6]}, {header: 'Other', values: [0, -1, -2]}]
}
I have a source of data from a service like the following.
this.ateco2007 = [{code: this.anagrafica?.ateco2007?.code, description: this.anagrafica?.ateco2007?.description}]
if(this.anagrafica?.atecoSecondari && this.anagrafica?.atecoSecondari.length > 0) {
let i = 0;
for(let elem of this.anagrafica.atecoSecondari) {
this.atecoSecondari.push({
index: i++,
code: elem.code,
description: elem.description
});
}
...where ateco2007 has got always one elem, instead atecoSecondari can have multiple values. But there is always a pair code/description.
I want to show this data in a table. The first row will always be one row(ateco 2007), the other rows can be multiple.
I want a structure like this:
The first column will be always fix with the label, the second column shows the code and the last the description.
I tried the following, this is almost correct:
But I want to achieve something closer to the following:
As you see, there is a sort of rowspan and in the second and third columns, the rows have borders and are in the same line.
I tried the following code:
<p-table [value]="ateco2007" [autoLayout]="true">
<ng-template pTemplate="body">
<tr>
<td class="font-weight-bold"> {{'cliente.ateco'|translate}}</td>
<td>{{ateco2007[0]?.code}}</td>
<td class="text-left">{{ateco2007[0]?.description}}</td>
</tr>
<tr>
<td rowspan="atecoSecondari.length" class="font-weight-bold">
{{'cliente.atecoSecondari'|translate}}</td>
<td>
<table>
<tr *ngFor="let elem of atecoSecondari">
<td>{{elem.code}}</td>
</tr>
</table>
</td>
<td>
<table>
<tr *ngFor="let elem of atecoSecondari">
<td class="text-left">{{elem.description}}</td>
</tr>
</table>
</td>
</tr>
</ng-template>
But I don't know if it's the best way to build the table.
I solved it in this way
<p-table [value]="ateco2007" [autoLayout]="true">
<ng-template pTemplate="body" let-rowIndex="rowIndex">
<tr>
<td class="font-weight-bold"> {{'cliente.ateco'|translate}}</td>
<td>{{ateco2007[0]?.code}}</td>
<td class="text-left">{{ateco2007[0]?.description}}</td>
</tr>
<tr *ngFor="let elem of atecoSecondari">
<td *ngIf="rowIndex == elem.index" [attr.rowspan]="atecoSecondari.length" class="font-weight-bold">
{{'cliente.atecoSecondari'|translate}}
</td>
<td>{{elem.code}}</td>
<td class="text-left">{{elem.description}}</td>
</tr>
</ng-template>
</p-table>
I added the rowspan and changed the structure.
I already have VueJS v-for working fine:
<tr v-for="request in requests">
<td>{{request.name}}</td>
<td> .. etc .. </td>
</tr>
Now I need to add a legend/guiding row say every 25 or 50 records, something like this:
<span v-for="(request, index) in requests">
<tr>
<td>{{request.name}}</td>
<td> .. etc .. </td>
</tr>
<tr v-if="index % 25 == 0">
<th>Name</th>
<th> .. etc .. </th>
</tr>
</span>
To my surprise, not only does the v-if part not work but I get back an error: "ReferenceError: request is not defined" (even if I left the v-if directive out, or even removed the extra <tr> entirely), so VueJS is making considerations on DOM structure perhaps that I don't understand yet.
Either way, how would I do this?
And by the way, is there a purely HTML/CSS way to do this?
Your code has invalid HTML. You can't have spans wrapping trs.
Usually invalid HTML is not much of a big deal, but the browsers are extremelly buggy when handling invalid tr/tds placement (the spec is not clear about what they should do in case of error, so they behave each in a specific way for specific cases/errors).
The correct approach is to use <template>s, aka "Conditional Groups":
<table>
<template v-for="(request, index) in requests">
<tr>
<td>{{request.name}}</td>
<td> .. etc .. </td>
</tr>
<tr v-if="index % 25 == 0">
<th>Name</th>
<th> .. etc .. </th>
</tr>
</template>
Demo reproducing your error:
new Vue({
el: '#app',
data: {
requests: [{name: 'a1'},{name: 'a2'},{name: 'a3'},{name: 'a4'},{name: 'a5'},{name: 'a6'},{name: 'a7'},{name: 'a8'}]
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<table border="1">
<span v-for="(request, index) in requests">
<tr>
<td>{{request.name}}</td>
<td> .. etc .. </td>
</tr>
<tr v-if="index % 3 == 0">
<th>Name</th>
<th> .. etc .. </th>
</tr>
</span>
</table>
</div>
Demo with the fix:
new Vue({
el: '#app',
data: {
requests: [{name: 'a1'},{name: 'a2'},{name: 'a3'},{name: 'a4'},{name: 'a5'},{name: 'a6'},{name: 'a7'},{name: 'a8'}]
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<table border="1">
<template v-for="(request, index) in requests">
<tr>
<td>{{request.name}}</td>
<td> .. etc .. </td>
</tr>
<tr v-if="index % 3 == 0">
<th>Name</th>
<th> .. etc .. </th>
</tr>
</template>
</table>
</div>
Okay so, I'm using Angular 1.5.7, and I'm trying to do some table rendering with ng-repeat and stuff. This is what my table markup looks like:
<table class="table table-hover">
<thead>
<tr>
<td>Property name</td>
<td>Property value</td>
</tr>
</thead>
<tbody>
<adm-preset-property
ng-repeat="(propertyName, definition) in
componentDefinition.component_properties"
property-name="propertyName"
property-value="component.component_properties"
property-definition="definition"></adm-preset-property>
</tbody>
</table>
The <adm-preset-property> directive has a replace: true property and is rendered from a root <tr></tr> tag.
Now the loop works fine, BUT, instead of the table rows being rendered inside the table body, where they are nested, they are rendered ABOVE the table. I end up with
<tr>
{{ content }}
</tr>
<tr>
{{ content }}
</tr>
<tr>
{{ content }}
</tr>
<table>...</table>
What's worse, I can't seem to reproduce this on JSFiddle. What am I doing wrong?
EDIT: As requested, here's the template for the <adm-preset-property>
Template:
<tr>
<td>
<span data-toggle="tooltip" ng-attr-title="{{ ::propertyDefinition.description }}">{{ ::propertyDefinition.name }}</span>
</td>
<td ng-switch="propertyDefinition.editor_type">
<div ng-switch-when="select">
<ui-select append-to-body="true" ng-model="propertyValue[propertyName]" theme="bootstrap">
<ui-select-match placeholder="Select option...">{{ $select.selected.value }}</ui-select-match>
<ui-select-choices repeat="option.key as (key, option) in propertyDefinition.editor_properties.options | filter:{'value':$select.search} track by $index"
ng-value="key">
{{ option.value | highlight: $select.search }}</ui-select-choices>
</ui-select>
</div>
<div ng-switch-when="boolean">
<input type="checkbox" ng-model="propertyValue[propertyName]">
</div>
<div ng-switch-when="float">
<input type="range" step="0.01" ng-model="propertyValue[propertyName]" min="{{propertyDefinition.editor_properties.min}}"
max="{{propertyDefinition.editor_properties.max}}"> {{ propertyValue[propertyName] }}
</div>
<div ng-switch-when="color">
<input type="color" style="width: 75%;" ng-model="propertyValue[propertyName]">
</div>
<div ng-switch-when="int">
<input type="number" style="width: 75%;" ng-model="propertyValue[propertyName]" min="{{propertyDefinition.editor_properties.min}}"
max="{{propertyDefinition.editor_properties.max}}"> <br/>
<small>{{::propertyDefinition.editor_properties.min}} - {{::propertyDefinition.editor_properties.max}}</small>
</div>
<div ng-switch-default>
<input type="text" style="width: 75%;" ng-bind="propertyValue[propertyName]" />
</div>
</td>
</tr>
Directive:
(function() {
"use strict";
angular
.module('adomee.admin')
.directive('admPresetProperty', admPresetProperty);
/* #ngInject */
function admPresetProperty($log)
{
return {
restrict: 'E',
replace: true,
scope: {
propertyName: '=',
propertyValue: '=',
propertyDefinition: '='
},
templateUrl: 'app/preset/components/preset-property.tmpl.html',
link: function($scope, $element, $attributes) {
if ($scope.propertyDefinition.editor_type === 'select' && typeof($scope.propertyDefinition.defaultValue) === 'number') {
$scope.propertyValue[$scope.propertyName] = String($scope.propertyDefinition.defaultValue);
}
}
}
}
})();
Okay, after some research and consulting with my boss, we have concluded that Angular, naturally, compiles the directive's template into the DOM after the primary markup has been loaded.
Since the table expects a <tr> tag followed by <td>, but it runs into my custom tag instead, it removes the custom tag and places it outside of the table, which then compiles to my template afterwards, resulting in table rows on top of the actual table.
What I did was:
Change restrict: 'E' to restrict: 'A' in the directive.
Remove the replace property from it.
Remove the root <tr> tag and leave two <td> tags.
Place the directive into the table onto a <tr> and ng-repeat it.
Here's what it looks like now.
<tr adm-preset-property
ng-repeat="(propertyName, definition) in componentDefinition.component_properties"
property-name="propertyName"
property-value="component.component_properties"
property-definition="definition"></tr>
I'm trying to iterate over lists with sublists, and printing them as <tr> without much success.
This code illustrates what i want to accomplish:
<table>
<tr>
<th>1</th>
<th>2</th>
<th>3</th>
</tr>
<span ng-repeat='x in [["a1","a2"],["b1","b2"],["c1","c2"]]'>
<tr>{{x.length}}<tr>
<span ng-repeat='y in x'>
<tr>{{y}}<tr>
</span>
</span>
</table>
I would expect this to show:
<table>
<tr>3</tr>
<tr>a1</tr>
<tr>a2</tr>
<tr>b1</tr>
// and so on..
</table>
what should i do to make this work? I want to be able to repeat without the need to put in spans..
Only table tags (td, th, tr, tbody...) inside of <table> tag are shown, you should add ng-repeat to <tr>
If you use angular1.2 or higher you can use ng-repeat-start and ng-repeat-end tags:
html:
<table ng-controller="apiCtrl">
<tr ng-repeat-start="item in foo" ng-init="first=item[0]">
<td>first: {{ first }}</td>
</tr>
<tr ng-repeat-end ng-init="last = item[1]">
<td>last: {{ last }}</td>
</tr>
</table>
js:
function apiCtrl($scope) {
$scope.foo = [
['one', 'uno'],
['two', 'dos'],
['three', 'tres']
];
}
Here is JSfiddle
Here is fiddle with nested lists
This question is really old and AngularDart has changed a lot in the meantime. We can use the <ng-container> tag to apply all kinds of directives to a group of tags which are to be repeated or put under an *ngIf and so forth. The <ng-container> tag will not appear in the DOM output, but its contents will be there and affected by the specified directive.
<table>
<ng-container *ngFor="let row of matrix">
<tr>
<ng-container *ngFor="let value of row">
<td>{{value}}</td>
</ng-container>
<tr>
</ng-container>
</table>
When your component.dart has:
List<List<int>> get matrix => [[1, 2, 3], [4, 5, 6]];