AngularJS ng-repeat group by date formatting issues - angularjs

I have a JSON array that I need to group by the date.
so using angular-filter I created this:
<ul ng-repeat="(key, value) in controller.collections.data | groupBy: 'plannedCollectionDate'">
Group name: {{ key | date: 'fullDate' }}
<li ng-repeat="item in value">
item: {{ item }}
</li>
</ul>
which is fine, but what I actually want to generate is a table. I tried this:
<div ng-repeat="(key, value) in controller.collections.data | groupBy: 'plannedCollectionDate' | orderBy: 'plannedCollectionDate'">
{{ key | date: 'fullDate' }}
<table class="table table-hover table-light">
<tbody>
<tr ng-repeat="collection in value | filter: controller.filter" ng-click="controller.select(collection)" ng-class="{ active: controller.isSelected(collection), warning: collection.status.id === 2, success: collection.status.id === 4, danger: collection.status.id === 5 }">
<td>
<div>{{ collection.supplierName }} {{ collection.description }}</div>
<div>to be collected by {{ collection.customerName }}</div>
</td>
<td>
<a ui-sref=".collect({ selected: [collection]})">{{ collection.status.name }}</a>
</td>
<td>
<button class="btn btn-danger" ng-click="controller.delete(collection.id)">
<span class="fa fa-close"></span>
</button>
</td>
</tr>
</tbody>
</table>
</div>
which again works fine, it generates a table for each group though. What I would prefer is to have one table with a header for the groupBy dates and then trs for the inner repeat. I imagine I have to do something like ng-repeat-start but I can't get it to work.
Has anyone got any idea how to do what I need?

Actually this wasn't too hard to figure out.
I just changed the way I was thinking about the headers.
This is my solution:
<table class="table table-hover table-light">
<tbody ng-repeat="group in controller.collections.data | groupBy: 'plannedCollectionDate' | toArray: true | orderBy: '-plannedCollectionDate'">
<tr>
<th colspan="3">
{{ group.$key | date: 'fullDate' }}
</th>
</tr>
<tr ng-repeat="collection in group | filter: controller.filter" ng-click="controller.select(collection)" ng-class="{ active: controller.isSelected(collection), warning: collection.status.id === 2, success: collection.status.id === 4, danger: collection.status.id === 5 }">
<td>
<div>{{ collection.supplierName }} {{ collection.description }}</div>
<div>to be collected by {{ collection.customerName }}</div>
</td>
<td>
<a ui-sref=".collect({ selected: [collection]})">{{ collection.status.name }}</a>
</td>
<td>
<button class="btn btn-danger" ng-click="controller.delete(collection.id)">
<span class="fa fa-close"></span>
</button>
</td>
</tr>
</tbody>
</table>

Related

Tricky Nested ng-repeat

