Angular2 - Table using *ngFor with paging and sorting - sorting

I am creating a simple html table with *ngFor in Angular 2.
My question is,
Is there an easy way to add sorting and paging to the same table without using any additional third party JavaScript ?
Does angular2 provide any techniques to achieve the same ?

I have been attempting to dynamically build and paginate my table in angular without the use of a 3rd party library. Some considerations around this desire may include some or more of the following:
Number of items per table-page. Do you want user to specify them or hard-corded?
Handling the click events, i.e. First,<>Last
The following code I used to dynamically generate the pages number (P2,...,Pn)
declare a variable (pagenumbers) to hold the page number HTML
pagenumbers = '<li class="page-item disabled clearfix d-none d-md-block"><a class="page-link waves-effect waves-effect">First</a></li><li class="page-item-disabled"><a class="page-link" aria-label="Previous"><span aria-hidden="true">«</span><span class="sr-only">Previous</span></a></li><li class="page-item active"><a class="page-link">1</a></li>';
Calling a service to get list of customers:
async getCustomerOrders(CustomerID: number, customerService: CustomerService) {
await customerService.getCustomerOrders(CustomerID)
.map((res) => res)
.subscribe(
data => {
this.customerOrders = data;
this.tot = this.customerOrders.length;
console.log('Total records: ' + this.tot);
this.workingWithLoop(this.tot / 10);
},
err => console.log(err),
() => console.log(this.customerOrders.length / 10)
);
}
The page numbers:
workingWithLoop(pages: number) {
// let pageNumbers = '';
console.log(this.pagenumbers);
console.log('Total pages: ' + pages);
for (let n = 2; n < pages; n++ ) {
console.log('Value of n: ' + n);
this.pagenumbers += '<li class="page-item"><a class="page-link waves-effect waves-effect">' + n + '</a></li>';
}
// tslint:disable-next-line:max-line-length
this.pagenumbers += ' <!--Arrow right--><li class="page-item"><a class="page-link" aria-label="Next"><span aria-hidden="true">»</span><span class="sr-only">Next</span></a></li><!--First--><li class="page-item clearfix d-none d-md-block"><a class="page-link">Last</a></li>';
}
In my html use the following code at the bottom of the table for the page numbers
<nav class="my-4 pt-2">
<ul class="pagination pagination-circle pg-purple mb-0" [innerHTML]="pagenumbers">
</ul>
</nav>
I am still working on dynamically generating a paginated table paginated table.

Angular does not have built-in way to sort tables. You will either have to do it yourself with wiring the events in the table headers, or use a third party component. PrimeNG does this and much more.
Update:
If you wanted to do this yourself, the equivalent of "filters" (Angular 1) is pipes in Angular 2. Here is good guide: http://voidcanvas.com/angular-2-pipes-filters/

Related

Rivetsjs iteration - by using an integer instead of collection

