Pagination in Angular Ionic with next and previous button - javascript

I display a list of data on my page.
Now the client says display one and then add next and previous button for prev and next.
Angular pagination without adds any packages.
my code is.
<div *ngFor="let l of list">
<ion-card mode="ios" *ngIf="l.data!=null">
<ion-card-content>
<div class="ion-des" [innerHTML]="l.data| safe"></div>
</ion-card-content>
</ion-card>
</div>
and my typescript code is
getMenu() {
this.api
.get(
'GetMenuData'
)
.then((data: any) => {
this.list = data.data;
});
}
JSON-
"Tabledata": [
{
"id":"01",
"data":"Smith"
},
{
"id":"02",
"data":"Smith2"
},
{
"id":"0",
"data":"Smith3"
}
]
Now-
I need this way-
Instead of data 1, data 2, data 3, ..., data n
I need to add data 1 and Next and Previous btn on this page.

What you are looking for is "pagination".
In my opinion a good approach is that your API only returns one item instead of a list.
But your API needs a option to take additional query parameters like ?page=2 or ?limit=1&offset=1 which both would return the second item.
Then you do not need to loop through the list but you only get one item and show it as a card. With a click on the next button you call your API with ?page=3 and get the next item.
To show Page 2/10 on every page you need to return the total amount of pages too.
Alternative
You load the whole list on page load and only show one item at a time. If you click on next, show the next item in the array.

Related

VueJs + BootstrapVue: Table delete items only on first page

I created a page where I list all my items in a BootstrapVue b-table. for each item, I added the possibility to delete the item.
When I activate pagination with b-pagination element and :per-page attribute, I have an unexpected behavior on the page. The problem is it works only for the 1st page, but all others pages get the data of the 1st page. For example, if the total item is 10 and I'm displaying 5 items per page, the second page displaying the next 5 items will still try to delete the first page items.
<b-table
id="traffic-routes"
:fields="fieldsForTrafficRoutes"
:items="itemsForTrafficRoutes"
:per-page="perPageForTrafficRoutes"
:current-page="currentPageForTrafficRoutes"
>
<template v-slot:cell(action)="data">
<div class="d-flex align-items-center justify-content-end">
<a class="action-icon mr-2"
#click="removeRow(data.index)"
> Delete </a>
</div>
</template>
</b-table>
<b-pagination
v-model="currentPageForTrafficRoutes"
aria-controls="traffic-routes"
align="right"
:total-rows="
itemsForTrafficRoutes ? itemsForTrafficRoutes.length : 0"
:per-page="perPageForTrafficRoutes"
></b-pagination>
export default {
data() {
return {
perPageForTrafficRoutes: 5,
currentPageForTrafficRoutes: 1,
// ...
}
},
computed: {
...mapGetters([
'getAllTrafficRoutes',
]),
itemsForTrafficRoutes() {
return JSON.parse(JSON.stringify(this.getAllTrafficRoutes));
},
}
}
However, I tried to make the items dynamic:
itemsForTrafficRoutes() {
const foo = JSON.parse(JSON.stringify(this.getAllTrafficRoutes));
return foo.slice((this.currentPageForTrafficRoutes - 1) *
this.perPageForTrafficRoutes, this.currentPageForTrafficRoutes * this.perPageForTrafficRoutes
);
},
This will make the items unique for each page, it will only contain the first 5 on the first and then update to the next 5 items on the next page, however, the table only displays the first page and is empty on the next page even though the items are present
That's because you're using the index from the scoped slot.
The index will be based the displayed rows, and not the actual index of the item in the array provided to the items prop.
This is described on the docs down in the notes under the Scoped field slots section.
index will not always be the actual row's index number, as it is computed after filtering, sorting and pagination have been applied to the original table data. The index value will refer to the displayed row number. This number will align with the indexes from the optional v-model bound variable.
I would suggest either using a unique identifier from your object if you have one, or using the reference to remove it.

AngularJS: Custom directives not working with dirPagination

