I am using vuetify 2.1 and a simple nested table. with the following data structure in my data model:
groups:[
{
style:"X",
colours:"colours",
sizes:"standard",
marketplaces:[
{
markeplace:"UK",
pricelists:["A","B","C"]
},
{
markeplace:"EU",
pricelists:["D","E","F"]
},
{
markeplace:"ROW",
pricelists:["G","H","I"]
},
]
},
{
style:"X",
colours:"Black/White",
sizes:"standard",
marketplaces:[
{
markeplace:"UK",
pricelists:["X","Y","Z"]
},
{
markeplace:"EU",
pricelists:["P","Q","R"]
},
{
markeplace:"ROW",
pricelists:["S","T","U"]
},
]
}
]
What I want to achieve is < td > records for:
style
colour
size
UK.pricelists[0]
UK.pricelists[1]
UK.pricelists[2]
EU.pricelists[0]
EU.pricelists[1]
EU.pricelists[2]
ROW.pricelists[0]
ROW.pricelists[1]
ROW.pricelists[2]
<v-simple-table
dense
calculate-widths
fixed-header
height="90vh"
>
<template v-slot:default>
<thead>
<tr>
<th>Style</th>
<th>Colour Group</th>
<th>Size Group</th>
<th>UK 1</th>
<th>UK 2</th>
<th>UK 3</th>
<th>EU 1</th>
<th>EU 2</th>
<th>EU 3</th>
<th>ROW 1</th>
<th>ROW 2</th>
<th>ROW 3</th>
</tr>
</thead>
<tbody>
<tr v-for="group in groups" >
<td>{{group.style}}</td>
<td>{{group.colour}}</td>
<td>{{group.size}}</td>
<!-- this is where I am struggling... I need the next 9 td records to iterate through two levels of arrays. -->
<td v-for="mkt in group.marketplaces">{{mkt.pricelists[0]}}<td>
</tr>
</tbody>
</template>
</v-simple-table>
for reference I have complete control over the API and the shape of the data object so feel free to suggest an alternative document structure. Can you native iterate over multiple levels in vuetify simple table - perhaps using array.foreach().
Is there a vue equivalent of react-fragment which acts as outer nesting element but does not actually render anything. The challenge is that this is within a table row and I need a collection around only some of the cells in the row.
do I move the logic to a method which remaps the pricelists for the passed in group. In my situation, all groups will have the same marketplaces in the same order and each marketplace will have the same number of price lists so I don't have any issues with sorting or padding the array.
In the absence of any other suggestions, I have create a method to remap the data into a single array:
methods: {
remapPricelists(style,colours,sizes){
/* This should find a single match */
let group = this.groups.filter(g=>{
return g.style == style
&& g.colours == colours
&& g.sizes == sizes
});
let pl =[];
group[0].pricelists.map(plst =>{
pl = pl.concat(plst.pricelists);
});
return pl;
}
}
DISCLAIMER: I have edited the above code from my live data which has a slightly different format (more outer groups and differently named fields) so E&OE. In production, I will likely abstract the group fetch to a separate method as I am going to need it in lots of places and will likely strip the outer array to just leave the group object so that I can access the inner data without having to specify the group array-index.
Related
I have clothes and orders tables and array which based on Clothes and Orders models.Whenever I push a clothes element into Orders array and especially to update amount of clothes and price which selected,Clothes array also being updated as well and I don't want it.I want to keep my array as immutable.I searched for it on the internet but didn't work.Here's what I tried below.Also to make it clear I'll add pictures here
https://imge.to/i/vg2aYm
https://imge.to/i/vg2uvF
HTML
<table class="table table-sm">
<thead>
<tr>
<th scope="col">Clothes</th>
<th scope="col">Price</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr *ngFor="let i of clothesList;let a = index">
<td>{{i.name}}</td>
<td>{{i.price}}$</td>
<td><button class="btn btn-alert" (click)="onAddItem(i)" >Add To Cart</button></td>
</tr>
</tbody>
</table>
<table class="table" *ngIf="orders.length>0">
<thead>
<tr>
<th scope="col">Clothes</th>
<th scope="col">Amount</th>
<th scope="col">Price</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let order of orders;">
<td>{{order.name}}</td>
<td>{{order.amount}}</td>
<td>{{order.price}}</td>
</tr>
</tbody>
<hr>
<strong>Total Cost: {{totalCost}}</strong>
</table>
TS
export class AppComponent {
private clothesList:Clothes[]=[
new Clothes(1,'Hat',500,1),
new Clothes(2,'Shoes',150,1),
new Clothes(3,'Pants',100,1),
new Clothes(4,'Jacket',200,1),
new Clothes(5,'T-Shirt',120,1),
new Clothes(6,'Souvether',150,1),
new Clothes(7,'Scarf',400,1)
];
private orders:Order[]=[];
onAddItem(value)
{
if(this.orders.find(i => i.name===value.name))
{
let myIndex= this.orders.indexOf(value);
value.amount++;
this.orders[myIndex].price+=this.orders[myIndex].price;
}
else
{
this.orders.push(value);
}
}
}
This is because the elements inside both the clothes and order array share same reference, You need to deep clone your object to break the reference:
Try the following:
onAddItem(value){
let order = this.orders.find(i => i.name === value.name);
if (order) {
value.amount++;
order.price *= 2;
}
else {
this.orders.push(JSON.parse(JSON.stringify(value))); // break the reference
}
}
try
this.orders.push(angular.copy(value));
this will add a copy of the object to the orders list an not a reference of it
As mentioned by others, the Clothes object you're passing in to onAddItem is a reference to the corresponding Clothes object in clothesList, so when you mutate that object, it will mutate the original object.
If Clothes is a simple class, you can just use the spread operator to make a copy:
onAddItem(value) {
let copyOfValue = {...value};
...
}
You could also use the Clothes constructor to make a copy:
onAddItem(value) {
let copyOfValue = new Clothes(value.someProperty, value.anotherProperty, value.aThirdProperty, value.aFourthProperty);
...
}
I have a few Bootstrap table on one page. Each table has some data attributes (like data-link="test-page" and so on). Besides that, one column of each Bootstrap table uses a column formatter, using data-formatter="actionFormatter". However, I want to get the current table data attributes when actionFormatter is called, so based on the data attributes I can return a string.
Both this and $(this) return an Object, which doesn't work. $(this).closest('table').data() doesn't work either, while I expected that one to be the most true.
Here's the code I use:
<th data-field="actions" data-formatter="actionFormatter" data-events="actionEvents">Actions</th>
this returns a JSON object with the row properties, and $(this).closest('table').data(XXX) return undefined. I expected it to return an array with all the data attributes.
Is there any way to get the current processing table from within the formatter?
Example code:
<!-- table 1 -->
<table
data-actions="edit,remove"
data-url="some/url"
>
<thead>
<tr>
<th data-formatter="actionFormatter">Actions</th>
</tr>
</thead>
</table>
<!-- table 2 -->
<table
data-actions="edit,remove"
data-url="some/url"
>
<thead>
<tr>
<th data-formatter="actionFormatter">Actions</th>
</tr>
</thead>
</table>
// actionFormatter:
function actionFormatter(value, row, index, field) {
// get data-actions from the right table somehow,
// and return a string based on data-url/other
// data attributes
}
It seems that when the action formatter is called, the execution context this is an object with all the bootstrap table row associated data as well as all the data-* attributes of the row.
Taking that into account you can add an id to each table and a data-table-id attribute to your rows like:
<table
id="table-1"
data-actions="edit,remove"
data-url="some/url"
>
<thead>
<tr>
<th data-formatter="actionFormatter" data-table-id="table-1">Actions</th>
</tr>
</thead>
so that in your formatter you can retrieve the table DOM Element by using that id:
function actionFormatter(value, row, index, field) {
// get data-actions from the right table somehow,
// and return a string based on data-url/other
// data attributes
var data = $('#' + this.tableId).data();
}
I data in this format in my angular controller. These are dummy datas and will be fetched from some sort of services later.
$scope.attendanceLog =
{
attendances:
[
{
date:'12.12.17',
entries:
[
{
time:'12PM',
device:'1212',
location:'katabon'
},
{
time:'1PM',
device:'1212',
location:'katabon'
},
{
time:'2PM',
device:'1321',
location:'katabon'
}
]
},
{
date:'13.12.17',
entries:
[
{
time:'12PM',
device:'1212',
location:'katabon'
},
{
time:'1PM',
device:'1212',
location:'katabon'
},
{
time:'2PM',
device:'1321',
location:'katabon'
},
{
time:'5PM',
device:'1321',
location:'katabon'
}
]
}
]
};
Now I designed the table to view this data like this. Here is the html code
for the table
<table class="table table-bordered">
<thead>
<th>Date</th>
<th class="text-center">Time</th>
<th class="text-center">Device</th>
<th class="text-center">Location</th>
</thead>
<tbody>
<tr ng-repeat-start="attendance in attendanceLog.attendances">
<td rowspan="{{attendance.entries.length}}" class="date">{{attendance.date}}</td>
<td>{{attendance.entries[0].time}}</td>
<td>{{attendance.entries[0].device}}</td>
<td>{{attendance.entries[0].location}}</td>
</tr>
<tr ng-repeat-end ng-repeat="entries in attendance.entries" ng-if="$index>0">
<td>{{entries.time}}</td>
<td>{{entries.device}}</td>
<td>{{entries.location}}</td>
</tr>
</tbody>
I want to make every other instance of the highlighted sections' background a diffrent color.Here is the reference image.
So if there are 5 dates then the 1st, 3rd and 5th date cell and all the other cells on their right side would have a different color.
Now is there any way to do this with angular. I am sorry if its a stupid question. I am new to front end development.
You could change it to have one expression for the table entries and use ng-class-odd and ng-class-even:
<tbody>
<tr ng-repeat="..."
ng-class-odd="'someOddClass'" ng-class-even="'someEvenClass'">
</tr>
</tbody>
Then you'd just need to change your styling.
Instead of printing new rows in the table I created a new table for every date. And then I applied the css class on every odd numbered table.
<table class="table table-bordered" ng-repeat="...." class =
ng-class ="$index % 2 == 0 ? 'table table-bordered striped':'table table-bordered'" >
.......
.......
</table>
Here striped is the class I used to highlight the odd numbered records background a different color
I'm aware of both ngRepeat and forEach but what I need is more or less a hybrid of these two. What I mean is the following:
I have a list of columns in the $scope. I can use
<th ng-repeat="col in columns">{{ col.label }}</th>
That would render
<th>Col A</th>
<th>Col B</th>
<th>Col C</th>
In the $scope I have a variable (mode). When this variable has the value admin, some admin related html tags are displayed using ng-show. Also when this variable is admin I would like my columns to be rendered like this
<th>config</th>
<th>Col A</th>
<th>config</th>
<th>Col B</th>
<th>config</th>
<th>Col C</th>
Is there a way to somehow use ng-repeat so that I can render the config and the label column at the same time? Perhaps something like
<repeat for="col in columns">
<th ng-show="mode == 'admin'">config</th>
<th>{{ col.label }}</th>
</repeat>
Or is my only option to create a new list that already has admin columns and gets regenerated (with forEach) every time mode is changed? What would be the best approach for this?
You can give a try using the ng-repeat-start and ng-repeat-end. Something like this:
<div ng-repeat-start="foo in foos">{{ foo.something }}</div>
Anything in here
<div><div ng-repeat-end="">This code will repeat and should work</div>
</div>
Refer to https://docs.angularjs.org/api/ng/directive/ngRepeat for more information.
Better to use a function that returns the columns
$scope.getColumns = function() {
if (mode != 'admin') {
return columns;
} else {
var c = new Array(columns.length * 2);
for (var i=0; i< c.length; i++) {
c[i] = i % 2 == 0 ? 'config' : columns[parseInt(i/2)];
}
return c;
}
}
template
<th ng-repeat="column in getColumns()" ng-bind="column"></th>
I have the following model: Item : {data: String, schedule: DateTime, category: String}
I need to display a report of this data in the following way:
<table>
<tr>
<th>Time Range</th>
<th>Category 1</th>
<th>Category 2</th>
<th>No Category</th>
</tr>
<tr>
<td>6:00 - 2:30</td>
<td>3</td>
<td>5</td>
<td>7</td>
</tr>
</table>
So I will need to compile and filter the list of Items into the time ranges and display the totals based on categories. How can I accomplish this with angular and tell which column I am in (and therefore choose the right value to display) as the categories are dynamic.
EDIT: The table will display a summary of all Items. So you'll have take all items and compile it into the form Time Range, # In Category 1, # In Category 2, # In No Category. The only thing that will be set by me is the time ranges.
I plan to store this data in a hash map where the keys are the category names, but I need to know which column I am in as the categories are dynamic. The categories come from the database (a user putting these items in).
Basically you need to do two things. Group list by category and display grouped list. See JsFiddle: https://jsfiddle.net/vittore/mmvxbcjx/4/
In order to group list you can use array.reduce. Convenient data structure for that would be hash of hashes, ie
var hash = {
'6:30': {
category1: 5
}
}
(Say you are grouping datetime based on time with an hour step.)
In order to get structure like that with reduce you will do:
var myList = [{},....];
$scope.grouped = list.reduce(function(a, d) {
var hours = d.schedule.getHours();
if (!(hours in a)) a[hours] = {}
var timeSlot = a[hours]
timeSlot[d.category || 'no category' ] =
1 + ( timeSlot[d.category || 'no category'] | 0)
return a;
}, {})
After you've got desired structure you need to do nested ng-repeat with angular:
<tr>
<th>time slot</th>
<th ng-repeat='c in categories'>{{ c }} </th>
<th>no category</th>
</tr>
<tr ng-repeat='(k,v) in grouped'>
<th>{{ k }} : 00</th>
<td ng-repeat='c in categories'>{{ v[c] }} </td>
<td>{{ v['no category'] }}</td>
</tr>