Angular 7 iterate table - javascript

I want to iterate table with one property but to have 4 td in one row, and to continue like that till the end...
I want like this:
<table>
<tr>
<td>
<mat-checkbox>1</mat-checkbox>
</td>
<td>
<mat-checkbox>2</mat-checkbox>
</td>
<td>
<mat-checkbox>3</mat-checkbox>
</td>
<td>
<mat-checkbox>4</mat-checkbox>
</td>
</tr>
<tr>
<td>
<mat-checkbox>5</mat-checkbox>
</td>
<td>
<mat-checkbox>6</mat-checkbox>
</td>
<td>
<mat-checkbox>7</mat-checkbox>
</td>
</tr>
</table>
I tried like this, but it's al in one column :
lista = [
{ value: "1" },
{ value: "2" },
{ value: "3" },
{ value: "4" },
{ value: "5" },
{ value: "6" },
{ value: "7" },
<table *ngFor="let list of lista">
<tr>
<td>
<mat-checkbox>{{ list.value }}</mat-checkbox>
</td>
</tr>
</table>

You need to group your array firstly in groups of 4 (chunk size), then iterate over it simply in your template.
In your component:
const lista = [
{ value: "1" },
{ value: "2" },
{ value: "3" },
{ value: "4" },
{ value: "5" },
{ value: "6" },
{ value: "7" }
];
const chunkSize = 4;
// Group in chunks of 4 and take only truthy values
const groups = lista
.map((x, index) => {
return index % chunkSize === 0 ? lista.slice(index, index + chunkSize): null;
})
.filter(x => x);
In your template:
<table >
<tr *ngFor="let item of groups">
<td *ngFor = "let innerItem of item">
<mat-checkbox>{{ innerItem.value }}</mat-checkbox>
</td>
</tr>
</table>

you can use 2d array for your table like below:
lista = [
[
{value: '1'},
{value: '2'},
{value: '3'},
{value: '4'}
],
[
{value: '5'},
{value: '6'},
{value: '7'},
{value: '8'}
]
];
and for HTML
<table >
<tr *ngFor="let row of lista">
<td *ngFor="let col of row">
<mat-checkbox>{{ col.value }}</mat-checkbox>
</td>
</tr>
</table>

Related

How to merge properties JavaScript objects

I'm starting with Vue and I'm having a little difficulty.
In the image below I have a table with some items:
Every time an item is chosen and the amount increased I need that in my addOptional method (optional) my variable gets the amount of that item concatenated with the value. Example if I choose hammer would look like this `
let variavel = opcional.Qtd + 'x' + opcional.Code
If I give console.log the result would be 2x1
But if I choose another option, example Serrote I should join the first choice in that same variable and separate with Pipe ( | ) Example would look like this.
2x1 | 1x2
How should I do this? Should I use array?
What I already have:
new Vue({
el: '#app',
data() {
return {
Opcionais: [
{ Code: 1, Nome: 'Martelo', Valor: 50.00, Qtd: 0 },
{ Code: 2, Nome: 'Serrote', Valor: 50.00, Qtd: 0 },
{ Code: 3, Nome: 'Prego', Valor: 60.00, Qtd: 0 }
]
}
},
methods: {
addOpcional(opcional) {
// The variable below should receive the value of the quantity plus the code. If more than one option is chosen the variable must receive this new value and separate with pipe example Qty (2) x Code (2) | Qty (3) x Code (2)
opcional.Qtd += 1
let Code = [opcional.Qtd + 'x' + opcional.Code]
},
remove(opcional) {
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<template>
<div class="usuario-lista">
<table>
<thead>
<tr>
<th>#Code</th>
<th>Nome</th>
<th>Valor Unitário</th>
<th>Valor Total</th>
</tr>
</thead>
<tbody>
<tr v-for="opcional in Opcionais" :key="opcional.Code">
<td>
<button #click="opcional.Qtd ? opcional.Qtd-- : false">-</button>
<input type="text" :value="opcional.Qtd">
<button #click="addOpcional(opcional)">+</button>
</td>
<td>{{ opcional.Nome }}</td>
<td>{{ opcional.Valor }}</td>
<td>{{ opcional.Valor * opcional.Qtd }}</td>
</tr>
</tbody>
</table>
</div>
</template>
</div>
This seems like a perfect use case for a computed property:
computed: {
Code: function () {
return this.Opcionais
.filter( opcional => opcional.Qtd > 0 )
.map( opcional => opcional.Qtd + 'x' + opcional.Code )
.join( ' | ' );
}
}
Here's a full working example, showing the code below the table and updating it live:
new Vue({
el: '#app',
data() {
return {
Opcionais: [
{ Code: 1, Nome: 'Martelo', Valor: 50.00, Qtd: 0 },
{ Code: 2, Nome: 'Serrote', Valor: 50.00, Qtd: 0 },
{ Code: 3, Nome: 'Prego', Valor: 60.00, Qtd: 0 }
]
}
},
computed: {
Code: function () {
return this.Opcionais
.filter( opcional => opcional.Qtd > 0 )
.map( opcional => opcional.Qtd + 'x' + opcional.Code )
.join( ' | ' );
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<template>
<div class="usuario-lista">
<table>
<thead>
<tr>
<th>#Code</th>
<th>Nome</th>
<th>Valor Unitário</th>
<th>Valor Total</th>
</tr>
</thead>
<tbody>
<tr v-for="opcional in Opcionais" :key="opcional.Code">
<td>
<button #click="opcional.Qtd > 0 && opcional.Qtd--">-</button>
<input type="text" v-model.number="opcional.Qtd">
<button #click="opcional.Qtd++">+</button>
</td>
<td>{{ opcional.Nome }}</td>
<td>{{ opcional.Valor }}</td>
<td>{{ opcional.Valor * opcional.Qtd }}</td>
</tr>
</tbody>
</table>
</div>
<p>Code: {{Code}}</p>
</template>
</div>
Not really familiar with Vue but you can reduce Opcionais like so:
const Opcionais = [
{ Code: 1, Nome: 'Martelo', Valor: 50.00, Qtd: 0 },
{ Code: 2, Nome: 'Serrote', Valor: 50.00, Qtd: 0 },
{ Code: 3, Nome: 'Prego', Valor: 60.00, Qtd: 0 }
];
const result = Opcionais.reduce((arr, { Qtd, Code }) => {
return [...arr, `${Qtd}x${Code}`];
}, []).join(' | ');
console.log(result);
You can use the spread operator to preserve the current state and add new items.
To join with pipes, use the 'reducer' as answered below or just do it in the html, as you wish.
new Vue({
el: '#app',
data() {
return {
Opcionais: [
{ Code: 1, Nome: 'Martelo', Valor: 50.00, Qtd: 0 },
{ Code: 2, Nome: 'Serrote', Valor: 50.00, Qtd: 0 },
{ Code: 3, Nome: 'Prego', Valor: 60.00, Qtd: 0 }
],
Elements: []
}
},
methods: {
addOpcional(opcional) {
// The variable below should receive the value of the quantity plus the code. If more than one option is chosen the variable must receive this new value and separate with pipe example Qty (2) x Code (2) | Qty (3) x Code (2)
opcional.Qtd += 1
this.Elements = [...this.Elements, (opcional.Qtd + 1) + 'x' + opcional.Code]
},
remove(opcional) {
}
}
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<template>
<div class="usuario-lista">
<table>
<thead>
<tr>
<th>#Code</th>
<th>Nome</th>
<th>Valor Unitário</th>
<th>Valor Total</th>
</tr>
</thead>
<tbody>
<tr v-for="opcional in Opcionais" :key="opcional.Code">
<td>
<button #click="opcional.Qtd ? opcional.Qtd-- : false">-</button>
<input type="text" :value="opcional.Qtd">
<button #click="addOpcional(opcional)">+</button>
</td>
<td>{{ opcional.Nome }}</td>
<td>{{ opcional.Valor }}</td>
<td>{{ opcional.Valor * opcional.Qtd }}</td>
</tr>
</tbody>
</table>
<pre>{{ Elements.join("|") }}</pre>
</div>
</template>
</div>

Angularjs: How do I ng-repeat through an array of objects with a field that is also an array?

I have an array that looks like this:
0: {ID: null,
name: "test",
city: "Austin",
UserColors: [{color: "blue"},{hobby:"beach"} ... ]}
}...
I am trying to ng-repeat through the initial array but once I try to loop through the list, i see nothing, heres the html/angular
<tr ng-repeat="c in vm.people">
<td>{{c.name}}</td>
<td>{{c.city}}</td>
<td ng-repeat="uc in c.UserColors">
<td>{{uc.color}}</td>
</td>
</tr>
I am not sure what is wrong, and I would appreciate your help, I thank you in advance.
I would process the field with a custom filter:
<td ng-repeat-start="(key, value) in c.UserColors | reduce">
<b>{{key}}</b>
</td>
<td ng-repeat-end>
{{value}}
</td>
The filter:
app.filter("reduce",function() {
return function(items) {
var x = items.map(o => Object.entries(o));
var x2 = x.reduce(((a,x) => (a.concat(x))), []);
var x3 = x2.reduce(((o,x) => (o[x[0]]=x[1],o)), {});
return x3;
}
})
The DEMO
angular.module("app",[])
.controller("ctrl",function(){
var vm = this;
vm.people = {
0: {ID: null,
name: "test",
city: "Austin",
UserColors: [{color: "blue"},{hobby:"beach"}]
},
1: {ID: null,
name: "best",
city: "Boston",
UserColors: [{colorx: "red"},{shirt:"black"}]
},
2: {ID: null,
name: "rest",
city: "Paris",
UserColors: [{colory: "yel"},{fruit:"peach"}]
},
}
})
.filter("reduce",function() {
return function(items) {
var x = items.map(o => Object.entries(o));
var x2 = x.reduce(((a,x) => (a.concat(x))), []);
var x3 = x2.reduce(((o,x) => (o[x[0]]=x[1],o)), {});
return x3;//items;
}
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app" ng-controller="ctrl as vm">
<h3>Table</h3>
<table>
<tr ng-repeat="c in vm.people">
<td>{{c.name}}</td>
<td>{{c.city}}</td>
<td ng-repeat-start="(key, value) in c.UserColors | reduce">
<b>{{key}}</b>
</td>
<td ng-repeat-end>
{{value}}
</td>
</tr>
</table>
</body>

filter just 2 out of 3 columns angularjs

<table>
<tr>
<th>Primary Key</th>
<th>descriptionShort</th>
<th>descriptionLong</th>
</tr>
<tr ng-repeat="terReason in data | filter :({descriptionLong:searchTerm}||{descriptionShort:searchTerm} )">
<td>terReason.primaryKey</td>
<td>terReason.descriptionShort</td>
<td>terReason.descriptionLong</td>
</tr>
</table>
I have this table. How can i make that the filter works only on descriptionLong or descriptionShort and not in primaryKey.
At this time the filter works only on descriptionLong.
DATA sceernshot
I suppose this is what you want:
<input ng-model="searchTerm">
<table>
<tr>
<th>Primary Key</th>
<th>descriptionShort</th>
<th>descriptionLong</th>
</tr>
<tr ng-repeat="terReason in data | filter : myFilter">
<td>{{ terReason.primaryKey }}</td>
<td>{{ terReason.descriptionShort }}</td>
<td>{{ terReason.descriptionLong }}</td>
</tr>
</table>
JS:
$scope.data = [
{
primaryKey: 0,
descriptionShort: 'descriptionShort ZERO',
descriptionLong: 'descriptionLong descriptionLong descriptionLong ZERO'
},
{
primaryKey: 1,
descriptionShort: 'descriptionShort ONE',
descriptionLong: 'descriptionLong descriptionLong descriptionLong ONE'
},
{
primaryKey: 2,
descriptionShort: 'descriptionShort TWO',
descriptionLong: 'descriptionLong descriptionLong descriptionLong TWO'
},
{
primaryKey: 3,
descriptionShort: 'descriptionShort THREE',
descriptionLong: 'descriptionLong descriptionLong descriptionLong THREE'
}
];
$scope.myFilter = function myFilter (value) {
return !$scope.searchTerm || Object.keys(value).some(function (key) {
return key !== 'primaryKey' && value[key].search($scope.searchTerm) !== -1;
});
}
pluner: http://plnkr.co/edit/VMcs064WWbGraULyXQTU?p=preview
try to search by 0 or 1 - nothing displayed, it is not searching by primary key.
<tr ng-repeat="terReason in data | filter :filter1 | filter :filter2 ">
<td>terReason.primaryKey</td>
<td>terReason.descriptionShort</td>
<td>terReason.descriptionLong</td>
</tr>
Why not use multiple filters?

Knockout filtered array issue

I have a table containing "device" data, when the user selects a device, the device form is filled. The device type drop-down list contains all the device types and the device type of the chosen device is automatically selected. There is also a device model drop-down list that displays only the models related to the selected device type. When the user select a device from the device list, the form is well populated and all works, but I wish that the device models drop-down will also update when the user manually selects another type of device from the device drop-down, and this is not working.
Snippet:
<select data-bind="options: selectedDevice() ? modelsByDeviceType(selectedDevice().DeviceTypeID) : null, optionsText: 'DeviceModelName', optionsValue: 'DeviceModelID', value: selectedDevice() ? selectedDevice().DeviceModelID : 0, optionsCaption: ''"></select>
self.modelsByDeviceType = function (selectedDeviceType) {
return ko.utils.arrayFilter(self.deviceModels(), function (m) {
return (m.DeviceTypeID === selectedDeviceType);
});
};
Full code: https://jsfiddle.net/rickhaar/9aLvd3uw/8/
Your currently work with static data, you need to change it to knockout observables, then your values would update 2-way. Mapping plugin does the trick for you:
function vm() {
var self = this;
var devicesData = [{
DeviceID: 1,
DeviceName: 'DVR1',
DeviceTypeID: 1,
DeviceModelID: 1
}, {
DeviceID: 2,
DeviceName: 'DVR2',
DeviceTypeID: 1,
DeviceModelID: 2
}, {
DeviceID: 3,
DeviceName: 'Cam1',
DeviceTypeID: 2,
DeviceModelID: 3
}, {
DeviceID: 4,
DeviceName: 'Cam2',
DeviceTypeID: 2,
DeviceModelID: 4
}];
var deviceTypesData = [{
DeviceTypeID: 1,
DeviceTypeName: 'DVR'
}, {
DeviceTypeID: 2,
DeviceTypeName: 'Camera'
}];
var deviceModelsData = [{
DeviceModelID: 1,
DeviceTypeID: 1,
DeviceModelName: 'BOSH AN 5000'
}, {
DeviceModelID: 2,
DeviceTypeID: 1,
DeviceModelName: 'Aver NEH1116HN'
}, {
DeviceModelID: 3,
DeviceTypeID: 2,
DeviceModelName: 'Axis M1054'
}, {
DeviceModelID: 4,
DeviceTypeID: 2,
DeviceModelName: 'FLIR A65'
}];
self.devices = ko.observableArray([]);
self.deviceTypes = ko.observableArray([]);
self.deviceModels = ko.observableArray([]);
self.selectedDevice = ko.observable();
self.init = function() {
ko.mapping.fromJS(devicesData, {}, self.devices);
ko.mapping.fromJS(deviceTypesData, {}, self.deviceTypes);
ko.mapping.fromJS(deviceModelsData, {}, self.deviceModels);
};
self.selectDevice = function(index, item) {
self.selectedDevice(item);
};
self.modelsByDeviceType = function(selectedDeviceType) {
return ko.utils.arrayFilter(self.deviceModels(), function(m) {
return (m.DeviceTypeID() === selectedDeviceType());
});
};
self.init();
return self;
}
ko.applyBindings(vm());
.selectedRow {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js"></script>
<table id="tblData" class="dataTable" border="1">
<thead>
<tr>
<th>Device ID</th>
<th>Device Name</th>
</tr>
</thead>
<tbody data-bind="foreach: devices">
<tr data-bind="click: selectDevice.bind($data, $index()), css: { selectedRow: selectedDevice() === $data }">
<td data-bind="text: DeviceID"></td>
<td data-bind="text: DeviceName"></td>
</tr>
</tbody>
</table>
<br/>
<table border="0">
<tbody>
<tr>
<td>Name</td>
<td>
<input type="text" style="width:80px" data-bind="value: selectedDevice() ? selectedDevice().DeviceName : ''" />
</td>
</tr>
<tr>
<td>Type</td>
<td>
<select data-bind="options: deviceTypes, optionsText: 'DeviceTypeName', optionsValue: 'DeviceTypeID', value: selectedDevice() ? selectedDevice().DeviceTypeID : 0, optionsCaption: ''"></select>
</td>
</tr>
<tr>
<td>Model</td>
<td>
<select data-bind="options: selectedDevice() ? modelsByDeviceType(selectedDevice().DeviceTypeID) : null, optionsText: 'DeviceModelName', optionsValue: 'DeviceModelID', value: selectedDevice() ? selectedDevice().DeviceModelID() : 0, optionsCaption: ''"></select>
</td>
</tr>
</tbody>

add formatting from scope object when binding element

I have a table which is build up from a array I create in my controller. When trying to bind I would like to add formatting, for example | number, from my object array.
So, in the td-element inside tbody, I would like to use something like
row[column.rowValue] | column.filter
I've tried using {{}} around, and also tried ng-bind-template and so on..
How should I do this?
JSFIDDLE
html:
<script data-require="angular.js#*" data-semver="1.2.0" src="http://code.angularjs.org/1.2.0/angular.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<div ng:controller="MyCtrl">
<table class="table table-striped table-hover table-responsive table-bordered" border="1">
<thead style="font-weight: bold;">
<tr>
<th class="text-right" ng-repeat="column in columns" ng-bind="column.rowHeader"></th>
</tr>
</thead>
<tbody>
<tr ng-repeat="row in rows">
<td ng-repeat="column in columns" ng-bind="row[column.rowValue] | column.filter"></td> <--doesnt work
</tr>
</tbody>
</table>
</div>
js:
var app = angular.module('myApp',[]);
function MyCtrl($scope) {
$scope.columns = [
{ checked: true, rowValue: 'value1', rowHeader: 'Value1', filter: 'number' },
{ checked: true, rowValue: 'value2', rowHeader: 'Value2', filter: 'number' },
{ checked: true, rowValue: 'ModelName', rowHeader: 'Name' }
];
$scope.rows = [
{ value1: 100, value2: 5, ModelName: "This is a cell value" },
{ value1: 15, value2: 5, ModelName: "This is a cell value2" },
{ value1: 38, value2: 2, ModelName: "This is a cell value3" }
];
}
Use a function in you controller for format the value and if you need use a custom filter, inject the $filter service.
var app = angular.module('myApp',[]);
function MyCtrl($scope, $filter) {
$scope.columns = [
{ checked: true, rowValue: 'value1', rowHeader: 'Value1', filter: 'number' },
{ checked: true, rowValue: 'value2', rowHeader: 'Value2', filter: 'number' },
{ checked: true, rowValue: 'ModelName', rowHeader: 'Name' }
];
$scope.formatRow = function(value, filterName){
return $filter(filterName)(value);
};
$scope.rows = [
{ value1: 100, value2: 5, ModelName: "This is a cell value" },
{ value1: 15, value2: 5, ModelName: "This is a cell value2" },
{ value1: 38, value2: 2, ModelName: "This is a cell value3" }
];
}
HTML
<tbody>
<tr ng-repeat="row in rows">
<td ng-repeat="column in columns" ng-bind="formatRow(row[column.rowValue], column.filter)"></td>
</tr>
</tbody>

Categories