I am using AngularJS to create a page which contains a list of products that shows information such as a name, price, region etc. This is displayed kind of like an accordion with the name in the header and extra information in the body.
Since there could be a large amount of these items displayed I am using dirPagination (https://github.com/michaelbromley/angularUtils/tree/master/src/directives/pagination) to paginate these items. My markup at the moment looks like this:
<div class="custom-list" dir-paginate="asset in assets | itemsPerPage: 10 track by $index">
<div class="custom-list-item" ng-class="{'tag-hover': isHoveredTag(asset)}">
<div class="custom-list-item-heading" ng-click="toggleShowAssetDetails(asset)">
<p class="col-lg-5">{{ asset.name }}</p>
<div class="col-lg-offset-2 col-lg-3 rating">
<rating value="asset.rating" />
</div>
<button ng-click="addAsset(asset)"><span class="glyphicon glyphicon-plus"></span></button>
<div class="clearfix"></div>
</div>
<div class="custom-list-item-content" style="display: none" animate="shouldShowAssetDetails(asset)">
...
</div>
</div>
</div>
As you can see I'm using paginate in a pretty standard way just looping through the items in an array and displaying 10 per page. I also have a directive called rating which looks at a value called rating in the item. This is a number from 1 - 5 which is used to display a star rating system next to the name. The directive looks like this:
var rating = function ($compile) {
return {
restrict: "E",
scope: {
value: "="
},
link: function (scope, element, attrs) {
scope.$watch(attrs.rating, function () {
for (var i = 1; i <= 5; i++) {
if (i <= scope.value) {
var starElement = angular.element("<span class=\"icon icon-crown\"></span>");
$compile(starElement)(scope);
element.append(starElement);
} else {
var emptyStarElement = angular.element("<span class=\"icon-empty icon-crown\"></span>");
$compile(emptyStarElement)(scope);
element.append(emptyStarElement);
}
}
})
}
}
}
This looks at the value and inserts the icons based on the value of rating (e.g if the rating was 2 the directive would insert two icon-crown icon spans and 3 icon-empty icon-crown icon spans.
This was working perfectly fine before I included the pagination. However now it will only work for the first 10 items. When you change the page, the rating will not change and just keep the same icons from the previous page, even if the values are different.
I understand this is because the directive sets everything at the beginning and it will not run when you change page because the items aren't reloading they are just being shown and hidden again. But the directive is manipulating the DOM so it doesn't update when the page changes.
The problem is I don't know how to resolve this. I thought about changing the directive to look for the pagination current page instead but then I don't have access to the current list item.
I'd appreciate any help on getting the directive to update the icons when the page is changed.
Update
Here's a link to a Plunker project showing the problem I'm having: http://plnkr.co/edit/VSQ20eWCwVpaCoS7SeQq?p=preview
This is a very stripped down version of the section on my app that I'm having an issue with. There's no styling included although I have kept the CSS class structure. I've also changed the icons to use bootstrap ones just to simplify the Plunker project.
The functionality is the same however. If you go from page 1 to page 2 notice how the stars remain the same despite that fact that the asset rating values are different. However if you go to page 3 and back to page 2 or 1 they will change. The reason this happens is because there are less items on page 3 and therefore when you go back to page 1 or 2 the remaining items will be called again to retrieve the rating values.
You simply need to remove or replace track by $index.
Tracking by $index will give you the following behavior:
There is an array of max 10 length that represents the items to show. The first item will have index 0.
You go to the next page and the items in the array are replaced.
The first item in the array will still have index 0. Since you are tracking by index and the index has not changed, AngularJS will not recreate the DOM node representing the item. Since the DOM node isn't recreated, the directive will not execute this time and the rating will not update.
If you go from page 3 to page 2, the directive will execute for the 7 last elements on page 2, since they didn't exist on page 3, so they are considered new this time.
If you really need to use track by you should use a property that is actually related to the object (and unique), for example:
dir-paginate="asset in assets | itemsPerPage: 10 track by asset.name"
Demo: http://plnkr.co/edit/A80tSEliUkG5idBmGe3B?p=preview

two ng-repeaters on the same page

I have a page with a list of items on it. Each row has a button. By pressing a button list item is added to another list on the same page (it's a typical "order" form). I'm using angular ng-repeater to show the first list. After user press a button an item info is added to JSON varaible. The question is what's the best way to show user's choice list on the same page? So far I'm think of adding an attribute to the first list so when user choose it, it'll be shown in the second list. But I also want first list to be modified by user without any changes to the second one. Any ideas?
Check the code below, the button ng-click directive is calling the function AddtoList2($index) to add the current List1 item to List2, optionally it removes the current item from List1.
At template side
<div ng-repeat="item1 in List1">
...
<input type="button" ng-click="AddtoList2($index)" />
</div>
<div ng-repeat="item2 in List2">
...
</div>
At controller side
$scope.List1 = [];
$scope.List2 = [];
$scope.AddtoList2 = function (idx) {
var item = $scope.List1[idx];
$scope.List2.push(item);
//If you want to remove from List 1
$scope.List1.splice(idx, 1)
};

Moving element from position to position with jquery/angular

Well the problem is quite simple...
I have an array of movies div (image and desc) written with a simple ng-repeat...
Now when i choose one of them i want to do the following (A game of positions i suppose):
1)I want to take out that element from the array and with a smooth animation to enlarge it to some other place in the screen, without breaking the order of that array.
2)when i choose another film, i want that one that i selected before to get back to where it was, following my newly selected film to take the space of the one before it:
here is a simple practice page i created so someone can dig it more:
http://single.org.il/
(just press on one of the categories up there,a list of movies will appear down at the bottom of the page, the black screen in the middle is where i want my selected film to enter, it's allready happening but it breaks, a lot)
Thank you very much!
I recommend including the selection state as part of the data model, then binding the view based on the selection data. The important points being:
Track the selected item as part of the $scope, and selection state as part of the item data
Filter selected items out of the navigation list
Bind the detail view to the selected item
I created a simplistic jsfiddle to demonstrate the concept, http://jsfiddle.net/ZLvQD/1/. The key code points include the filtered navigation list:
<div ng-controller="ListController">
<ul>
<li ng-repeat="item in itemList | filter:{isSelected:false}"
ng-class="{selected: item.isSelected}" ng-click="select(item)">
{{item.desc}}</li>
</ul>
<div ng-hide="!selectedItem">
<hr/>
The selected item is:
<p class="selected">{{selectedItem.desc}}</p>
</div>
the data model including selection state:
$scope.itemList = [
{
"desc": "Item A",
"isSelected": false
},
...
and the controller tracking selection state:
$scope.selectedItem = null;
$scope.select = function(selectedItem) {
// Deselect existing
if ($scope.selectedItem) {
$scope.selectedItem.isSelected = false;
}
// Select new
selectedItem.isSelected = true;
$scope.selectedItem = selectedItem;
};
I'm afraid I do not know anything about the animation part.

How can I call a function with user selected data?

I'm having a contacts array holding list of emails. I generate div's using ng-repeat when user clicks i on a particular div i call ng-click="foo($index)"
var contacts=[someMail];
var userSelectedContact[];
$scope.foo=function(row)
{
userSelectedContact.push(contacts[row]);
}
The problem is when I add "|filter" in ng-repeat to search and select a particular contact.
Since filter creates another array after filtering: When ever user selects a contact foo($index) is called, and it is adding some other contact which is not selected by the user.
I can understand it since I am just using $index which is different from the index in original contacts array before filter.
So I have to stop using either filter (or) index to find the user selected contact.
What should i do? Is there any other way?
How can I call a function with user selected data like ng-click="foo(someEmail)"?
you can pass the whole ng-repeat item it the ng-clickfunction
<div ng-repeat="item in items" ng-click="myFunction(item)"></div>
and then in the controller
$scope.myFunction(item) {
console.log(item.name);
//or whatever you want to do with the item
}

Categories