How to re-order the angularjs ngTable pagination? - javascript

I'm using angularjs 1.5.7 in my current project. I use ngTable almost everywhere in this project. Lately I've been asked to change the way we show pagination on top of each table. Our current format:
We want to have:
Here is my code snippet:
<ul class="pagination">
<li ng-class="{'disabled': !page.active}" ng-repeat="page in pages" ng-switch="page.type">
<a ng-switch-when="first" ng-click="params.page(page.number)" href="" translate>TABLE.FIRST</a>
<a ng-switch-when="prev" ng-click="params.page(page.number)" href="" translate>TABLE.PREVIOUS</a>
<a ng-switch-when="next" ng-click="params.page(page.number)" href="" translate>TABLE.NEXT</a>
<a ng-switch-when="last" ng-click="params.page(page.number)" href="" translate>TABLE.LAST</a>
</li>
<li ng-if="params.pages < 2" ng-class="{'disabled':true}" ng-repeat="n in ['«','1','»']">
<a type="button" ng-click="" href="" ng-bind="n"></a>
</li>
</ul>
Unfortunately, I can't get it in order of: First>Prev>Next>Last. What I get instead is:
I was wondering if there is a way to force it to appear in the order I want?
Thanks

Given that there doesn't seem to be any way to sort the pages collection using native properties, you can easily create your own function to do so. Here is an example of a $scope-level function that you can pass to the orderBy directive. It's not the most elegant thing, but essentially I'm converting the text of the page.type to a numeric equivalent to allow orderBy to sort in your desired order.
Update: I added a listing without the orderBy to demonstrate that it really is performing the sort when using orderBy.
angular.module('app', [])
.controller('ctrl', ($scope) => {
$scope.orderPages = (page) => {
switch (page.type) {
case 'first':
return 1;
case 'prev':
return 2;
case 'next':
return 3;
case 'last':
return 4;
}
}
$scope.pages = [{
type: 'last',
text: '>>'
}, {
type: 'first',
text: '<<'
}, {
type: 'next',
text: '>'
}, {
type: 'prev',
text: '<'
}];
});
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.5/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
Without orderBy:
<ul>
<li ng-repeat="page in pages">{{page.text}}</li>
</ul>
With orderBy:
<ul>
<li ng-repeat="page in pages | orderBy: orderPages">{{page.text}}</li>
</ul>
</div>

Related

Render ng-zorro tags from component