https://jsfiddle.net/n4qq4vxh/97/
I am building tables to describe packages selected when making a car. The dark grey is the whole package(ie luxury package), the lighter grey is the items that are currently included in the package. Lastly, the rows with carrots are the 'alternative' options that can replace something that is currently in the package. For example, you can click the 'Leather Dakota Saddle Brown Stitching' and it would replace the 'Oyster Dakota Leather'. Everything works correctly but i need to display it differently.
I need to show the alternatives for each currently selected item(if applicable) under its correct parent, as opposed to just a list of all alternatives at the end. For example, the alternative rims should be displayed directly under the selected rims, the alternative leathers should be displayed directly under the selected leather, etc.
I am not sure that posting my code will help much but please see below. Basically what i am looking for is to find out if there is a way to start repeating through current package content, then compare the alternatives to see if it is a leather, wheel, etc as the parent is...if it is, repeat over the alternatives. Once finished repeating over the alternatives, move back to the next item that is selected in the package. I have tried messing around with this and cannot seem to find a solution/strategy.
How i would like it to appear:
Luxury package
package item: 18" rims
Alt Rim 1
Alt Rim 2
Alt Rim 3
package item: dakota leather
Alt leather 1
Alt leather 2
Alt leather 3
<tbody ng-repeat="package in allOptions | orderBy: 'listPrice'" ng-if="package.isPackage">
<tr class="hover" ng-class="{'selected':isSelected(package)}" ng-click="addRemoveOption(package)" **MAIN PACKAGE **>
<td> {{ package.code }} </td>
<td> {{ package.name }} </td>
<td> {{ package.listPrice | currency }} </td>
<td> {{ package.disclaimer }} </td>
<td> {{ package.salesGroup.name }} </td>
<td> {{ package.optionPrev }} </td>
<td> {{ package.familyCode }} </td>
</tr>
<tr ng-repeat="item in package.packageItems" ng-class="{'nested-selected':isSelected(package)}" id="nested" ** CURRENT PACKAGE CONTENT **>
<td> {{ item.code }} </td>
<td> {{ item.name }} </td>
<td> {{ item.comboPrice | included }} </td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
<!-- ng-if="hasAlternatives(package.packageItems)" -->
<tr ng-if="package.packageItems.alts.length" class="nested-alt" ng-repeat="alt in package.packageItems.alts" ng-click="addPackageOption(alt,package)" ng-hide="!isSelected(package)">
** ALTERNATIVES **
<td><span class="glyphicon glyphicon-menu-right" ng-click="addPackageOption(alt,package)"></span></td>
<td> {{ alt.code }} </td>
<td> {{ alt.name }} </td>
<td> {{ alt.price | included }} </td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
I was able to fix this with the following code. Basically, what it came down to was not using a table but instead making my own table with div's. This way i was able to provide the full scope 3 layers down the table, while with a standard table i was only able to go two 2 layers deep.
<h3>{{ tableName }}</h3>
<div class="flx flx-column hover packageTable"
ng-repeat="package in list | orderBy: 'listPrice' track by $index"
ng-class="{'selected': package.isSelected}"
ng-click="addRemoveOption(package)">
<div class="flx flx-row" id="anchor{{$index}}">
<div class="flx-cell-1">{{ package.code }}</div>
<div class="flx-cell-1">{{ package.name }}</div>
<div class="flx-cell-1">{{ package.listPrice | currency}}</div>
<div class="flx-cell-2">{{ package.disclaimer }}</div>
<div class="flx-cell-1">{{ package.salesGroup.name }}</div>
<div class="flx-cell-1">{{ package.optionPrev }}</div>
<div class="flx-cell-1">{{ package.familyCode }}</div>
</div>
<br>
<div class="flx flx-column" ng-repeat="item in package.packageItems track by $index"
ng-class="{'package-nested-selected': package.isSelected}"
>
<div class="flx flx-row" id="nested">
<div class="flx-cell-1">{{ item.code }}</div>
<div class="flx-cell-1">{{ item.listPrice | included }}</div>
<div class="flx-cell-2">{{ item.name }}</div>
<div class="flx-cell-1"></div>
<div class="flx-cell-1"></div>
<div class="flx-cell-1"></div>
</div>
<div class="flx flx-row package-nested-alt"
ng-if="item.alts.hasOwnProperty(item.code)"
ng-hide="!package.isSelected"
ng-repeat="alt in item.alts[item.code] track by $index"
ng-click="addPackageOption(alt, package, $event)"
>
<div class="flx-cell-1" id="packageArrow"><span class="glyphicon glyphicon-menu-right" /></div>
<div class="flx-cell-1">{{ alt.code }}</div>
<div class="flx-cell-1">{{ alt.price | included }}</div>
<div class="flx-cell-2">{{ alt.name }}</div>
<div class="flx-cell-1"></div>
<div class="flx-cell-1"></div>
<div class="flx-cell-1"></div>
<!--<div class="flx-cell-1"></div>-->
</div>
</div>
</div>

Angular - How do I filter objects depending on id within a ng-repeat?