According to rivetsjs docs, we can render content by iterating over a object (array) by,
<ul>
<li rv-each-todo="list.todos">
<input type="checkbox" rv-checked="todo.done">
<span>{ todo.summary }</span>
</li>
<ul>
but is there a way where I can iterate by using a single integer to indicate number of times the iteration to take place?
I mean something like this,
<li rv-each="list.num_of_todos">
...
where num_of_todos is an integer to indicate number of iterations to take place.
There is no "proper" way of doing it. However, you can easily mimic this using a formatter that returns an array as shown below:
var list = {
name: 'to do list',
noOfToDos: 5
};
rivets.formatters.makearray = function(value) {
var result = [];
while (value--) {
result.push(0)
};
return result;
}
rivets.bind($("ul"), { // bind rivets
list: list
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/rivets/0.7.1/rivets.bundled.min.js"></script>
<ul>
<li rv-each-item="list.noOfToDos | makearray">Test</li>
<ul>

What is best way to do column totals in ng-grid?

If I have columns (name, amount) how do I best create a row / footer that shows ("Total",8877)? Clearly you can do it by adding a row to the data, but this ruins the sorting capability. It appears relatively easy to group by name and show the amount for each name, but I have not found how to do the simpler case (though I have found others asking - https://github.com/angular-ui/ng-grid/issues/679 for example)
You can include a custom footer template on the gridOptions. I looked for the default formatting of the footer in the source code and copied that, but added the function that calculates the totals. Something like this:
$scope.gridOptions = {
data: 'hereGoesTheData',
columnDefs : [list of your column names],
showFooter: true,
footerTemplate:
'<div ng-show="showFooter" class="ngFooterPanel" ng-class="{\'ui-widget-content\': jqueryUITheme, \'ui-corner-bottom\': jqueryUITheme}" ' +
'ng-style="footerStyle()"><div ng-style="{ \'cursor\': row.cursor }" ng-repeat="col in renderedColumns" ng-class="col.colIndex()" class="ngCell {{col.cellClass}} " ng-cell style="text-align:right;">' +
'{{getTotal(col.field)}}</div></div>'
};
And then define $scope.getTotal to do whatever you want it to do.
Quite possibly not the best solution, but I ended up adding a totals row to the top of the footer. https://github.com/mchapman/forms-angular/commit/9f02ba1cdafe050f5cb5e7bb7d26325b08c85ad2
without modifying ng grid, you could just provide your own footer template, that somehow gets the total for each column.
In my case, as I ""build"" the table from server data, I also accumulate a totals hash.
My template looks like this:
total_cell_footer = """
<div ng-show="showFooter" class="ngFooterPanel" ng-class="{'ui-widget-content': jqueryUITheme, 'ui-corner-bottom': jqueryUITheme}" ng-style="footerStyle()">
<div class="ngTotalSelectContainer" >
<div ng-style="{ 'cursor': row.cursor }" ng-repeat="col in renderedColumns" ng-class="col.colIndex()" class="ngCell {{col.cellClass}}">
<span class="ngCellText">{{ get_total(col) | currency:"$"}} </span>
<div class="ngVerticalBar" ng-style="{height: rowHeight}" ng-class="{ ngVerticalBarVisible: !$last }"> </div>
</div>
</div>
</div>
"""
The get_total function is defined in my scope (which is the parent of the ngGrid scope, hence inherited), as follows:
$scope.get_total= (col) ->
# used by the footer template to access column totals.
$scope.totals[col.field]
Take a look at the "Server side paging" example it has exactly what you want! you can slice and dice depending on what you need.
http://angular-ui.github.io/ng-grid/
in your grid options put
enablePaging: true,
showFooter: true,
showFilter: true,
totalServerItems: 'totalServerItems',
pagingOptions: $scope.pagingOptions,
and up top
$scope.pagingOptions = {
pageSizes: [100, 500, 1000],
pageSize: 100,
totalServerItems: 0,
currentPage: 1
};
$scope.setPagingData = function (data, page, pageSize) {
var pagedData = data.slice((page - 1) * pageSize, page * pageSize);
$scope.myData = pagedData;
**$scope.pagingOptions.totalServerItems = data.length**;
if (!$scope.$$phase) {
$scope.$apply();
}
};

My Recent Orders on home page, outside of user dashboard

I need to know if I need to create a widget for something like this, or just add a recent orders block to the home page layout.xml.
it should only reflect order for that store.
I know that the user has to be logged in for this to work, but there could be some cookie magic applied to detect the user, and display a short list of recent orders, without displaying the items, which I think is not a massive privacy / security issue.
This is for use in a mobile app, where space is limited, and quick links are helpful...
There will then simply be a link to reorder / view as in the dashboard, and that view would require a descent login.
What is the best way to go about it, if at all possible.
this is a Paypal app, and the recent orders are seen via:
http://www.sitename.com/storename/jsonsales/order/recenttemplate?s=2752732063744
Thanks in Advance.
On top of my head, what I would do is create a custom module with observer to catch when order placed, trigger the process of storing the order in the cookie (e.g. last 3 orders?)
Then just create a block in that module to read the data from the cookie.
After that render that block into the template (either via editing appropriate .phtml or create your own .phtml and add it inside layout.xml).
Hope this helps.
For those that might want to try this, with a module called Paypal order Ahead.
Because this is a paypal specific web app, I was able to do this using the javascript functions calling the json templates.
On the /app/design/frontend/paypal/test/template/cms/index.phtml I added the following:
<div class="content-box greybg recentordercontainer">
<div id="productError_<?php echo $sid; ?>" style="display:none"></div>
<div class="page-title text-title"><?php echo $this->__('My Recent Orders'); ?></div>
<div id="recentorder-list-box_<?php echo $sid; ?>" class=""></div>
<div class="cart-empty" id="cart-empty_<?php echo $sid; ?>" style="display:none">
<div class="box-shadow">
<div class="error-desc cart-row2" style="margin-top:10px;"><?php echo $this->__('You have not yet made any purchases.'); ?></div>
</div>
<div class="pay-now" id="contshop_<?php echo $sid; ?>">
<?php echo Mage::getStoreConfig('checkout/options/continue_shopping_text'); ?>
</div>
</div>
</div>
Then I added some javascript in the script section at the bottom of this template:
getRecentOrderBlockJSON("<?php echo $sid; ?>")
function getRecentOrderBlockJSON(id) {
$.getJSON(storeRootUrl+"/jsonsales/order/recent", function(jsonObj) {
renderRecentOrderBlock(jsonObj,id);
});
}
function renderRecentOrderBlock(jsonObj,id) {
var dataObj = jsonObj.recentorder;
// Recent Order Item box target
var ulObj = $("#recentorder-list-box_"+id);
// Display error message on top
if (jsonObj.messages.error.length >0) {
var pdObj = $("#productError_"+id);
var tplHTML = $("#ErrorPageTemplate").html();
for (key in jsonObj.messages) {
var re = new RegExp("%"+key+"%", "g");
tplHTML = tplHTML.replace(re,jsonObj.messages[key]);
}
pdObj.append(tplHTML);
$("#productError_"+id).css("display","block");
}
// have recent order
if ( dataObj && dataObj.length > 0 ) {
for ( var i=0; i<dataObj.length; i++ ) {
var tplHTML = $("#RecentOrderFrontTemplate").html();
// date
var re = new RegExp("%created_at%", "g");
//tplHTML = tplHTML.replace(re,dataObj[i].order.created_at);
tplHTML = tplHTML.replace(re,dataObj[i].order.created_at_localetime);
// update id
var re3 = new RegExp("%id%", "g");
tplHTML = tplHTML.replace(re3,dataObj[i].order.id);
// update rid
var re4 = new RegExp("%rid%", "g");
tplHTML = tplHTML.replace(re4,id);
ulObj.append(tplHTML);
// Recent Ordered Item box
var orderObj = $("#order-list-attr-"+id+dataObj[i].order.id);
} // for
}
}
And that gave me the list, provided that there was a cookie called ppmeccookie, with the value of your customerid. This cookie normally gets generated when you have done a paypal checkout before, using this paypal App.
Thanks for the help.

AngularJS ng-repeat messing up with order

I have created an Javascript object the following way:
$scope.initNews = function () {
for (var i = 2013; i > 2000; i--) {
$scope.news[i] = {};
$scope.news[i]["year"] = Number(i);
for (var j = 1; j <= 12; j++) {
$scope.news[i][j] = {};
$scope.news[i][j]["month"] = $scope.month_names[j-1];
}
}
};
but ng-repeat seems to mess up with the way it loops through the items.
<ul ng-repeat="old_news_year in news">{{old_news_year.year}}
<li ng-repeat="old_news_month in old_news_year">{{$index + 1}}. {{old_news_month.month}}</li>
</ul>
For the whole sample, check http://jsfiddle.net/tFewZ/1/
EDIT I am under the impression (by adding a "debugger;" into the code)
that AngularJS is looping through the entries according to their
$$hashKey and not according to their Javascript index...
Still do not understand about the 13th element though... is this the prototype attribute?
Thanks in advance.
Best regards,
Guillaume
Solution is to use $index instead of the collection approach ('in').
<ul ng-repeat="old_news_year in news">{{old_news_year.year}}
<li ng-repeat="old_news_month in old_news_year">
{{$index + 1}}. {{old_news_year[$index+1].month}}
</li>
</ul>
Check: http://jsfiddle.net/guillaume1978/XmSTK/2/

Allow "invalid" html markup in partial view in MVC 3

I have a problem with generating html with razor engine. In my case I have a app where a stored procedure lists a nested tree and have calculated how many submenus, how many siblings etc there are. And I need to have some logic in my partial view. And razor engine doesn't seem to like it since it seems to be invalid markup. How can I fix this to it prints out what I want?
<ul class="menu">
#foreach (var item in Model.NestedMenus)
{
if (item.StartNode > 0)
{
if (item.SubMenus > 0)
{
<li style="submenu">
}
else
{
<li style="menu">
}
#item.MenuName
}
else
{
</li>
}
}
</ul>
Must I use some old school Response.Write or summet? :)
/L
You need to prefix the lines with #: to prevent Razor from trying to parse the markup.
Otherwise, it will need to parse the markup in order to end the code block outside the top layer of markup.

Resources