I'm trying to render a <li> with some ng-zorro tags, I try with innerHTML but when i enter to the page this fails, I want to show differents menu's if the user get in a specific application (route)
I have my JSON like this
menuApp = [
{
"app": "requerimientos",
'menu': '\
<li nz-menu-item nzMatchRouter>\
<a routerLink="/applications/dashboard/requerimientos/crear-pedido">Crear pedido</a>\
</li>\
<li nz-menu-item nzMatchRouter>\
<a routerLink="/applications/dashboard/requerimientos/verificar-pedido">Verificar pedido</a>\
</li>\
'
}
]
and in my HTML
<li nz-submenu nzOpen nzTitle="Menú {{activeChild}}" nzIcon="appstore" *ngIf="activeChild != ''">
<div *ngFor="let app of menuApp; index as i">
<ul *ngIf="app.app === activeChild">
<div [innerHTML]="app.menu"></div>
</ul>
</div>
</li>
but when i render the page the <li> print without any ng-zorro tag or class
hope someone can help me
Angular doesn't work that way. Custom directives (like nzMatchRouter) cannot be shoehorned in with an innerHTML. You have to instruct the parser to compile the HTML after it's been added. But even still, there are more efficient, angular-centric methods you can use - like keeping your menu as a simple array of text/url values and using *ngFor to iterate them. That way the <li> directives are compiled at runtime
menuApp = [
{
"app": "requerimientos",
'menu': [
{text: "Crear pedido", url: "/applications/dashboard/requerimientos/crear-pedido" },
{text: "Verificar pedido", url: "/applications/dashboard/requerimientos/verificar-pedido" }
]}
Then your HTML would be like
<li nz-submenu nzOpen nzTitle="Menú {{activeChild}}" nzIcon="appstore" *ngIf="activeChild != ''">
<div *ngFor="let app of menuApp; index as i">
<ul *ngIf="app.app === activeChild">
<li *ngFor="item in app.menu" nz-menu-item nzMatchRouter>
<a [routerLink]="item.url">{{item.text}}</a>
</li>
</ul>
</div>
</li>

AngularJS - get the link in HTML

Good morning, this one is the only question that is similar in nature to mine, but unfortunately, it did not help me find an answer, so your help is highly appreciated.
I have the following snippet of HTML code:
<li class="first-page" ng-click="selectFirst()" onclick="window.scrollTo(0,0)">
First
</li>
I want to adjust the <li> so that it shows a link. In the directive's link function I included one more function:
function getPaginationLink($location) {
var linkToPage = $location.$$url;
console.log($location.$$url);
return linkToPage;
}
and slightly changed the html, in the following way:
<li class="first-page" ng-click="selectFirst()" onclick="window.scrollTo(0,0)">
<a ng-onload="getPaginationLink($location)" ng-href="{{ linkToPage }}">
First
</a>
</li>
linkToPage is not linked with the model anywhere in your controller's getPaginationLink function and using it in href="{{linkToPage}}" would be undefined.
If you just want to use the returned in value of getPaginationLink in href, just use
<li class="first-page" ng-click="selectFirst()" onclick="window.scrollTo(0,0)">
<a ng-init="url = getPaginationLink($location)" ng-href="{{url}}">First</a>
</li>
Here is an example snippet
angular.module('app', []).controller('ctrl', function($scope){
$scope.getr = function(){
return "URL"
}
})
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.9/angular.min.js"></script>
<div ng-app="app" ng-controller="ctrl">
<li class="first-page" ng-click="selectFirst()" onclick="window.scrollTo(0,0)">
<a ng-init="url = getr()" ng-href="{{url}}">First</a>
</li>
</div>

Override the default pagination template of ui-bootstrap

I am using AngularJS to show pagination. I made some changes on the default pagination template. This is my HTML:
<ul uib-pagination ng-model="source_pagination.current" template-url="pagination.html" total-items="source_pagination.total_pages" items-per-page="1" max-size="source_pagination.max_items" class="pagination-sm" force-ellipses="true" direction-links="false" ng-change="source_page_changed()"></ul>
...
<script id="pagination.html" type="text/ng-template">
<ul class="pagination">
<li ng-if="boundaryLinks" ng-class="{disabled: noPrevious()}">
<a href ng-click="selectPage(1)" title="First Page">
<span class="glyphicon glyphicon-fast-backward"></span>
</a>
</li>
<li ng-if="directionLinks" ng-class="{disabled: noPrevious()}">
<a href ng-click="selectPage(page - 1)" title="Previous Page">
<span class="glyphicon glyphicon-step-backward"></span>
</a>
</li>
<li ng-repeat="page in pages track by $index" ng-class="{active: page.active}">
<a href ng-click="selectPage(page.number)" ng-class="{highlight: ShouldHighlightPage(page.number)}">
{{page.text}}
</a>
</li>
<li ng-if="directionLinks" ng-class="{disabled: noNext()}"><a href ng-click="selectPage(page + 1)" title="Next Page"><span class="glyphicon glyphicon-step-forward"></span></a></li>
<li ng-if="boundaryLinks" ng-class="{disabled: noNext()}"><a href ng-click="$scope.changePage('last')" title="Last Page"><span class="glyphicon glyphicon-fast-forward"></span> </a></li>
</ul>
</script>
I modified this section:
<li ng-repeat="page in pages track by $index" ng-class="{active: page.active}">
<a href ng-click="selectPage(page.number)" ng-class="{highlight: ShouldHighlightPage(page.number)}">
{{page.text}}
</a>
</li>
And added this class-condition {highlight: ShouldHighlightPage(page.number)}. This code should call the function ShouldHighlightPage(pageNum) which is located in the controller:
$scope.ShouldHighlightPage = function (pageNum)
{
return true;
}
So all the pages should contain the class highlight. But this function is never called (checked by adding a breakpoint in the function). All the pages presented without the highlight class.
What am I doing wrong?
Thank you.
Okay, got the issue here.
The uib-pagination directive creates an isolated scope. See it here: https://github.com/angular-ui/bootstrap/blob/2.1.3/src/pagination/pagination.js#L126
And from the docs:
Scope
{...} (an object hash): A new "isolate" scope is created for the
directive's element. The 'isolate' scope differs from normal scope in
that it does not prototypically inherit from its parent scope. This is
useful when creating reusable components, which should not
accidentally read or modify data in the parent scope.
That means, the method ShouldHighlightPage will not be available in the scope of your overridden template.
One immediate solution (not recommended, getting you a cleaner solution) is to register your method in $rootScope:
$rootScope.ShouldHighlightPage = function (pageNum)
{
return true;
}
To fix this problem, add a custom directive:
myApp.directive('uibCustomPagination', function () {
return {
restrict: 'A',
require: 'uibPagination',
link: function ($scope, $element, $attr, uibPaginationCtrl) {
uibPaginationCtrl.ShouldHighlightPage = function (pageNum) {
return true;
};
}
}
});
This directive is basically injecting your method to the controller of uib-pagination directive so that it can be available in your template.
Now, just apply uib-custom-pagination directive to your ul element:
<ul uib-pagination uib-custom-pagination ng-model="source_pagination.current"
template-url="pagination.html" total-items="source_pagination.total_pages"
items-per-page="1" max-size="source_pagination.max_items" class="pagination-sm"
force-ellipses="true" direction-links="false" ng-change="source_page_changed()"></ul>

AngularJS - parse JSON

I have a list of photographs being generated like the following snippet. Basically this would render a table like structure, with each photo being like a cell in this table. The ID of each photo like for example 1D means that the photo is in the first row of the table and in the 4th/D column.
<ul>
<li class="row">
<ul>
<li class="photo" id="photo-1A">1A</li>
<li class="photo" id="photo-1B">1B</li>
<li class="photo" id="photo-1C">1C</li>
<li class="photo" id="photo-1D">1D</li>
<li class="photo" id="photo-2A">2A</li>
<li class="photo" id="photo-2B">2B</li>
<li class="photo" id="photo-2C">2C</li>
<li class="photo" id="photo-2D">2D</li>
<li class="photo" id="photo-3A">3A</li>
<li class="photo" id="photo-3B">3B</li>
<li class="photo" id="photo-3C">3C</li>
<li class="photo" id="photo-3D">3D</li>
</ul>
</li>
</ul>
I have a JSON which includes whether the photo is available or not. Basically the JSON string is something along these lines:
[{"row":1,"position":"A","available":true},{"row":1,"position":"B","available":false},{"row":1,"position":"C","available":false},{"row":1,"position":"D","available":false},{"row":2,"position":"A","available":true},{"row":2,"position":"B","available":false},{"row":2,"position":"C","available":false},{"row":2,"position":"D","available":false},{"row":3,"position":"A","available":true},{"row":3,"position":"B","available":false},{"row":3,"position":"C","available":false},{"row":3,"position":"D","available":false}]
Now basically what I need to do is to parse this JSON string and when any of these photos have "available:true" in the JSON string, I add a class photo-available in the HTML. I am new to angular and I am not sure if there is an easy way to assign a class to the available photos. Would be glad if someone can tell me what to use or how to do it.
Edit: Angular Code is this:
<ul class="table-rows">
<li class="photo-row" ng:repeat="photo in photos" ng:class="'photo-' + photo.row + photo.position">
<ul class="table-photos">
<li class="photo photo-available" ng:class="selectedOrNot(photo)" ng:init="photo.selected = false" ng:click="photo.selected = !photo.selected">
<div class="photo-number">{{photo.row + photo.position}}</div>
</li>
</ul>
</li>
<div class="clear"></div>
Update3
The reason you are unable to restore previous selections is that you are overwriting the photo's selected property with ng-init:
ng:init="photo.selected = false"
ng-class="{'selected': photo.selected, 'available': photo.available}"
When you combine these two, the 'selected' class will never be added because photo.selected has been hardcoded to false. You just need to remove ng-init, and the previous selection will trigger ng-class to add the correct class.
Here is a working demo: http://plnkr.co/tVdhRilaFfcn55h6mogu
Original answer
If the list of photos is not the same array as the list of available photos, you can use a directive to add the class.
app.directive('availablePhoto', function($filter) {
return {
restrict: 'A',
scope: true,
link: function(scope, element, attr) {
var id = attr.id
var regex = /photo-(.)(.)/g;
var match = regex.exec(id);
var row = match[1]
var position = match[2]
var photo = $filter('filter')(scope.photos, {row:row, position:position}, false)
console.log(photo);
if (photo[0].available) {
element.addClass('available');
}
}
}
});
Then attach it to each list item like this:
<li class="photo" id="photo-1A" available-photo>1A</li>
Here is a demo: http://plnkr.co/WJCmLf2M39fcUnvOPyNA
Update1
Based on your update, I see that there is just one array populating the list, and it contains the available flag. Therefore, you don't need a custom directive - ngClass will work. Here it is integrated into your code sample:
<ul class="table-rows">
<li class="photo-row" ng:repeat="photo in photos" ng:class="'photo-' + photo.row + photo.position">
<ul class="table-photos">
<li class="photo" ng-class="{'available': photo.available}" ng:init="photo.selected = false" ng:click="photo.selected = !photo.selected">
<div class="photo-number">{{photo.row + photo.position}}
</div>
</li>
</ul>
</li>
<div class="clear"></div>
</ul>
I have update the plunker to demonstrate this.
http://plnkr.co/WJCmLf2M39fcUnvOPyNA
Update2
Since you need ngClass to add multiple classes, use it like this:
ng-class="{'selected': photo.selected, 'available': photo.available}"
Demonstration of selected + available: http://plnkr.co/WJCmLf2M39fcUnvOPyNA
Here's a plnkr with an example of how to solve your problem. You need to
make use of both the ng-repeat and the ng-class:
http://plnkr.co/edit/hk68qp4yhEjcvOkzmIuL?p=preview
As you can see, I also added some filters for your photos, they will come handy if you need to just show the available ones (for some reason).
Here's the documentation for angular $filter service
I think this meets all your requirements:
$scope.photos = JSON.parse('[{"row":1,"position":"A","available":true},{"row":1,"position":"B","available":false},{"row":1,"position":"C","available":false},{"row":1,"position":"D","available":false},{"row":2,"position":"A","available":true},{"row":2,"position":"B","available":false},{"row":2,"position":"C","available":false},{"row":2,"position":"D","available":false},{"row":3,"position":"A","available":true},{"row":3,"position":"B","available":false},{"row":3,"position":"C","available":false},{"row":3,"position":"D","available":false}]');
and then you can just use ng-repeat to build the list:
<ul>
<li class="row">
<ul>
<li ng-repeat="photo in photos" class="photo" ng-class="{'photo-available': photo.available}" id="photo-{{photo.row}}{{photo.position}}">{{photo.row}}{{photo.position}}</li>
</ul>
</li>
</ul>
So what we are doing is we are taking our photo array, and for every one (ng-repeat="photo in photos") we are assigning that specific item to the variable photo. Then, if photo.available is true, we assign the class photo-available (ng-class="{'photo-available': photo.available}").
Then, we can simply interpolate the id and text based off the properties row and position ({{photo.row}}{{photo.position}}). You could also have done that like this {{photo.row + photo.position}} but that could cause issues if they were both numbers.
http://plnkr.co/edit/hBkoyHVtIwF60MKDF84j?p=preview

What's the best way to "transfer" directives from and to, using AngularJs

I have a widget that has the following structure (it's a "plan selector" resume):
<div>
<ul>
<li resume="to" ng-repeat="item in items"></li>
</ul>
<div class="total"></div>
<ul>
<li resume="from" ng-repeat="item in items"></li>
</ul>
</div>
my directive is the following:
plans.directive('resume', function() {
return {
restrict: 'A',
template: '<h3>{{step.name}}</h3><div>{{val}}</div>',
link: function($scope, element, attrs, controller) {}
};
});
Notes:
plans is an angular.module('Plans')
items is some data that is loaded via AJAX, and each item has a selected attribute
they share the same controller as the items that will be selected, that is PlansCtrl
What I want:
When the user finishes selecting the items (setting each "selected"), it will only show in the "to" when it's selected, and it will only show in the "from" when it's not selected. I don't want to hard code anything because everything is mounted using AJAX.
Assuming I'm understanding you right, it's probably better to use the filter filter like so:
http://jsfiddle.net/qbGeG/
<div ng-app ng-controller="x">
<b>selected</b>
<ul>
<li ng-repeat="item in items | filter:{selected:true}" ng-click="item.selected = !item.selected">{{item.name}}</li>
</ul>
<div class="total"></div>
<b>unselected</b>
<ul>
<li ng-repeat="item in items | filter:{selected:!true}" ng-click="item.selected = !item.selected">{{item.name}}</li>
</ul>
</div>
If you need the functionality as a directive, then it would be better to replace the entire li element with something like: <selectableList source="items" /> where your directive just uses filter and ngClick like I did above.

Categories

Resources