I got 3 types of product groups:
Import (product_group_id value = 1)
Export (product_group_id value = 2)
Non-EU (product_group_id value = 3)
Each group contains products. I can determine to which product group each product belongs within my product create.
Now, I'm summing up these products in a product index. I do this with a ng-repeat and I'm suppose to be filtering the products depending on the product_group_id, like so:
<div style="display: inline;" class="btn-group">
<a href="/products/export" type="submit"
class="{{ $product_group_id == 1 ? 'btn btn-primary' : 'btn btn-default' }}"
role="button">#lang('product.export')</a>
<a href="/products/import" type="submit"
class="{{ $product_group_id == 2 ? 'btn btn-primary' : 'btn btn-default' }}"
role="button">#lang('product.import')</a>
<a href="/products/non-eu" type="submit"
class="{{ $product_group_id == 3 ? 'btn btn-primary' : 'btn btn-default' }}"
role="button">#lang('product.non_eu')</a>
</div>
<table class="table table-hover product-table" ng-controller="ProductCtrl">
<thead>
<tr>
<th></th>
<th>#</th>
<th>#lang('product.name')</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="products in product | filter:SearchProduct" ng-cloak>
<td><input type="checkbox" name="product_id" class="checkbox"
value="#{{product.id}}"></td>
<td><a href="#{{ url('/products/' . product_id) }}">
#{{product.id}}</a></td>
<td><a ng-href="/products/#{{ product.id }}">
#{{product.name | uppercase}}</a></td>
</tr>
</tbody>
</table>
ProductService.js:
.controller('ProductCtrl', function($scope, $http) {
$http.get('/api/product')
.success(function(response) {
$scope.products = response;
});
});
I set my product_group_id to 1, 2 and 3 in my controller.
Every time I create a new $product, it'll display in every filter ('/products/import', '/products/export' and '/products/non-eu').
So, my question is, how do I filter my products on $product_group_id within my ng-repeat?
Check the example on the filter page. Use an object, and set the product_id property
Search by product_id: <input type="text" ng-model="SearchProduct.product_id">
<tr ng-repeat="product in products | filter:SearchProduct">
To filter by specific fields you can;
<tr ng-repeat="product in products | filter:{product_group_id: SearchProduct}" ng-cloak></tr>
This allows filtering on specific fields that you use in the filter:{} object.
Hope it helps!
Take a look at this very basic example, the gist of it below:
<div class="row">
<button class="btn btn-xs" type="button" placeholder="Search" ng-click="myFilter = {product_group_id: 1}">Group 1</button>
<button class="btn btn-xs" type="button" placeholder="Search" ng-click="myFilter = {product_group_id: 2}">Group 2</button>
</div>
<br/>
<div class="row">
<table id="results" class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th>Product Name</th>
<th>Brand</th>
<th>PG</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="product in products | filter:myFilter ">
<td id="productName">{{product.productName}}</td>
<td id="brand">{{product.brand}}</td>
<td id="product_group_id">{{product.product_group_id}}</td>
</tr>
</tbody>
</table>
</div>
Can you show us the ProductCtrl?
Is $product_group_id really defined with $scope.$product_group_id
And do you set the $product_group_id with your routes?
Normally you don't need any routes here only if you want to make it bookmark able.
Here is an similar question with an valid approach but without routes : link
Beside that:
<a href="#{{ url('/products/' . product_id) }}">
Use ng-href here
Edit:
you doing this with an very complicated approach. To answer your question :
<a ng-click="$product_group_id=1" type="submit"
class="{{ $product_group_id == 1 ? 'btn btn-primary' : 'btn btn-default' }}"
role="button">#lang('product.export')</a>
<a ng-click="$product_group_id=2" type="submit"
class="{{ $product_group_id == 2 ? 'btn btn-primary' : 'btn btn-default' }}"
role="button">#lang('product.import')</a>
<a ng-click="$product_group_id=3" type ="submit"
class="{{ $product_group_id == 3 ? 'btn btn-primary' : 'btn btn-default' }}"
role="button">#lang('product.non_eu')</a>
Your repeat :
<tr ng-repeat="products in product | filter:SearchProductFn" ng-cloak>
your controller :
.controller('ProductCtrl', function($scope, $http) {
$scope.SearchProductFn = function(product) {
return product.group.id == $product_group_id && product.name == $scope.SearchName;
}
$http.get('/api/product')
.success(function(response) {
$scope.products = response;
});
});

How-To: Multiple Column Sort in AngularJS, while maintaining 'Always on Top' record(s)?

