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.
Related
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.
I have built a react application which shows items from JSON file, it consists of 4 main components, Items.js which created the item itself alongside its props (title and image), Itemlist.js which shows items from items.js and other functions.
MenuCat maps and shows each category from the json, and whenever the category is clicked it returns a number(props) to Itemlist.js adding it to the mapping function of items as ( children[{props here}].children[]....) so that it maps all items of each clicked category by using its json index.
Whenever an item is clicked, it shows more JSON properties such as promos inside a pop up called drawer, the content of this pop up is from Modifiers.js.
I need a function similar to MenuCat.js's category clicks but with items instead, where it shows the exact children of the item (e.g Burger 1 shows desert promo 1 on click, and burger 2 shows desert promo 2). The way its set up now, it only shows desert promo 1 for all the items.
Sandbox (current view on app.js): https://codesandbox.io/embed/8j5mmrjk2?fontsize=14&moduleview=1
One problem I noticed with your components was that you were passing the key attribute. This is anti-pattern as React does not pass down ref or key props.
If you need to access the same value within the child component, you should pass it as a different prop (ex: <ListItemWrapper key={result.id} id={result.id} />).
Just pass it down as a different attribute such as myKey.
I've done some other tweaks which you can check in the forked sandbox. Most notably, I added a state value to track the child index that was clicked.
this.state = {
itemSelected: 0
}
...
{items.children[selected].children[itemSelected].children.map(
(item, index) => (
<Modifiers
key={index}
title={item.name}
myKey={index}
childp={item.children[index].name}
>
{" "}
</Modifiers>
)
)}
I was trying to develop a dynamic list component, which involves state snyc. I have to admit the current design is terrible, it's not readable but at least I've made some progress. However I can't solve an issue, the delete has a bug.
https://codesandbox.io/s/wkqoz0wwv8
To reproduce it:
click 'add more' twice, to add 2 new item.
fill in the value for the last dropdown.
delete the 2nd list.
Something is not right there, it supposes to delete the 2nd item and kep the state of the last item.
The problem is not how you are removing the state but how you are rendering it, you are actually using the times function to creates a loop will will iterate row_count number of times and give you the index,
All you need to do is iterate over your data object like
return (
<div className="companyContactInfo-addContact">
{Object.keys(company_contacts).map(i => this.singleContact(i))}
<div className="row">
<div className="col-xs-12">
<a onClick={::this.addNewRow}>
<span>Add</span>
</a>
</div>
</div>
</div>
);
Also you once the duplicated variable is modified you could just assign it to state rather than using spread operator
handleInputChange(e, index) {
let targetedContact = this.state.company_contacts[index];
targetedContact.value = e.target.value;
this.setState({
company_contacts: {
...this.state.company_contacts,
[index]: targetedContact
}
});
}
Working Sandbox
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
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)
};