I have a simple table that I'm doing sorting on, and I'd like to be able to sort by one of two columns (date or 'has attachment'), but keep specific records at the top of the table (records that are 'pinned') and just sort the rows AFTER that.
Html Code
<table class="table table-striped table-bordered table-hover">
<thead>
<tr>
<th style="width: 50px" class="centered">
<div class="icon-refresh clickable" ng-click="loadNotes()"></div>
</th>
<th class="clickable colored_text" style="width: 50px" ng-click="sort('LastModified')">Last Modified</th>
<th class="clickable colored_text" style="width: 50px" ng-click="sort('HasAttachment')">Attachment</th>
<th>Note</th>
</tr>
</thead>
<tr ng-show="notesLoaded == true && notes.length == 0">
<td colspan="3">No notes found.</td>
</tr>
<tr ng-repeat="note in notes | filter: myFilterNotes | orderBy: noteSortOrder: noteSortDirection | startAt: (currentPage-1)*pageSize | limitTo :pageSize " ng-class="{'pinned': note.Pinned}">
<td class="span1"><button class="btn btn-inverse" ng-click="editNote(note.Id)" ng-show="(note.CreatedBy == history.currentUserId && note.TypeId === 0) || (history.canEditNotes && note.TypeId === 0)">Edit</button></td>
<td>{{note.LastModified | date:'MM/dd/yyyy h:mm a'}}</td>
<td class="centered">
<span ng-if="note.HasAttachment">
<a href ng-click="downloadAttachment(note)"><i class="icon-paper-clip icon-1point5x" title="{{note.NoteFileName}}"></i></a>
</span>
</td>
<td class="forceWordWrap">
<div class="span11">
<span ng-show="note.TypeId === 1">(Lead Note) </span>
<span ng-if="note.IsValidHtml" ng-bind-html="note.Note"></span>
<span ng-if="!note.IsValidHtml" ng-repeat="line in (note.Note | newlines) track by $index">
{{line}}<br/>
</span>
<span style="color: #bbb; font-size: 85%; font-weight: normal">
<p style="margin: 0;">-- created by {{(note.CreatedByName.length > 0) ? note.CreatedByName : "Unknown"}} on {{::note.CreatedDate | date:'MM/dd/yyyy h:mm a' }}</p>
<span ng-show="note.LastModifiedBy.length > 0">
<p style="margin: 0;">-- last modified by {{::note.LastModifiedBy}}</p>
</span>
</span>
</div>
<span class="span1" ng-show="note.Pinned" style="color: rgb(255, 0, 0);">
<i class="icon-pushpin icon-1point5x"></i>
</span>
</td>
</tr>
</table>
The Angular Sort method
$scope.noteSortDirection = true;
$scope.noteSortOrder = ["Pinned", "HasAttachment"];
$scope.sort = function (column) {
$scope.noteSortDirection = !$scope.noteSortDirection;
var sortColumnWithDirection = $scope.noteSortDirection !== true
? "-" + column
: column;
var newSortOrder = ["Pinned", sortColumnWithDirection];
$scope.noteSortOrder = newSortOrder;
};
The initial multi column sort is working fine, but when I click on either of the headers, it sorts the 'pinned' stuff in some wonky ways. (screenshots below)
This is working great!
This, not so much. It's like it does the sort exactly how I want, and then reverses the entire thing! (grr)
Ideally, this is what I want!
Solved it. I removed the sort order from the ng-repeat:
<tr ng-repeat="note in notes | filter: myFilterNotes | orderBy: noteSortOrder: true | startAt: (currentPage-1)*pageSize | limitTo :pageSize " ng-class="{'pinned': note.Pinned}">
and updated the custom sort method to deal with the sort order at the right time:
$scope.sort = function (column) {
var sortDir = $scope.noteSortDirection;
var revSortDir = !sortDir;
var sortColumnWithDirection = revSortDir !== true
? "-" + column
: column;
var newSortOrder = ["Pinned", sortColumnWithDirection];
$scope.noteSortDirection = !$scope.noteSortDirection;
$scope.noteSortOrder = newSortOrder;
};

ng-switch not working with nested ng-repeat for table

I am getting error while rendering table based on provided columns definitions and row data (2 different JSON objects) using nested 'ng-repeat' and ng-switch directives:
Is there any way to use 'ng-repeat' and ng-switch for rendering table cells without using wrapper elements.
Error: [$compile:ctreq] Controller 'ngSwitch', required by directive 'ngSwitchWhen', can't be found!
Here is my table template
<table id={{metaData.id}} name="{{metaData.name}}" class="{{metaData.classes}}">
<tbody>
<tr ng-repeat="record in data">
<td ng-repeat="col in metaData.columns" ng-switch on="col.type"
ng-switch-when="index"> {{record.$index + 1}} </td>
</tr>
</tbody>
Work around I made for time being
I am using ng-if to render table cell and rows dynamically in following way:
<table id={{metaData.id}} name="{{metaData.name}}" class="{{metaData.classes}} vk-table" >
<tbody>
<tr ng-repeat="record in data | orderBy : sortBy : reverse">
<td ng-repeat-start="col in metaData.columns" ng-if="col.type == 'index'"> {{$parent.$parent.$index + 1}}. </td>
<td ng-if="col.type =='name'">{{record.name = [record.fname, record.mname, record.lname].join(' ') }}</td>
<td ng-if="col.type =='date'">{{record[col.dataKey] = toDate(record[col.dataKey]) | date : 'mediumDate'}}</td>
<td ng-if="col.type =='inMobile'">
{{record[col.dataKey]}}
</td>
<td ng-if="col.type =='email'">
{{record[col.dataKey]}}
</td>
<td ng-if="col.type =='gender'">{{record[col.dataKey] == 'm' ? 'Male' : 'Female'}}</td>
<td ng-if="col.type =='place'">{{record[col.dataKey].name}}</td>
<td ng-repeat-end ng-if="!col.type">{{record[col.dataKey]}}</td>
</tr>
</tbody>
</table>
How can I achieve same output using ng-switch without using additional elements?
The ng-switch-when directive should be applied on a child element of the element where the ng-switch directive is applied. In your case, you applied them on the same element.
Try (inside tbody)
<tr ng-repeat="record in data | orderBy : sortBy : reverse">
<td ng-repeat="col in metaData.columns">
<div ng-switch on="col.type">
<div ng-switch-when="index">
{{record.$index + 1}}
</div>
<div ng-switch-when="name">
{{record.name = [record.fname, record.mname, record.lname].join(' ') }}
</div>
<div ng-switch-when="date">
{{record[col.dataKey] = toDate(record[col.dataKey]) | date : 'mediumDate'}}
</div>
<div ng-switch-when="inMobile">
{{record[col.dataKey]}}
</div>
<div ng-switch-when="email">
{{record[col.dataKey]}}
</div>
<div ng-switch-when="gender">
{{record[col.dataKey] == 'm' ? 'Male' : 'Female'}}
</div>
<div ng-switch-when="place">
{{record[col.dataKey].name}}
</div>
<div ng-switch-default>
{{record[col.dataKey]}}
</div>
</div>
</td>
</tr>

AngularJS orderBy not working with groupBy

I have this ng-repeater which looks like this:
<ul>
<li ng-repeat="item in controller.collections.data | orderBy: '-plannedCollectionDate'">
{{ item.plannedCollectionDate | date: 'fullDate' }}
</li>
</ul>
and this works fine, it gets my data and orders it in reverse, but when I try this with the groupBy filter, the orderBy is ignored.
I tried it like this at first:
<div ng-repeat="(key, value) in controller.collections.data | groupBy: 'plannedCollectionDate' | orderBy: '-plannedCollectionDate'">
{{ key | date: 'fullDate' }}
<table class="table table-hover table-light">
<tbody>
<tr ng-repeat="collection in value | filter: controller.filter" ng-click="controller.select(collection)" ng-class="{ active: controller.isSelected(collection), warning: collection.status.id === 2, success: collection.status.id === 4, danger: collection.status.id === 5 }">
<td>
<div>{{ collection.supplierName }} {{ collection.description }}</div>
<div>to be collected by {{ collection.customerName }}</div>
</td>
<td>
<a ui-sref=".collect({ selected: [collection]})">{{ collection.status.name }}</a>
</td>
<td>
<button class="btn btn-danger" ng-click="controller.delete(collection.id)">
<span class="fa fa-close"></span>
</button>
</td>
</tr>
</tbody>
</table>
</div>
But it didn't work. After using google I found a solution that suggested this:
<div ng-repeat="group in controller.collections.data | groupBy: 'plannedCollectionDate' | toArray: true | orderBy: '-plannedCollectionDate'">
{{ group.$key | date: 'fullDate' }}
<table class="table table-hover table-light">
<tbody>
<tr ng-repeat="collection in group | filter: controller.filter" ng-click="controller.select(collection)" ng-class="{ active: controller.isSelected(collection), warning: collection.status.id === 2, success: collection.status.id === 4, danger: collection.status.id === 5 }">
<td>
<div>{{ collection.supplierName }} {{ collection.description }}</div>
<div>to be collected by {{ collection.customerName }}</div>
</td>
<td>
<a ui-sref=".collect({ selected: [collection]})">{{ collection.status.name }}</a>
</td>
<td>
<button class="btn btn-danger" ng-click="controller.delete(collection.id)">
<span class="fa fa-close"></span>
</button>
</td>
</tr>
</tbody>
</table>
</div>
But this doesn't work either.
Can someone help me fix this?
the user group of angular-filter have written what needs to be done here
example: groupBy returns an object but orderBy fiter expects an array ..so use toArray:true AND give orderBy an array to work with:
Controller:
$scope.objectToArray= function(arr) {
return $filter('objectToArray')
($filter('map')(arr, 'id'));
}
View:
noworderBy can use $scope.objectToArray in your ng-repeat:
<div ng-repeat="group in controller.collections.data | groupBy: 'plannedCollectionDate' | toArray: true | orderBy: '-objectToArray'">
GL!

Resources